package com.limegroup.gnutella.downloader; import java.util.Collection; import java.util.Iterator; import com.limegroup.gnutella.RemoteFileDesc; import com.limegroup.gnutella.RouterService; import com.limegroup.gnutella.settings.DownloadSettings; /** * A class that ranks sources for a download. * * It uses a factory pattern to provide the best ranker based on system * conditions. */ public abstract class SourceRanker { /** * The mesh handler to inform when altlocs fail */ protected MeshHandler meshHandler; /** * @param hosts a collection of remote hosts to rank * @return if we didn't know about at least one of the hosts */ public boolean addToPool(Collection hosts) { boolean ret = false; for (Iterator iter = hosts.iterator(); iter.hasNext();) { RemoteFileDesc host = (RemoteFileDesc) iter.next(); if (addToPool(host)) ret = true; } return ret; } /** * @param host the host that the ranker should consider * @return if we did not already know about this host */ public abstract boolean addToPool(RemoteFileDesc host); /** * @return whether the ranker has any more potential sources */ public abstract boolean hasMore(); /** * @return the source that should be tried next */ public abstract RemoteFileDesc getBest(); /** * @return the collection of hosts that can be shared with other rankers */ protected abstract Collection getShareableHosts(); /** * @return the number of hosts this ranker knows about */ public abstract int getNumKnownHosts(); /** * @return the ranker knows about at least one potential source that is * not currently busy */ public synchronized boolean hasNonBusy() { return getNumKnownHosts() > getNumBusyHosts(); } /** * @return the number of busy hosts the ranker knows about */ public synchronized int getNumBusyHosts() { int ret = 0; long now = System.currentTimeMillis(); for (Iterator iter = getPotentiallyBusyHosts().iterator(); iter.hasNext();) { RemoteFileDesc rfd = (RemoteFileDesc) iter.next(); if (rfd.isBusy(now)) ret++; } return ret; } /** * @return how much time we should wait before at least one host * will become non-busy */ public synchronized int calculateWaitTime() { if (!hasMore()) return 0; // waitTime is in seconds int waitTime = Integer.MAX_VALUE; long now = System.currentTimeMillis(); for (Iterator iter = getPotentiallyBusyHosts().iterator(); iter.hasNext();) { RemoteFileDesc rfd = (RemoteFileDesc) iter.next(); if (!rfd.isBusy(now)) continue; waitTime = Math.min(waitTime, rfd.getWaitTime(now)); } // waitTime was in seconds return (waitTime*1000); } protected abstract Collection getPotentiallyBusyHosts(); /** * stops the ranker, clearing any state */ public synchronized void stop() { clearState(); meshHandler = null; } protected void clearState() {} /** * @return a ranker appropriate for our system's capabilities. */ public static SourceRanker getAppropriateRanker() { if (RouterService.canReceiveSolicited() && DownloadSettings.USE_HEADPINGS.getValue()) return new PingRanker(); else return new LegacyRanker(); } /** * @param original the current ranker that we use * @return the ranker that should be used. If different than the current one, * the current one is stopped. */ public static SourceRanker getAppropriateRanker(SourceRanker original) { if(original == null) return getAppropriateRanker(); SourceRanker better; if (RouterService.canReceiveSolicited() && DownloadSettings.USE_HEADPINGS.getValue()) { if (original instanceof PingRanker) return original; better = new PingRanker(); }else { if (original instanceof LegacyRanker) return original; better = new LegacyRanker(); } better.setMeshHandler(original.getMeshHandler()); better.addToPool(original.getShareableHosts()); original.stop(); return better; } /** sets the Mesh handler if any */ public synchronized void setMeshHandler(MeshHandler handler) { meshHandler = handler; } /** * @return the Mesh Handler, if any */ public synchronized MeshHandler getMeshHandler() { return meshHandler; } }