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

org.apache.jetspeed.cache.file.FileCache Maven / Gradle / Ivy

There is a newer version: 2.3.1
Show newest version
/*
 * 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.jetspeed.cache.file;

import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.LinkedList;
import java.util.Iterator;
import java.io.File;
import java.io.FileNotFoundException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * FileCache keeps a cache of files up-to-date with a most simple eviction policy.
 * The eviction policy will keep n items in the cache, and then start evicting
 * the items ordered-by least used first. The cache runs a thread to check for
 * both evictions and refreshes.
 *
 *  @author David S. Taylor David Sean Taylor
 *  @version $Id: FileCache.java 828270 2009-10-22 01:23:05Z rwatler $
 */

public class FileCache implements java.util.Comparator
{
    protected long scanRate = 300;  // every 5 minutes
    protected int maxSize = 100; // maximum of 100 items
    protected List listeners = new LinkedList();

    private FileCacheScanner scanner = null;
    private Map cache = null;

    private final static Log log = LogFactory.getLog(FileCache.class);

    /**
     * Default constructor. Use default values for scanReate and maxSize
     *
     */
    public FileCache()
    {
        cache = Collections.synchronizedMap(new HashMap());
        this.scanner = new FileCacheScanner();
        this.scanner.setDaemon(true);
    }

    /**
     * Set scanRate and maxSize
     *
     * @param scanRate how often in seconds to refresh and evict from the cache
     * @param maxSize the maximum allowed size of the cache before eviction starts
     */
    public FileCache(long scanRate, 
                     int maxSize)
    {
        
        cache = Collections.synchronizedMap(new HashMap());

        this.scanRate = scanRate;
        this.maxSize = maxSize;
        this.scanner = new FileCacheScanner();
        this.scanner.setDaemon(true);
    }

    /**
     * Set all parameters on the cache
     *
     * @param initialCapacity the initial size of the cache as passed to HashMap
     * @param loadFactor how full the hash table is allowed to get before increasing
     * @param scanRate how often in seconds to refresh and evict from the cache
     * @param maxSize the maximum allowed size of the cache before eviction starts
     */
    public FileCache(int initialCapacity, 
                     int loadFactor, 
                     long scanRate, 
                     int maxSize)
    {
        cache = Collections.synchronizedMap(new HashMap(initialCapacity, loadFactor));

        this.scanRate = scanRate;
        this.maxSize = maxSize;
        this.scanner = new FileCacheScanner();
        this.scanner.setDaemon(true);
    }

    /**
     * Set the new refresh scan rate on managed files.
     *
     * @param scanRate the new scan rate in seconds
     */
    public void setScanRate(long scanRate)
    {
        this.scanRate= scanRate;
    }

    /**
     * Get the refresh scan rate 
     *
     * @return the current refresh scan rate in seconds
     */
    public long getScanRate()
    {
        return scanRate;
    }

    /**
     * Set the new maximum size of the cache 
     *
     * @param maxSize the maximum size of the cache
     */
    public void setMaxSize(int maxSize)
    {
        this.maxSize = maxSize;
    }

    /**
     * Get the maximum size of the cache 
     *
     * @return the current maximum size of the cache
     */
    public int getMaxSize()
    {
        return maxSize;
    }

    /**
     * Gets an entry from the cache given a key
     *
     * @param key the key to look up the entry by
     * @return the entry
     */
    public FileCacheEntry get(String key)
    {
        return (FileCacheEntry) cache.get(key);
    }

    /**
     * Gets an entry from the cache given a key
     *
     * @param key the key to look up the entry by
     * @return the entry
     */
    public Object getDocument(String key)
    {
        FileCacheEntry entry = (FileCacheEntry) cache.get(key);
        if (entry != null)
        {
            return entry.getDocument();
        }
        return null;
    }

    /**
     * Puts a file entry in the file cache
     *
     * @param file The file to be put in the cache
     * @param document the cached document
     */
    public void put(File file, Object document)
        throws java.io.IOException
    {
        if(!file.exists())
        {
            throw new FileNotFoundException("File to cache: "+file.getAbsolutePath()+" does not exist.");
        }
        FileCacheEntry entry = new FileCacheEntryImpl(file, document);
        cache.put(file.getCanonicalPath(), entry);
    }

    /**
     * Puts a file entry in the file cache
     *
     * @param path the full path name of the file
     * @param document the cached document
     */
    public void put(String key, Object document, File rootFile)
        throws java.io.IOException
    {
        File file = new File(rootFile, key);
        if(!file.exists())
        {
            throw new FileNotFoundException("File to cache: "+file.getAbsolutePath()+" does not exist.");
        }
        FileCacheEntry entry = new FileCacheEntryImpl(file, document);
        cache.put(key, entry);
    }

    /**
     * Removes a file entry from the file cache
     *
     * @param key the full path name of the file
     * @return the entry removed
     */
    public Object remove(String key)
    {
        return cache.remove(key);
    }


    /**
     * Add a File Cache Event Listener 
     *
     * @param listener the event listener
     */
    public void addListener(FileCacheEventListener listener)
    {
        listeners.add(listener);
    }

    /**
     * Remove a File Cache Event Listener 
     *
     * @param listener the event listener
     */
    public void removeListener(final FileCacheEventListener listener)
    {
        listeners.remove(listener);
    }


