package com.limegroup.gnutella;
import com.limegroup.gnutella.settings.ApplicationSettings;
import com.limegroup.gnutella.settings.ConnectionSettings;
import com.limegroup.gnutella.settings.DownloadSettings;
import com.limegroup.gnutella.settings.UltrapeerSettings;
import com.limegroup.gnutella.settings.UploadSettings;
import com.limegroup.gnutella.statistics.BandwidthStat;
import com.limegroup.gnutella.util.CommonUtils;
import com.limegroup.gnutella.util.ManagedThread;
import com.limegroup.gnutella.util.NetworkUtils;
import com.limegroup.gnutella.util.ThreadFactory;
/**
* This class determines whether or not this node has all of the necessary
* characteristics for it to become a ultrapeer if necessary. The criteria
* uses include the node's upload and download bandwidth, the operating
* system, the node's firewalled status, the average uptime, the current
* uptime, etc. <p>
*
* One of this class's primary functions is to run the timer that continually
* checks the amount of bandwidth passed through upstream and downstream
* HTTP file transfers. It records the maximum of the sum of these streams
* to determine the node's bandwidth.
*/
//2345678|012345678|012345678|012345678|012345678|012345678|012345678|012345678|
public final class SupernodeAssigner {
/**
* Constant value for whether or not the operating system qualifies
* this node for Ultrapeer status.
*/
private static final boolean ULTRAPEER_OS = CommonUtils.isUltrapeerOS();
/**
* Constant for the number of milliseconds between the timer's calls
* to its <tt>Runnable</tt>s.
*/
public static final int TIMER_DELAY = 1000;
/**
* Constant for the number of seconds between the timer's calls
* to its <tt>Runnable</tt>s.
*/
private static final int TIMER_DELAY_IN_SECONDS = TIMER_DELAY/1000;
/**
* A <tt>BandwidthTracker</tt> instance for keeping track of the
* upload bandwidth used for file uploads.
*/
private static BandwidthTracker _uploadTracker;
/**
* A <tt>BandwidthTracker</tt> instance for keeping track of the
* download bandwidth used for file downloads.
*/
private static BandwidthTracker _downloadTracker;
/**
* A reference to the Connection Manager
*/
private static ConnectionManager _manager;
/**
* Variable for the current uptime of this node.
*/
private static long _currentUptime = 0;
/**
* Variable for the maximum number of bytes per second transferred
* downstream over the history of the application.
*/
private static int _maxUpstreamBytesPerSec =
UploadSettings.MAX_UPLOAD_BYTES_PER_SEC.getValue();
/**
* Variable for the maximum number of bytes per second transferred
* upstream over the history of the application.
*/
private static int _maxDownstreamBytesPerSec =
DownloadSettings.MAX_DOWNLOAD_BYTES_PER_SEC.getValue();
/**
* Variable for whether or not this node has such good values that it is too
* good to pass up for becoming an Ultrapeer.
*/
private static volatile boolean _isTooGoodToPassUp = false;
/**
* Variable for the last time we attempted to become an Ultrapeer.
*/
private static long _lastAttempt = 0L;
/**
* Number of times we've tried to become an Ultrapeer.
*/
private static int _ultrapeerTries = 0;
/**
* Creates a new <tt>UltrapeerAssigner</tt>.
*
* @param uploadTracker the <tt>BandwidthTracker</tt> instance for
* tracking bandwidth used for uploads
* @param downloadTracker the <tt>BandwidthTracker</tt> instance for
* tracking bandwidth used for downloads
* @param manager Reference to the ConnectionManager for this node
*/
public SupernodeAssigner(final BandwidthTracker uploadTracker,
final BandwidthTracker downloadTracker,
ConnectionManager manager) {
_uploadTracker = uploadTracker;
_downloadTracker = downloadTracker;
_manager = manager;
}
/**
* Schedules a timer event to continually updates the upload and download
* bandwidth used. Non-blocking.
* @param router provides the schedule(..) method for the timing
*/
public void start() {
Runnable task=new Runnable() {
public void run() {
try {
collectBandwidthData();
} catch(Throwable t) {
ErrorService.error(t);
}
}
};
RouterService.schedule(task, 0, TIMER_DELAY);
}
/**
* Sets EVER_ULTRAPEER_CAPABLE to true if this has the necessary
* requirements for becoming a ultrapeer if needed, based on
* the node's bandwidth, operating system, firewalled status,
* uptime, etc. Does not modify the property if the capabilities
* are not met. If the user has disabled ultrapeer support,
* sets EVER_ULTRAPEER_CAPABLE to false.
*/
private static void setUltrapeerCapable() {
if (UltrapeerSettings.DISABLE_ULTRAPEER_MODE.getValue()) {
UltrapeerSettings.EVER_ULTRAPEER_CAPABLE.setValue(false);
return;
}
// Ignore this check if we're already an Ultrapeer --
// we already know we're Ultrapeer-capable in that
// case
if(RouterService.isSupernode())
return;
boolean isUltrapeerCapable =
//Is upstream OR downstream high enough?
(_maxUpstreamBytesPerSec >=
UltrapeerSettings.MIN_UPSTREAM_REQUIRED.getValue() ||
_maxDownstreamBytesPerSec >=
UltrapeerSettings.MIN_DOWNSTREAM_REQUIRED.getValue()) &&
//AND I'm not a modem (in case estimate wrong)
(ConnectionSettings.CONNECTION_SPEED.getValue() > SpeedConstants.MODEM_SPEED_INT) &&
//AND is my average uptime OR current uptime high enough?
(ApplicationSettings.AVERAGE_UPTIME.getValue() >= UltrapeerSettings.MIN_AVG_UPTIME.getValue() ||
_currentUptime >= UltrapeerSettings.MIN_INITIAL_UPTIME.getValue()) &&
//AND am I not firewalled?
ConnectionSettings.EVER_ACCEPTED_INCOMING.getValue() &&
//AND I have accepted incoming messages over UDP
RouterService.isGUESSCapable() &&
//AND am I a capable OS?
ULTRAPEER_OS &&
//AND I do not have a private address
!NetworkUtils.isPrivate();
long curTime = System.currentTimeMillis();
// check if this node has such good values that we simply can't pass
// it up as an Ultrapeer -- it will just get forced to be one
_isTooGoodToPassUp = isUltrapeerCapable &&
RouterService.acceptedIncomingConnection() &&
(curTime - RouterService.getLastQueryTime() > 5*60*1000) &&
(BandwidthStat.HTTP_UPSTREAM_BANDWIDTH.getAverage() < 1);
// record new ultrapeer capable value.
if(isUltrapeerCapable)
UltrapeerSettings.EVER_ULTRAPEER_CAPABLE.setValue(true);
if(_isTooGoodToPassUp && shouldTryToBecomeAnUltrapeer(curTime)) {
_ultrapeerTries++;
// try to become an Ultrapeer -- how persistent we are depends on
// how many times we've tried, and so how long we've been
// running for
final int demotes = 4 * _ultrapeerTries;
Runnable ultrapeerRunner =
new Runnable() {
public void run() {
RouterService.getConnectionManager().tryToBecomeAnUltrapeer(demotes);
}
};
ThreadFactory.startThread(ultrapeerRunner, "UltrapeerAttemptThread");
}
}
/**
* Checks whether or not we should try again to become an Ultrapeer.
*
* @param curTime the current time in milliseconds
* @return <tt>true</tt> if we should try again to become an Ultrapeer,
* otherwise <tt>false</tt>
*/
private static boolean shouldTryToBecomeAnUltrapeer(long curTime) {
if(curTime - _lastAttempt < UltrapeerSettings.UP_RETRY_TIME.getValue()) {
return false;
}
_lastAttempt = curTime;
return true;
}
/**
* Accessor for whether or not this machine has settings that are too good
* to pass up for Ultrapeer election.
*
* @return <tt>true</tt> if this node has extremely good Ultrapeer settings,
* otherwise <tt>false</tt>
*/
public static boolean isTooGoodToPassUp() {
return _isTooGoodToPassUp;
}
/**
* Collects data on the bandwidth that has been used for file uploads
* and downloads.
*/
private static void collectBandwidthData() {
_currentUptime += TIMER_DELAY_IN_SECONDS;
_uploadTracker.measureBandwidth();
_downloadTracker.measureBandwidth();
_manager.measureBandwidth();
float bandwidth = 0;
try {
bandwidth = _uploadTracker.getMeasuredBandwidth();
}catch(InsufficientDataException ide) {
bandwidth = 0;
}
int newUpstreamBytesPerSec =
(int)bandwidth
+(int)_manager.getMeasuredUpstreamBandwidth();
bandwidth = 0;
try {
bandwidth = _downloadTracker.getMeasuredBandwidth();
} catch (InsufficientDataException ide) {
bandwidth = 0;
}
int newDownstreamBytesPerSec =
(int)bandwidth
+(int)_manager.getMeasuredDownstreamBandwidth();
if(newUpstreamBytesPerSec > _maxUpstreamBytesPerSec) {
_maxUpstreamBytesPerSec = newUpstreamBytesPerSec;
UploadSettings.MAX_UPLOAD_BYTES_PER_SEC.setValue(_maxUpstreamBytesPerSec);
}
if(newDownstreamBytesPerSec > _maxDownstreamBytesPerSec) {
_maxDownstreamBytesPerSec = newDownstreamBytesPerSec;
DownloadSettings.MAX_DOWNLOAD_BYTES_PER_SEC.setValue(_maxDownstreamBytesPerSec);
}
//check if became ultrapeer capable
setUltrapeerCapable();
}
}