/* This code is part of Freenet. It is distributed under the GNU General * Public License, version 2 (or at your option any later version). See * http://www.gnu.org/ for further details of the GPL. */ package freenet.client; import java.util.LinkedList; import freenet.keys.FreenetURI; import freenet.support.LogThresholdCallback; import freenet.support.Logger; import freenet.support.Logger.LogLevel; /** * Tracks all files currently in the cache from a given key. * Keeps the last known hash of the key (if this changes in a fetch, we flush the cache, unpack, * then throw an ArchiveRestartedException). * Provides fetch methods for Fetcher, which try the cache and then fetch if necessary, * subject to the above. * * Always take the lock on ArchiveStoreContext before the lock on ArchiveManager, NOT the other way around. */ class ArchiveStoreContext { private FreenetURI key; private final ArchiveManager.ARCHIVE_TYPE archiveType; /** Archive size */ private long lastSize = -1; /** Archive hash */ private byte[] lastHash; /** Index of still-cached ArchiveStoreItems with this key. * Note that we never ever hold this and then take another lock! In particular * we must not take the ArchiveManager lock while holding this lock. It must be * the inner lock to avoid deadlocks. */ private final LinkedList<ArchiveStoreItem> myItems; private static volatile boolean logMINOR; static { Logger.registerLogThresholdCallback(new LogThresholdCallback(){ @Override public void shouldUpdate(){ logMINOR = Logger.shouldLog(LogLevel.MINOR, this); } }); } ArchiveStoreContext(FreenetURI key, ArchiveManager.ARCHIVE_TYPE archiveType) { this.key = key; this.archiveType = archiveType; myItems = new LinkedList<ArchiveStoreItem>(); } /** Returns the size of the archive last time we fetched it, or -1 */ long getLastSize() { return lastSize; } /** Sets the size of the archive - @see getLastSize() */ void setLastSize(long size) { lastSize = size; } /** Returns the hash of the archive last time we fetched it, or null */ byte[] getLastHash() { return lastHash; } /** Sets the hash of the archive - @see getLastHash() */ void setLastHash(byte[] realHash) { lastHash = realHash; } /** * Remove all ArchiveStoreItems with this key from the cache. */ void removeAllCachedItems(ArchiveManager manager) { ArchiveStoreItem item = null; while(true) { synchronized (myItems) { // removeCachedItem() will call removeItem(), so don't remove it here. item = myItems.peek(); } if(item == null) break; manager.removeCachedItem(item); } } /** Notify that a new archive store item with this key has been added to the cache. */ void addItem(ArchiveStoreItem item) { synchronized(myItems) { myItems.push(item); } } /** Notify that an archive store item with this key has been expelled from the * cache. Remove it from our local cache and ask it to free the bucket if * necessary. */ void removeItem(ArchiveStoreItem item) { synchronized(myItems) { if (!myItems.remove(item)) { if(logMINOR) Logger.minor(this, "Not removing: "+item+" for "+this+" - already removed"); return; // only removed once } } item.innerClose(); } public short getArchiveType() { return archiveType.metadataID; } public FreenetURI getKey() { return key; } }