    /**
     * Start the file Scanner running at the current scan rate.
     *
     */
    public void startFileScanner()
    {
        try
        {

            this.scanner.start();
        }
        catch (java.lang.IllegalThreadStateException e)
        {
            log.error("Exception starting scanner", e);
        }
    }

    /**
     * Stop the file Scanner 
     *
     */
    public void stopFileScanner()
    {
        this.scanner.setStopping(true);
    }

    /**
     * Evicts entries based on last accessed time stamp
     *
     */
    protected void evict()        
    {
        synchronized (cache)
        {
            if (this.getMaxSize() >= cache.size())
            {
                return;
            }
    
            List list = new LinkedList(cache.values());
            Collections.sort(list, this);
    
            int count = 0;
            int limit = cache.size() - this.getMaxSize();
    
            for (Iterator it = list.iterator(); it.hasNext(); )
            {
                if (count >= limit)
                {
                    break;
                }
    
                FileCacheEntry entry = (FileCacheEntry) it.next();
                String key = null;
                try
                {
                    key = entry.getFile().getCanonicalPath();
                }                    
                catch (java.io.IOException e)
                {
                    log.error("Exception getting file path: ", e);
                }
                // notify that eviction will soon take place
                for (Iterator lit = this.listeners.iterator(); lit.hasNext(); )
                {
                    FileCacheEventListener listener = 
                        (FileCacheEventListener) lit.next();
                    try
                    {
                        listener.evict(entry);
                    }
                    catch (Exception e1)
                    {
                        log.warn("Unable to evict cache entry.  "+e1.toString(), e1);
                    }                                    
                }
                cache.remove(key);
    
                count++;
            }        
        }
    }

    /**
     * Evicts all entries
     *
     */
    public void evictAll()        
    {
        synchronized (cache)
        {
            // evict all cache entries
            List list = new LinkedList(cache.values());
            for (Iterator it = list.iterator(); it.hasNext(); )
            {
                // evict cache entry
                FileCacheEntry entry = (FileCacheEntry) it.next();
                // notify that eviction will soon take place
                for (Iterator lit = this.listeners.iterator(); lit.hasNext(); )
                {
                    FileCacheEventListener listener = 
                        (FileCacheEventListener) lit.next();
                    try
                    {
                        listener.evict(entry);
                    }
                    catch (Exception e1)
                    {
                        log.warn("Unable to evict cache entry.  "+e1.toString(), e1);
                    }                                    
                }
                // remove from cache by key
                String key = null;
                try
                {
                    key = entry.getFile().getCanonicalPath();
                }                    
                catch (java.io.IOException e)
                {
                    log.error("Exception getting file path: ", e);
                }
                cache.remove(key);
            }        
        }
    }

    /**
     * Comparator function for sorting by last accessed during eviction
     *
     */
    public int compare(Object o1, Object o2)
    {
        FileCacheEntry e1 = (FileCacheEntry)o1;
        FileCacheEntry e2 = (FileCacheEntry)o2;
        if (e1.getLastAccessed() < e2.getLastAccessed())
        {
            return -1;
        }
        else if (e1.getLastAccessed() == e2.getLastAccessed())
        {
            return 0;
        }
        return 1;
    }

    /**
     * inner class that runs as a thread to scan the cache for updates or evictions
     *
     */
    protected class FileCacheScanner extends Thread
    {
        private boolean stopping = false;

        public void setStopping(boolean flag)
        {
            this.stopping = flag;
        }

        /**
         * Run the file scanner thread
         *
         */
        public void run()
        {
            boolean done = false;
    
            try
            {
                while(!done)
                {
                    try
                    {
                        int count = 0;
                        Collection values = Collections.synchronizedCollection(FileCache.this.cache.values());
                        synchronized (values)
                        {
                            for (Iterator it = values.iterator(); it.hasNext(); )
                            {
                                FileCacheEntry entry = (FileCacheEntry) it.next();
                                Date modified = new Date(entry.getFile().lastModified());
        
                                if (modified.after(entry.getLastModified()))
                                {                            
                                    for (Iterator lit = FileCache.this.listeners.iterator(); lit.hasNext(); )
                                    {
                                        FileCacheEventListener listener = 
                                            (FileCacheEventListener) lit.next();
                                        try
                                        {
                                            listener.refresh(entry);
                                        }
                                        catch (Exception e1)
                                        {
                                            log.warn("Unable to refresh cached document:  "+e1.toString(), e1);
                                        }                                    
                                        entry.setLastModified(modified);
                                    }
                                }
                                count++;
                            }
                        }
                        if (count > FileCache.this.getMaxSize())
                        {
                            FileCache.this.evict();
                        }
                    }
                    catch (Exception e)
                    {
                        log.error("FileCache Scanner: Error in iteration...", e);
                    }
    
                    sleep(FileCache.this.getScanRate() * 1000);                

                    if (this.stopping)
                    {
                        this.stopping = false;
                        done = true;
                    }
                }
            }
            catch (InterruptedException e)
            {
                log.error("FileCacheScanner: recieved interruption, exiting.", e);
            }
        }
    } // end inner class:  FileCacheScanner


    /**
     * get an iterator over the cache values
     *
     * @return iterator over the cache values
     */
    public Iterator getIterator()
    {
        return cache.values().iterator();
    }

    /**
      * get the size of the cache
      *
      * @return the size of the cache
      */
    public int getSize()
    {
        return cache.size();
    }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy