public synchronized void remove(K key) {
    String digest = mKeyToDigest.remove(key);
    sLog.debug("Removing %s, digest=%s", key, digest);

    if (digest != null) {
      Set<K> keys = mDigestToKeys.get(digest);
      if (keys != null) {
        keys.remove(key);
      }

      if (keys == null || keys.isEmpty()) {
        File file = mDigestToFile.remove(digest);
        mDigestToKeys.remove(digest);
        sLog.debug("Deleting unreferenced file %s.", file);
        if (file != null) {
          try {
            FileUtil.delete(file);
          } catch (Exception e) { // IOException and SecurityException
            ZimbraLog.store.warn("Unable to remove a file from the uncompressed cache.", e);
          }
        }
      } else {
        sLog.debug("Not deleting %s.  It is referenced by %s.", digest, keys);
      }
    }
  }
  /**
   * Returns the uncompressed version of the given file. If the uncompressed file is not in the
   * cache, uncompresses it and adds it to the cache.
   *
   * @param key the key used to look up the uncompressed data
   * @param compressedFile the compressed file. This file is read, if necessary, to write the
   *     uncompressed file.
   * @param sync <tt>true</tt> to use fsync
   */
  public SharedFile get(K key, File compressedFile, boolean sync) throws IOException {
    File uncompressedFile = null;

    sLog.debug("Looking up SharedFile for key %s, path %s.", key, compressedFile.getPath());

    synchronized (this) {
      String digest = mKeyToDigest.get(key);
      sLog.debug("Digest for %s is %s", key, digest);
      if (digest != null) {
        uncompressedFile = mDigestToFile.get(digest);
        if (uncompressedFile != null) {
          sLog.debug("Found existing uncompressed file.  Returning new SharedFile.");
          return new SharedFile(uncompressedFile);
        } else {
          sLog.debug("No existing uncompressed file.");
        }
      }
    }

    // Uncompress the file outside of the synchronized block.
    UncompressedFile temp = uncompressToTempFile(compressedFile, sync);
    SharedFile shared = null;

    synchronized (this) {
      uncompressedFile = mDigestToFile.get(temp.digest);

      if (uncompressedFile != null) {
        // Another thread uncompressed the same file at the same time.
        sLog.debug("Found existing uncompressed file.  Deleting %s.", temp.file);
        mapKeyToDigest(key, temp.digest);
        FileUtil.delete(temp.file);
        shared = new SharedFile(uncompressedFile);
      } else {
        uncompressedFile = new File(mCacheDir, temp.digest);
        sLog.debug("Renaming %s to %s.", temp.file, uncompressedFile);
        FileUtil.rename(temp.file, uncompressedFile);
        shared = new SharedFile(uncompressedFile); // Opens the file implicitly.
        put(key, temp.digest, uncompressedFile);
      }
    }

    return shared;
  }