/** * Remove a file from the cache. Called after it has been removed from its ArchiveHandler. * * @param item The ArchiveStoreItem to remove. */ synchronized void removeCachedItem(ArchiveStoreItem item) { long size = item.spaceUsed(); storedData.removeKey(item.key); // Hard disk space limit = remove it here. // Soft disk space limit would be to remove it outside the lock. // Soft disk space limit = we go over the limit significantly when we // are overloaded. cachedData -= size; if (logMINOR) Logger.minor(this, "removeCachedItem: " + item); item.close(); }
/** * Get a cached, previously extracted, file from an archive. * * @param key The key used to fetch the archive. * @param filename The name of the file within the archive. * @return A Bucket containing the data requested, or null. * @throws ArchiveFailureException */ public Bucket getCached(FreenetURI key, String filename) throws ArchiveFailureException { if (logMINOR) Logger.minor(this, "Fetch cached: " + key + ' ' + filename); ArchiveKey k = new ArchiveKey(key, filename); ArchiveStoreItem asi = null; synchronized (this) { asi = storedData.get(k); if (asi == null) return null; // Promote to top of LRU storedData.push(k, asi); } if (logMINOR) Logger.minor(this, "Found data"); return asi.getReaderBucket(); }
/** * Add a store element. * * @param callbackName If set, the name of the file for which we must call the callback if this * file happens to match. * @param gotElement Flag indicating whether we've already found the file for the callback. If so * we must not call it again. * @param callback Callback to be called if we do find it. We must getReaderBucket() before adding * the data to the LRU, otherwise it may be deleted before it reaches the client. * @throws ArchiveFailureException If a failure occurred resulting in the data not being readable. * Only happens if callback != null. */ private ArchiveStoreItem addStoreElement( ArchiveStoreContext ctx, FreenetURI key, String name, Bucket temp, MutableBoolean gotElement, String callbackName, ArchiveExtractCallback callback, ClientContext context) throws ArchiveFailureException { RealArchiveStoreItem element = new RealArchiveStoreItem(ctx, key, name, temp); element.addToContext(); if (logMINOR) Logger.minor( this, "Adding store element: " + element + " ( " + key + ' ' + name + " size " + element.spaceUsed() + " )"); ArchiveStoreItem oldItem; // Let it throw, if it does something is drastically wrong Bucket matchBucket = null; if ((!gotElement.value) && name.equals(callbackName)) { matchBucket = element.getReaderBucket(); } synchronized (this) { oldItem = storedData.get(element.key); storedData.push(element.key, element); cachedData += element.spaceUsed(); if (oldItem != null) { cachedData -= oldItem.spaceUsed(); if (logMINOR) Logger.minor(this, "Dropping old store element from archive cache: " + oldItem); oldItem.close(); } } if (matchBucket != null) { callback.gotBucket(matchBucket, context); gotElement.value = true; } return element; }
/** * Add an error element to the cache. This happens when a single file in the archive is invalid * (usually because it is too large). * * @param ctx The ArchiveStoreContext which must be notified about this element's creation. * @param key The key from which the archive was fetched. * @param name The name of the file within the archive. * @param error The error message to be included on the eventual exception thrown, if anyone tries * to extract the data for this element. */ private void addErrorElement( ArchiveStoreContext ctx, FreenetURI key, String name, String error, boolean tooBig) { ErrorArchiveStoreItem element = new ErrorArchiveStoreItem(ctx, key, name, error, tooBig); element.addToContext(); if (logMINOR) Logger.minor(this, "Adding error element: " + element + " for " + key + ' ' + name); ArchiveStoreItem oldItem; synchronized (this) { oldItem = storedData.get(element.key); storedData.push(element.key, element); if (oldItem != null) { oldItem.close(); cachedData -= oldItem.spaceUsed(); if (logMINOR) Logger.minor(this, "Dropping old store element from archive cache: " + oldItem); } } }
/** Drop any stored data beyond the limit. Call synchronized on storedData. */ private void trimStoredData() { synchronized (this) { while (true) { ArchiveStoreItem item; if (cachedData <= maxCachedData && storedData.size() <= maxCachedElements) return; if (storedData.isEmpty()) { // Race condition? cachedData out of sync? Logger.error( this, "storedData is empty but still over limit: cachedData=" + cachedData + " / " + maxCachedData); return; } item = storedData.popValue(); long space = item.spaceUsed(); cachedData -= space; // Hard limits = delete file within lock, soft limits = delete outside of lock // Here we use a hard limit if (logMINOR) Logger.minor( this, "Dropping " + item + " : cachedData=" + cachedData + " of " + maxCachedData + " stored items : " + storedData.size() + " of " + maxCachedElements); item.close(); } } }