package com.limegroup.gnutella; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.limewire.io.GUID; import com.google.inject.Provider; import com.limegroup.gnutella.guess.GUESSEndpoint; /** * Keeps track of possible ips that provide results for * query GUIDs and have not been queried yet. */ public class BypassedResultsCache { /** * The maximum number of bypassed results to remember per query. */ static int MAX_BYPASSED_RESULTS = 150; private final Provider<? extends ActivityCallback> _callback; private final DownloadManager _downloadManager; /** * Keeps track of potential sources of content. Comprised of Sets of GUESS * Endpoints. Kept tidy when searches/downloads are killed. */ private final Map<GUID, Set<GUESSEndpoint>> _bypassedResults = Collections.synchronizedMap(new HashMap<GUID, Set<GUESSEndpoint>>()); public BypassedResultsCache(Provider<? extends ActivityCallback> callback, DownloadManager downloadManager) { _callback = callback; _downloadManager = downloadManager; } // made package private until this is refactored to events and // the cache can subscribe to a query killed event void queryKilled(GUID guid) { if (!_downloadManager.isGuidForQueryDownloading(guid)) { _bypassedResults.remove(guid); } } // made package private until this is refactored to events and // the cache can subscribe to a download finished event void downloadFinished(GUID guid) { if (!isGUIDOfInterest(guid)) { _bypassedResults.remove(guid); } } /** * Returns a set of possible query endpoints for the guid or * an empty set if there aren't any. * * @return the set is owned by the caller and can be modified */ public Set<GUESSEndpoint> getQueryLocs(GUID guid) { Set<GUESSEndpoint> clone = new HashSet<GUESSEndpoint>(); synchronized (_bypassedResults) { Set<GUESSEndpoint> eps = _bypassedResults.get(guid); if (eps != null) clone.addAll(eps); } return clone; } /** * Returns true if the guid is of interest to this peer, i.e. there is an * active query or a download for this guid. */ private boolean isGUIDOfInterest(GUID guid) { return _callback.get().isQueryAlive(guid) || _downloadManager.isGuidForQueryDownloading(guid); } /** * Adds the endpoint to its internal cache and returns true if it * does so. */ public boolean addBypassedSource(GUID guid, GUESSEndpoint endpoint) { if (!isGUIDOfInterest(guid)) { return false; } synchronized (_bypassedResults) { // this is a quick critical section for _bypassedResults // AND the set within it Set<GUESSEndpoint> eps = _bypassedResults.get(guid); if (eps == null) { eps = new HashSet<GUESSEndpoint>(); _bypassedResults.put(guid, eps); } if (eps.size() < MAX_BYPASSED_RESULTS) { return eps.add(endpoint); } else { return false; } } } }