// $Id: CacheDataset.java,v 1.3 2004-02-06 15:23:49 donm Exp $ /* * Copyright 1997-2000 Unidata Program Center/University Corporation for * Atmospheric Research, P.O. Box 3000, Boulder, CO 80307, * support@unidata.ucar.edu. * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or (at * your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package dods.servers.agg; import thredds.catalog.InvCatalog; import java.util.*; import dods.util.Debug; /** * This keeps a cache of Datasets, up to maxCached, and closes old ones based on * a simple LRU algorithm. Supposed to be thread safe. * The dataset mutex lock is acquired if successful. * * @author John Caron * @version $Id: CacheDataset.java,v 1.3 2004-02-06 15:23:49 donm Exp $ */ public class CacheDataset { private DatasetFactory factory = null; private String cacheName; private int maxCached; private long wait = 250; // msec private int numCached = 0; private LinkedList cache; private boolean debug = Debug.isSet("cache"); private boolean debugAdd = Debug.isSet("cacheAdd"); /** * @param cacheName name of cache (for debug messages) * @param maxCached maximum number to cache (<= 0 means unlimited) */ public CacheDataset(DatasetFactory factory, String cacheName, int maxCached) { this.factory = factory; this.cacheName = cacheName; this.maxCached = maxCached; cache = new LinkedList(); // switch to LinkedHashMap in 1.4 if (debug) System.out.println("CACHE "+cacheName+" created"); } /** * set maximum size of cache. */ public void setCacheMax( int maxCached) { this.maxCached = maxCached; } /** * set maximum time to wait before opening another copy of the dataset. * @param wait : time in msec */ public void setWaitTime( long wait) { this.wait = wait; } /** * get current size of the cache. */ public int getCacheSize() { return numCached; } /** FOR DEBUGUGGING ONLY **/ public Iterator getCache() { return cache.iterator(); } /** * This finds the named dataset and gets a lock on it. * WARNING: you better call ds.release() when you are done or you are SOL!!! * * @param extPath : external URL of dataset * @param intPath : internal URL of dataset * @param invDS : InvCatalog.Dataset object * @return locked dataset, or null if no room in cache for it. */ public Dataset acquire( String extPath, String intPath, InvCatalog.Dataset invDS) throws java.io.IOException { Dataset ds = null; synchronized (this) { // look for it in the cache, no waiting for locks if (null != (ds = search( intPath, 0))) { if (debug) System.out.println("CACHE "+cacheName+" found file = "+intPath); } // not found, try again, waiting up to wait msec if (null == ds) { if (null != (ds = search( intPath, wait))) { if (debug) System.out.println("CACHE "+cacheName+" found file (waited) = "+intPath); } } if (debug && (ds == null)) System.out.println("CACHE "+cacheName+" miss = "+intPath); // not in cache, see if theres room to add it if (ds == null) { if (!reserveRoomInCache()) { if (debug) System.out.println("CACHE "+cacheName+" IS FULL "+numCached); return null; } if (debug || debugAdd) System.out.println("CACHE "+cacheName+" reserved room for "+intPath+" " +numCached+" "+Thread.currentThread()); } } // synch // open new file if (ds == null) { ds = factory.factory( extPath, intPath, invDS); if (debug || debugAdd) System.out.println( "CACHE "+cacheName+" opened new dataset = "+intPath); ds.acquire(); synchronized (this) { cache.add( 0, ds); // add to the top of the list } } return ds; } // search for the dataset named "want"; if found try to // acquire its mutex, waiting up to wait msec. // move it to the top of the list if successful and maxCached > 0. // return locked dataset, or null if failed private Dataset search( String want, long wait) { int tries = 1, count = -1; Iterator iter = cache.iterator(); while (iter.hasNext()) { Dataset ds = (Dataset) iter.next(); count++; if (ds.getInternalPath().equals(want)) { // found it if (!ds.attempt(wait)) { // but its locked if ((debug) && (wait > 0)) System.out.println("CACHE "+cacheName+" waited in vain ("+tries+") for file "+want); tries++; continue; } // move it up if in bottom half of cache if ((maxCached > 0) && (count > maxCached/2)) { cache.remove(count); // remove from list cache.add( 0, ds); // add at the top if (debug) System.out.println("CACHE "+cacheName+" moved file = "+want); } return ds; } } return null; // never found it } private boolean reserveRoomInCache() { numCached++; if ((maxCached <= 0) || (numCached <= maxCached)) return true; numCached--; if (cache.size() == 0) return false; // cache is empty, but all slots are reserved // see if we can find a non-locked dataset to bump out ListIterator iter = cache.listIterator( cache.size()-1); while (iter.hasPrevious()) { Dataset ds = (Dataset) iter.previous(); if (ds.attempt(0)) { ds.release(); iter.remove(); try { ds.close(); // release resources if (debug || debugAdd) System.out.println("CACHE "+cacheName+" closed old file = "+ds.getInternalPath()); } catch (java.io.IOException e) { System.out.println("ERROR closing file "+ds.getInternalPath()+"/n"+e); } finally { return true; } } } // cache is full return false; } } /* Change History: $Log: not supported by cvs2svn $ Revision 1.2 2001/10/24 23:00:47 ndp added Makefile Revision 1.1.1.1 2001/09/26 15:36:47 caron checkin beta1 */