FileDocCategorySizeDatePackage
TRTrackerServerImpl.javaAPI DocAzureus 3.0.3.423089Wed Sep 12 23:18:04 BST 2007org.gudy.azureus2.core3.tracker.server.impl

TRTrackerServerImpl

public abstract class TRTrackerServerImpl extends Object implements TRTrackerServer
author
parg

Fields Summary
public static final int
RETRY_MINIMUM_SECS
public static final int
RETRY_MINIMUM_MILLIS
public static final int
CLIENT_TIMEOUT_MULTIPLIER
public static final int
TIMEOUT_CHECK
public static int
max_peers_to_send
public static boolean
send_peer_ids
public static int
announce_cache_period
public static int
scrape_cache_period
public static int
announce_cache_threshold
public static int
max_seed_retention
public static int
seed_limit
public static boolean
full_scrape_enable
public static boolean
restrict_non_blocking_requests
public static boolean
all_networks_permitted
public static String[]
permitted_networks
public static boolean
support_experimental_extensions
public static String
redirect_on_not_found
private static Map
torrent_map
private static Map
link_map
protected AEMonitor
class_mon
protected IpFilter
ip_filter
private long
current_announce_retry_interval
private long
current_scrape_retry_interval
private long
current_total_clients
private int
current_min_poll_interval
private TRTrackerServerStatsImpl
stats
private String
name
private boolean
web_password_enabled
private boolean
web_password_https_only
private boolean
tracker_password_enabled
private String
password_user
private byte[]
password_pw
private boolean
compact_enabled
private boolean
key_enabled
protected Vector
listeners
private List
auth_listeners
private Vector
request_listeners
protected AEMonitor
this_mon
private COConfigurationListener
config_listener
private boolean
destroyed
private Set
biased_peers
private boolean
is_ready
Constructors Summary
public TRTrackerServerImpl(String _name, boolean _start_up_ready)

	
	
	
				
				 
	
		name		= _name==null?DEFAULT_NAME:_name;
		is_ready	= _start_up_ready;
		
		
		config_listener = 
			new COConfigurationListener()
			{
				public void
				configurationSaved()
				{
					readConfigSettings();
				}
			};
		
		COConfigurationManager.addListener( config_listener );

		readConfigSettings();
					
		current_min_poll_interval	= COConfigurationManager.getIntParameter("Tracker Poll Interval Min", DEFAULT_MIN_RETRY_DELAY );
		
		if ( current_min_poll_interval < RETRY_MINIMUM_SECS ){
			
			current_min_poll_interval = RETRY_MINIMUM_SECS;
		}
		
		current_announce_retry_interval = current_min_poll_interval;		

		int	scrape_percentage 		= COConfigurationManager.getIntParameter("Tracker Scrape Retry Percentage", DEFAULT_SCRAPE_RETRY_PERCENTAGE );		
	
		current_scrape_retry_interval	= (current_announce_retry_interval*scrape_percentage)/100;
		
		Thread timer_thread = 
			new AEThread("TrackerServer:timer.loop")
			{
				public void
				runSupport( )
				{
					timerLoop();
				}
			};
		
		timer_thread.setDaemon( true );
		
		timer_thread.start();
	
Methods Summary
public voidaddAuthenticationListener(TRTrackerServerAuthenticationListener l)

		auth_listeners.add( l );
	
public TRTrackerServerTorrentaddLink(java.lang.String link, TRTrackerServerTorrent target)

		try{
			class_mon.enter();
			
			return((TRTrackerServerTorrent)link_map.put( link, target ));
			
		}finally{
			
			class_mon.exit();
		}
	
public voidaddListener(TRTrackerServerListener l)

		try{
			this_mon.enter();
		
			listeners.addElement( l );
			
		}finally{
			
			this_mon.exit();
		}
	
public voidaddRequestListener(TRTrackerServerRequestListener l)

		request_listeners.addElement( l );
	
public voidclose()

		TRTrackerServerFactoryImpl.close( this );
	
protected abstract voidcloseSupport()

public voiddeny(byte[] _hash, boolean _explicit)

		// System.out.println( "TRTrackerServerImpl::deny( " + _explicit + ")");
		
		HashWrapper	hash = new HashWrapper( _hash );
		
		for (int i=0;i<listeners.size();i++){
			
			if ( !((TRTrackerServerListener)listeners.elementAt(i)).denied( _hash, _explicit )){				
				
				throw( new TRTrackerServerException( "operation denied"));			
			}
		}

		try{
			class_mon.enter();
			
			TRTrackerServerTorrentImpl	entry = (TRTrackerServerTorrentImpl)torrent_map.get( hash );
	
			if ( entry != null ){
				
				entry.delete();
			}
		
			torrent_map.remove( hash );
			
		}finally{
			
			class_mon.exit();
		}
	
protected voiddestroySupport()

		destroyed	= true;
		
		COConfigurationManager.removeListener( config_listener );
	
protected static booleangetAllNetworksSupported()

		return( all_networks_permitted );
	
protected static intgetAnnounceCachePeerThreshold()

		return( announce_cache_threshold );
	
protected static intgetAnnounceCachePeriod()

		return( announce_cache_period );
	
public longgetAnnounceRetryInterval(TRTrackerServerTorrentImpl torrent)

		
		long	clients = current_total_clients;
		
		if ( clients == 0 ){
			
			return( current_announce_retry_interval );
		}
		
		long	res = ( torrent.getPeerCount() * current_announce_retry_interval ) / clients;
		
		if ( res < current_min_poll_interval ){
			
			res = current_min_poll_interval;
		}
		
		return( res );
	
protected java.util.SetgetBiasedPeers()

		return( biased_peers );
	
protected static intgetMaxPeersToSend()

		return( max_peers_to_send );
	
protected static intgetMaxSeedRetention()

		return( max_seed_retention );
	
public longgetMinAnnounceRetryInterval()

		return( current_min_poll_interval );
	
public longgetMinScrapeRetryInterval()

		return( current_min_poll_interval );
	
public java.lang.StringgetName()

		return( name );
	
public byte[]getPassword()

		return( password_pw );
	
public TRTrackerServerPeer[]getPeers(byte[] hash)

		TRTrackerServerTorrentImpl	torrent = getTorrent( hash );
		
		if ( torrent == null ){
			
			return( null );
		}
		
		return( torrent.getPeers());
	
protected static java.lang.String[]getPermittedNetworks()

		return( permitted_networks );
	
protected static intgetScrapeCachePeriod()

		return( scrape_cache_period );
	
public longgetScrapeRetryInterval(TRTrackerServerTorrentImpl torrent)

		
		long	clients = current_total_clients;
		
		if ( torrent == null || clients == 0 ){
			
			return( current_scrape_retry_interval );
		}
		
		long	res = ( torrent.getPeerCount() * current_scrape_retry_interval ) / clients;
		
		if ( res < current_min_poll_interval ){
			
			res = current_min_poll_interval;
		}
		
		return( res );
	
protected static intgetSeedLimit()

		return( seed_limit );
	
protected static booleangetSendPeerIds()

		return( send_peer_ids );
	
public TRTrackerServerStatsgetStats()

		return( stats );
	
public TRTrackerServerTorrentStatsgetStats(byte[] hash)

		TRTrackerServerTorrentImpl	torrent = getTorrent( hash );
		
		if ( torrent == null ){
			
			return( null );
		}
		
		return( torrent.getStats());
	
public TRTrackerServerTorrentImplgetTorrent(byte[] hash)

		try{
			class_mon.enter();
		
			return((TRTrackerServerTorrentImpl)torrent_map.get(new HashWrapper(hash)));
			
		}finally{
			
			class_mon.exit();
		}
	
