package com.limegroup.gnutella.downloader;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.limewire.util.Visitor;
/**
* 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 AbstractSourceRanker implements SourceRanker {
/** The mesh handler to inform when altlocs fail */
private MeshHandler meshHandler;
/** A visitor to verify if RFDs are OK to use. */
private Visitor<RemoteFileDescContext> rfdVisitor;
public boolean addToPool(Collection<? extends RemoteFileDescContext> hosts) {
boolean ret = false;
for(RemoteFileDescContext host : hosts) {
if (addToPool(host))
ret = true;
}
return ret;
}
public abstract boolean addToPool(RemoteFileDescContext host);
public abstract boolean hasMore();
public abstract RemoteFileDescContext getBest();
/**
* @return the collection of hosts that can be shared with other rankers
*/
public abstract Collection<RemoteFileDescContext> getShareableHosts();
/** Returns true if there's atleast one usable host. */
public synchronized boolean hasUsableHosts() {
final long now = System.currentTimeMillis();
final Visitor<RemoteFileDescContext> rfdValidator = getRfdVisitor();
final AtomicBoolean usable = new AtomicBoolean(false);
visitSources(new Visitor<RemoteFileDescContext>() {
@Override
public boolean visit(RemoteFileDescContext context) {
if((rfdValidator == null || rfdValidator.visit(context)) && !context.isBusy(now)) {
usable.set(true);
return false; // short-circuit, there's a valid one.
}
return true; // keep looking.
}
});
return usable.get();
}
public synchronized int calculateWaitTime() {
if (!hasMore()) {
return 0;
}
// waitTime is in seconds
final AtomicInteger waitTime = new AtomicInteger(Integer.MAX_VALUE);
final long now = System.currentTimeMillis();
final Visitor<RemoteFileDescContext> rfdValidator = getRfdVisitor();
visitSources(new Visitor<RemoteFileDescContext>() {
@Override
public boolean visit(RemoteFileDescContext context) {
if((rfdValidator == null || rfdValidator.visit(context)) && context.isBusy(now)) {
waitTime.set(Math.min(waitTime.get(), context.getWaitTime(now)));
}
return true;
}
});
// Nothing was busy -- no wait time.
if (waitTime.get() == Integer.MAX_VALUE) {
return 0;
} else {
// waitTime was in seconds
return (waitTime.get() * 1000);
}
}
public synchronized void stop() {
clearState();
meshHandler = null;
}
protected void clearState() {}
public synchronized void setMeshHandler(MeshHandler handler) {
meshHandler = handler;
}
public synchronized MeshHandler getMeshHandler() {
return meshHandler;
}
public synchronized void setRfdVisitor(Visitor<RemoteFileDescContext> rfdVisitor) {
this.rfdVisitor = rfdVisitor;
}
public synchronized Visitor<RemoteFileDescContext> getRfdVisitor() {
return rfdVisitor;
}
/**
* Visits each source in the ranker with the visitor.
* When the visitor returns false, iteration stops and this method returns false.
* If all sources are visited (with the iterator returning true for each one),
* then this method returns true.
*/
abstract protected boolean visitSources(Visitor<RemoteFileDescContext> contextVisitor);
}