cdm/src/main/java/ucar/nc2/util/cache/FileCache.java
@Override
  public FileCacheable acquire(FileFactory factory, Object hashKey, DatasetUrl location,
                               int buffer_size, CancelTask cancelTask, Object spiObject) throws IOException {

    if (null == hashKey) hashKey = location.trueurl;
    if (null == hashKey) throw new IllegalArgumentException();

    Tracker t = null;
    if (trackAll) {
      t = new Tracker(hashKey);
      Tracker prev = track.putIfAbsent(hashKey, t);
      if (prev != null) t = prev;
    }

    FileCacheable ncfile = acquireCacheOnly(hashKey);
    if (ncfile != null) {
      hits.incrementAndGet();
      if (t != null) t.hit++;
      return ncfile;
    }
    miss.incrementAndGet();
    if (t != null) t.miss++;

    // open the file
    ncfile = factory.open(location, buffer_size, cancelTask, spiObject);
    if (cacheLog.isDebugEnabled())
      cacheLog.debug("FileCache " + name + " acquire " + hashKey + " " + ncfile.getLocation());
    if (debugPrint) System.out.println("  FileCache " + name + " acquire " + hashKey + " " + ncfile.getLocation());

    // user may have canceled
    if ((cancelTask != null) && (cancelTask.isCancel())) {
      if (ncfile != null) ncfile.close();       // LOOK ??
      return null;
    }

    if (disabled.get()) return ncfile;

    // see if cache element already exists
    // cant use putIfAbsent, because we cant create the CacheElement until we know if doesnt exist
    CacheElement elem;
    synchronized (cache) {
      elem = cache.get(hashKey);
      if (elem == null) cache.put(hashKey, new CacheElement(ncfile, hashKey)); // new element
    }

    // already exists, add to list
    if (elem != null) {
      synchronized (elem) {
        elem.addFile(ncfile); // add to existing list
      }
    }

    // increment the number of files in the cache
    //int count = counter.incrementAndGet();

    // do we need a cleanup ??
    boolean needHard = false;
    boolean needSoft = false;
    synchronized (hasScheduled) {
      if (!hasScheduled.get()) {
        int count = files.size();
        if ((count > hardLimit) && (hardLimit > 0)) {
          needHard = true;
          hasScheduled.getAndSet(true); // tell other threads not to schedule another cleanup

        } else if ((count > softLimit) && (softLimit > 0)) {   // && wantsCleanup) { //
          hasScheduled.getAndSet(true); // tell other threads not to schedule another cleanup
          needSoft = true;
        }
      }
    }

    if (needHard) {
      if (debugCleanup)
        System.out.println("CleanupTask due to hard limit time=" + new Date().getTime()); // +" Thread="+Thread.currentThread().hashCode()
      cleanup(hardLimit);

    } else if (needSoft) {
      schedule(new CleanupTask(), 100); // immediate cleanup in 100 msec

      if (debugCleanup) System.out.println("CleanupTask scheduled due to soft limit time=" + new Date());
    }

    return ncfile;
  }
@Override
  public FileCacheable acquire(FileFactory factory, Object hashKey, DatasetUrl location,
                               int buffer_size, CancelTask cancelTask, Object spiObject) throws IOException {

    if (null == hashKey) hashKey = location.trueurl;
    if (null == hashKey) throw new IllegalArgumentException();

    Tracker t = null;
    if (trackAll) {
      t = new Tracker(hashKey);
      Tracker prev = track.putIfAbsent(hashKey, t);
      if (prev != null) t = prev;
    }

    FileCacheable ncfile = acquireCacheOnly(hashKey);
    if (ncfile != null) {
      hits.incrementAndGet();
      if (t != null) t.hit++;
      return ncfile;
    }
    miss.incrementAndGet();
    if (t != null) t.miss++;

    // open the file
    ncfile = factory.open(location, buffer_size, cancelTask, spiObject);
    if (cacheLog.isDebugEnabled())
      cacheLog.debug("FileCache " + name + " acquire " + hashKey + " " + ncfile.getLocation());
    if (debugPrint) System.out.println("  FileCache " + name + " acquire " + hashKey + " " + ncfile.getLocation());

    // user may have canceled
    if ((cancelTask != null) && (cancelTask.isCancel())) {
      if (ncfile != null) ncfile.close();       // LOOK ??
      return null;
    }

    if (disabled.get()) return ncfile;

    // see if cache element already exists
    // cant use putIfAbsent, because we cant create the CacheElement until we know if doesnt exist
    CacheElement elem;
    synchronized (cache) {
      elem = cache.get(hashKey);
      if (elem == null) cache.put(hashKey, new CacheElement(ncfile, hashKey)); // new element
    }

    // already exists, add to list
    if (elem != null) {
      synchronized (elem) {
        elem.addFile(ncfile); // add to existing list
      }
    }

    // increment the number of files in the cache
    //int count = counter.incrementAndGet();

    // do we need a cleanup ??
    boolean needHard = false;
    boolean needSoft = false;
    synchronized (hasScheduled) {
      if (!hasScheduled.get()) {
        int count = files.size();
        if ((count > hardLimit) && (hardLimit > 0)) {
          needHard = true;
          hasScheduled.getAndSet(true); // tell other threads not to schedule another cleanup

        } else if ((count > softLimit) && (softLimit > 0)) {   // && wantsCleanup) { //
          hasScheduled.getAndSet(true); // tell other threads not to schedule another cleanup
          needSoft = true;
        }
      }
    }

    if (needHard) {
      if (debugCleanup)
        System.out.println("CleanupTask due to hard limit time=" + new Date().getTime()); // +" Thread="+Thread.currentThread().hashCode()
      cleanup(hardLimit);

    } else if (needSoft) {
      schedule(new CleanupTask(), 100); // immediate cleanup in 100 msec

      if (debugCleanup) System.out.println("CleanupTask scheduled due to soft limit time=" + new Date());
    }

    return ncfile;
  }
Acquire a FileCacheable from the cache, and lock it so no one else can use it.
If not already in cache, open it the FileFactory, and put in cache.
<p/>
App should  call FileCacheable.close when done, and the file is then released instead of closed.
<p/>
If cache size goes over maxElement, then immediately (actually in 100 msec) schedule a cleanup in a background thread.
This means that the cache should never get much larger than maxElement, unless you have them all locked.

@param factory     use this factory to open the file if not in the cache; may not be null
@param hashKey     unique key for this file. If null, the location will be used
@param location    file location, may also used as the cache name, will be passed to the NetcdfFileFactory
@param buffer_size RandomAccessFile buffer size, if <= 0, use default size
@param cancelTask  user can cancel, ok to be null.
@param spiObject   passed to the factory if object needs to be recreated
@return FileCacheable corresponding to location.
@throws IOException on error