public TRTrackerServerTorrentImplgetTorrent(java.lang.String link)

		try{
			class_mon.enter();
		
			return((TRTrackerServerTorrentImpl)link_map.get( link ));
			
		}finally{
			
			class_mon.exit();
		}
	
public intgetTorrentCount()

		return( torrent_map.size());
	
public TRTrackerServerTorrentImpl[]getTorrents()

		try{
			class_mon.enter();
		
			TRTrackerServerTorrentImpl[]	res = new TRTrackerServerTorrentImpl[torrent_map.size()];
			
			torrent_map.values().toArray( res );
			
			return( res );	
		}finally{
			
			class_mon.exit();
		}
	
public java.lang.StringgetUsername()

		return( password_user );
	
public booleanhasExternalAuthorisation()

		return( auth_listeners.size() > 0 );
	
public booleanhasInternalAuthorisation()

		return( web_password_enabled || tracker_password_enabled );
	
public booleanisCompactEnabled()

		return( compact_enabled );
	
public static booleanisFullScrapeEnabled()

		return( full_scrape_enable );
	
public booleanisKeyEnabled()

		return( key_enabled );
	
public final booleanisReady()

		return( is_ready );
	
public booleanisTrackerPasswordEnabled()

		return( tracker_password_enabled || auth_listeners.size() > 0 );
	
public booleanisWebPasswordEnabled()

		return( web_password_enabled || auth_listeners.size() > 0 );
	
public booleanisWebPasswordHTTPSOnly()

		return( web_password_https_only );
	
public booleanperformExternalAuthorisation(java.net.URL resource, java.lang.String user, java.lang.String password)

		for (int i=0;i<auth_listeners.size();i++){
			
			try{
				
				if ( ((TRTrackerServerAuthenticationListener)auth_listeners.get(i)).authenticate( resource, user, password )){
					
					return( true );
				}
			}catch( Throwable e ){
				
				Debug.printStackTrace( e );
			}
		}
		
		return( false );
	
public byte[]performExternalAuthorisation(java.net.URL resource, java.lang.String user)

		for (int i=0;i<auth_listeners.size();i++){
			
			try{
				
				byte[] sha_pw =  ((TRTrackerServerAuthenticationListener)auth_listeners.get(i)).authenticate( resource, user );
					
				if ( sha_pw != null ){
					
					return( sha_pw );
				}
			}catch( Throwable e ){
				
				Debug.printStackTrace( e );
			}
		}
		
		return( null );
	
public TRTrackerServerTorrentpermit(java.lang.String _originator, byte[] _hash, boolean _explicit)

		return( permit( _originator, _hash, _explicit, true ));
	
public TRTrackerServerTorrentpermit(java.lang.String _originator, byte[] _hash, boolean _explicit, boolean _enabled)

		// System.out.println( "TRTrackerServerImpl::permit( " + _explicit + ")");
		
		HashWrapper	hash = new HashWrapper( _hash );
		
			// don't invoke listeners when synched, deadlock possible
		
		TRTrackerServerTorrentImpl	entry;
		
		try{
			class_mon.enter();
			
			entry = (TRTrackerServerTorrentImpl)torrent_map.get( hash );
			
		}finally{
			
			class_mon.exit();
		}
		
		if ( entry == null ){
			
			for (int i=0;i<listeners.size();i++){
				
				if ( !((TRTrackerServerListener)listeners.elementAt(i)).permitted( _originator, _hash, _explicit )){
					
					throw( new TRTrackerServerException( "operation denied"));			
				}
			}
		
			try{
				class_mon.enter();
			
					// double check in-case added in parallel
				
				entry = (TRTrackerServerTorrentImpl)torrent_map.get( hash );
				
				if ( entry == null ){
				
					entry = new TRTrackerServerTorrentImpl( this, hash, _enabled );
				
					torrent_map.put( hash, entry );
				}
			}finally{
				
				class_mon.exit();
			}
		}
		
		return( entry );
	
