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;
}
}