public voidpostProcess(TRTrackerServerPeer peer, TRTrackerServerTorrentImpl torrent, int type, java.lang.String request, java.util.Map response)

		if ( request_listeners.size() > 0 ){
			
			TRTrackerServerRequestImpl	req = new TRTrackerServerRequestImpl( this, peer, torrent, type, request, response );
			
			for (int i=0;i<request_listeners.size();i++){
				
				try{
					((TRTrackerServerRequestListener)request_listeners.elementAt(i)).postProcess( req );
				
				}catch( TRTrackerServerException e ){
					
					throw( e );
					
				}catch( Throwable e ){
					
					Debug.printStackTrace( e );
				}
			}
		}
	
public voidpreProcess(TRTrackerServerPeer peer, TRTrackerServerTorrent torrent, int type, java.lang.String request, java.util.Map response)

		if ( request_listeners.size() > 0 ){
			
				// if this is a scrape then we need to patch up stuff as it may be multi-scrape
			
			if ( type == TRTrackerServerRequest.RT_SCRAPE ){
				
				try{
					int	request_pos = 10;
					
					while( true ){
						
						int	p = request.indexOf( "info_hash=", request_pos );
						
						String	bit;
						
						if ( p == -1 ){
						
							if ( request_pos == 10 ){
								
								break;	// only one entry, nothing to do
							}
							
							bit = request.substring( request_pos );
							
						}else{
							
							bit = request.substring( request_pos, p );
						}
														
						int	pos = bit.indexOf('&");
						
						String	hash_str = pos==-1?bit:bit.substring(0,pos);
						
						hash_str = URLDecoder.decode( hash_str, Constants.BYTE_ENCODING );
						
						byte[]	hash = hash_str.getBytes(Constants.BYTE_ENCODING);
						
						if ( Arrays.equals( hash, torrent.getHash().getBytes())){
							
							request = "info_hash=" + bit;
							
							if ( request.endsWith("&")){
								
								request = request.substring(0,request.length()-1);
							}
							
							break;
						}
						
						if ( p == -1 ){
							
							break;
						}
						
						request_pos = p + 10;
					}
				}catch( Throwable e ){
						
					Debug.printStackTrace(e);
				}
			}
			
			TRTrackerServerRequestImpl	req = new TRTrackerServerRequestImpl( this, peer, torrent, type, request, response );
			
			for (int i=0;i<request_listeners.size();i++){
				
				try{
					((TRTrackerServerRequestListener)request_listeners.elementAt(i)).preProcess( req );
					
				}catch( TRTrackerServerException e ){
				
					throw( e );
					
				}catch( Throwable e ){
					
					Debug.printStackTrace( e );
				}
			}
		}
	
protected static voidreadConfig()



	

		COConfigurationManager.addListener(
			new COConfigurationListener()
			{
				public void
				configurationSaved()
				{
					readConfig();
				}
			});
		
		readConfig();
	
		send_peer_ids = COConfigurationManager.getBooleanParameter( "Tracker Send Peer IDs" );
				
		max_peers_to_send = COConfigurationManager.getIntParameter( "Tracker Max Peers Returned" );
				
		scrape_cache_period = COConfigurationManager.getIntParameter( "Tracker Scrape Cache", TRTrackerServer.DEFAULT_SCRAPE_CACHE_PERIOD );
				
		announce_cache_period = COConfigurationManager.getIntParameter( "Tracker Announce Cache", TRTrackerServer.DEFAULT_ANNOUNCE_CACHE_PERIOD );
						
		announce_cache_threshold = COConfigurationManager.getIntParameter( "Tracker Announce Cache Min Peers", TRTrackerServer.DEFAULT_ANNOUNCE_CACHE_PEER_THRESHOLD );

		max_seed_retention = COConfigurationManager.getIntParameter( "Tracker Max Seeds Retained", 0 );
		
		seed_limit = COConfigurationManager.getIntParameter( "Tracker Max Seeds", 0 );
		
		List	nets = new ArrayList();
		
		for (int i=0;i<AENetworkClassifier.AT_NETWORKS.length;i++){
			
			String	net = AENetworkClassifier.AT_NETWORKS[i];
			
			boolean	enabled = 
				COConfigurationManager.getBooleanParameter(
						"Tracker Network Selection Default." + net );
			
			if ( enabled ){
				
				nets.add( net );
			}
		}
		
		String[]	s_nets = new String[nets.size()];
		
		nets.toArray(s_nets);
		
		permitted_networks	= s_nets;
		
		all_networks_permitted = s_nets.length == AENetworkClassifier.AT_NETWORKS.length;
		
		full_scrape_enable = COConfigurationManager.getBooleanParameter( "Tracker Server Full Scrape Enable" );

		redirect_on_not_found = COConfigurationManager.getStringParameter( "Tracker Server Not Found Redirect" ).trim();
		
		support_experimental_extensions = COConfigurationManager.getBooleanParameter( "Tracker Server Support Experimental Extensions" );
		
		restrict_non_blocking_requests = COConfigurationManager.getBooleanParameter( "Tracker TCP NonBlocking Restrict Request Types" );
	
protected voidreadConfigSettings()

		
		web_password_enabled 		= COConfigurationManager.getBooleanParameter("Tracker Password Enable Web");
		tracker_password_enabled 	= COConfigurationManager.getBooleanParameter("Tracker Password Enable Torrent");

		web_password_https_only		= COConfigurationManager.getBooleanParameter("Tracker Password Web HTTPS Only");
		
		if ( web_password_enabled || tracker_password_enabled ){
			
			password_user	= COConfigurationManager.getStringParameter("Tracker Username", "");
			password_pw		= COConfigurationManager.getByteParameter("Tracker Password", new byte[0]);
		}
		
		compact_enabled = COConfigurationManager.getBooleanParameter("Tracker Compact Enable" );
		
		key_enabled = COConfigurationManager.getBooleanParameter("Tracker Key Enable Server");
	
public voidremoveAuthenticationListener(TRTrackerServerAuthenticationListener l)

		auth_listeners.remove(l);
	
public voidremoveLink(java.lang.String link, TRTrackerServerTorrent target)

		try{
			class_mon.enter();
			
			link_map.remove( link );
			
		}finally{
			
			class_mon.exit();
		}	
	
public voidremoveListener(TRTrackerServerListener l)

		try{
			this_mon.enter();
		
			listeners.removeElement(l);
			
		}finally{
			
			this_mon.exit();
		}
	
public voidremoveRequestListener(TRTrackerServerRequestListener l)

		request_listeners.removeElement(l);
	
public voidsetBiasedPeers(java.util.Set peers)

		if ( biased_peers != null && peers.equals( biased_peers )){
			
			return;
		}
		
		String	str = "";
		
		Iterator	it = peers.iterator();
		
		while( it.hasNext()){
			
			str += " " + it.next();
		}
		
		System.out.println( "biased peers: " + str );
		
		try{
			class_mon.enter();
		
			biased_peers = new HashSet( peers );
			
			Iterator	tit = torrent_map.values().iterator();
			
			while(tit.hasNext()){
							
				TRTrackerServerTorrentImpl	this_torrent = (TRTrackerServerTorrentImpl)tit.next();
				
				this_torrent.updateBiasedPeers( biased_peers );
			}
			
		}finally{
			
			class_mon.exit();
		}
	
public voidsetReady()

		is_ready	= true;
	
public static booleansupportsExtensions()

		return( support_experimental_extensions );
	
protected voidtimerLoop()

		long	time_to_go = TIMEOUT_CHECK;
		
		while( !destroyed ){
			
			try{
				Thread.sleep( RETRY_MINIMUM_MILLIS );
				
				time_to_go -= RETRY_MINIMUM_MILLIS;
				
				// recalc tracker interval every minute
				
				current_min_poll_interval 	= COConfigurationManager.getIntParameter("Tracker Poll Interval Min", DEFAULT_MIN_RETRY_DELAY );
				
				if ( current_min_poll_interval < RETRY_MINIMUM_SECS ){
					
					current_min_poll_interval = RETRY_MINIMUM_SECS;
				}
				
				int	min		= current_min_poll_interval;
				int	max 	= COConfigurationManager.getIntParameter("Tracker Poll Interval Max", DEFAULT_MAX_RETRY_DELAY );
				int	inc_by 	= COConfigurationManager.getIntParameter("Tracker Poll Inc By", DEFAULT_INC_BY );
				int	inc_per = COConfigurationManager.getIntParameter("Tracker Poll Inc Per", DEFAULT_INC_PER );
				
				int	scrape_percentage = COConfigurationManager.getIntParameter("Tracker Scrape Retry Percentage", DEFAULT_SCRAPE_RETRY_PERCENTAGE );
				
				int	retry = min;
				
				int	clients = 0;
				
				try{
					class_mon.enter();
					
					Iterator	it = torrent_map.values().iterator();
					
					while(it.hasNext()){
												
						TRTrackerServerTorrentImpl	t = (TRTrackerServerTorrentImpl)it.next();
						
						clients += t.getPeerCount();
					}
				}finally{
					
					class_mon.exit();
				}
				
				if ( inc_by > 0 && inc_per > 0 ){
					
					retry += inc_by * (clients/inc_per);
				}
				
				if ( max > 0 && retry > max ){
					
					retry = max;
				}
				
				if ( retry < RETRY_MINIMUM_SECS ){
					
					retry = RETRY_MINIMUM_SECS;
				}
				
				current_announce_retry_interval = retry;
				
				current_scrape_retry_interval	= (current_announce_retry_interval*scrape_percentage)/100;
				
				current_total_clients	= clients;
				
				// timeout dead clients
				
				if ( time_to_go <= 0 ){
					
					time_to_go = TIMEOUT_CHECK;
					
					try{
						class_mon.enter();
						
						Iterator	it = torrent_map.values().iterator();
						
						while(it.hasNext()){
														
							TRTrackerServerTorrentImpl	t = (TRTrackerServerTorrentImpl)it.next();
							
							t.checkTimeouts();
						}
					}finally{
						
						class_mon.exit();
					}
				}
				
			}catch( InterruptedException e ){
				
				Debug.printStackTrace( e );
			}
			
		}
	
public voidupdateStats(int request_type, TRTrackerServerTorrentImpl torrent, int bytes_in, int bytes_out)

		try{
			class_mon.enter();
		
			stats.update( request_type, bytes_in, bytes_out );
			
			if ( torrent != null ){
				
				torrent.updateXferStats( bytes_in, bytes_out );
				
			}else{
				
				int	num = torrent_map.size();
				
				if ( num > 0 ){
				
						// full scrape or error - spread the reported bytes across the torrents
				
					int	ave_in	= bytes_in/num;
					int	ave_out	= bytes_out/num;
					
					int	rem_in 	= bytes_in-(ave_in*num);
					int rem_out	= bytes_out-(ave_out*num);
					
					Iterator	it = torrent_map.values().iterator();
				
					while(it.hasNext()){
									
						TRTrackerServerTorrentImpl	this_torrent = (TRTrackerServerTorrentImpl)it.next();
						
						if ( it.hasNext()){
							
							this_torrent.updateXferStats( ave_in, ave_out );
							
						}else{
							
							this_torrent.updateXferStats( ave_in+rem_in, ave_out+rem_out );
							
						}
					}
				}
			}
		}finally{
			
			class_mon.exit();
		}