FileDocCategorySizeDatePackage
TRHostImpl.javaAPI DocAzureus 3.0.3.429090Wed Sep 12 23:18:06 BST 2007org.gudy.azureus2.core3.tracker.host.impl

TRHostImpl

public class TRHostImpl extends Object implements TRTrackerServerListener, TRTrackerServerFactoryListener, TRTrackerServerRequestListener, TRHost, TRTrackerServerAuthenticationListener, TRTrackerAnnouncerFactoryListener
author
parg

Fields Summary
private static final LogIDs
LOGID
private static final int
URL_DEFAULT_PORT
private static final int
URL_DEFAULT_PORT_SSL
public static final int
STATS_PERIOD_SECS
private static final int
TICK_PERIOD_SECS
private static final int
TICKS_PER_STATS_PERIOD
private static TRHostImpl
singleton
private static AEMonitor
class_mon
private TRHostConfigImpl
config
private Hashtable
server_map
private List
host_torrents
private Map
host_torrent_hash_map
private Map
host_torrent_map
private Map
tracker_client_map
private static final int
LDT_TORRENT_ADDED
private static final int
LDT_TORRENT_REMOVED
private static final int
LDT_TORRENT_CHANGED
private ListenerManager
listeners
private static boolean
host_add_announce_urls
private List
auth_listeners
private boolean
server_factory_listener_added
protected AEMonitor
this_mon
private volatile boolean
closed
Constructors Summary
protected TRHostImpl()

	
			// we need to synchronize this so that the async (possible) establishment of
			// a server within the stats loop (to deal with public trackers with no locally
			// hosted torrents) doesn't get ahead of the reading of persisted torrents
			// If we allow the server to start early then it can potentially receive an
			// announce/scrape and result in the creation of an "external" torrent when
			// it should really be using an existing torrent 
			 
		try{
			this_mon.enter();
					
			config = new TRHostConfigImpl(this);	
			
			TRTrackerAnnouncerFactory.addListener( this );
			
			Thread t = new AEThread("TRHost::stats.loop")
						{
							private int	tick_count = 0;
							
							private Set	failed_ports = new HashSet();
							
							public void
							runSupport()
							{
								while(true){
									
									try{	
										
										URL[][]	url_sets = TRTrackerUtils.getAnnounceURLs();
										
										for (int i=0;i<url_sets.length;i++){
											
											URL[]	urls = url_sets[i];
											
											for (int j=0;j<urls.length;j++){
												
												URL	url = urls[j];
														
												int port = url.getPort();
												
												if ( port == -1 ){
													
													port = url.getDefaultPort();
												}
												
												String	protocol = url.getProtocol().toLowerCase();
												
												try{
													if ( protocol.equals( "http" )){
												
														startServer( TRTrackerServerFactory.PR_TCP, port, false );
													
													}else if ( protocol.equals( "udp" )){
														
														startServer( TRTrackerServerFactory.PR_UDP, port, false );

													}else if ( protocol.equals( "https" )){
														
														startServer( TRTrackerServerFactory.PR_TCP, port, true );

													}else{
														
														Debug.out( "Unknown protocol '" + protocol + "'" );
													}
													
												}catch( Throwable e ){
											
													Integer port_i = new Integer(port);
													
													if ( !failed_ports.contains(port_i)){
														
														failed_ports.add( port_i );
														
														Logger.log(
																new LogEvent(LOGID,
																"Tracker Host: failed to start server", e));
													}
												}
											}
										}
										
										Thread.sleep( TICK_PERIOD_SECS*1000 );
										
										if ( closed ){
											
											break;
										}
										
										if ( tick_count % TICKS_PER_STATS_PERIOD == 0 ){
											
											try{
												this_mon.enter();
												
												for (int i=0;i<host_torrents.size();i++){
					
													TRHostTorrent	ht = (TRHostTorrent)host_torrents.get(i);
													
													if ( ht instanceof TRHostTorrentHostImpl ){
																						
														((TRHostTorrentHostImpl)ht).updateStats();
														
													}else{
														
														((TRHostTorrentPublishImpl)ht).updateStats();
														
													}
												}
											}finally{
												
												this_mon.exit();
											}
											
											config.saveConfig( true );
											
										}else{
											
											config.saveConfig( false );
										}
										
									}catch( InterruptedException e ){
										
										Debug.printStackTrace( e );
										
										break;
									}finally{
										
										tick_count++;
									}
								}
							}
						};
			
			t.setDaemon(true);
			
				// try to ensure that the tracker stats are collected reasonably
				// regularly
			
			t.setPriority( Thread.MAX_PRIORITY -1);
			
			t.start();
			
		}finally{
			
			this_mon.exit();
		}
	
Methods Summary
public voidaddAuthenticationListener(TRHostAuthenticationListener l)

	
		try{
			this_mon.enter();
		
			auth_listeners.add(l);
			
			if ( auth_listeners.size() == 1 ){
				
				Iterator it = server_map.values().iterator();
				
				while( it.hasNext()){
					
					((TRTrackerServer)it.next()).addAuthenticationListener( this );
				}			
			}
		}finally{
			
			this_mon.exit();
		}
	
protected voidaddExternalTorrent(byte[] hash, int state, long date_added)

		try{
			this_mon.enter();
		
			if ( lookupHostTorrentViaHash( hash ) != null ){
				
				return;
			}
			
			String 	tracker_ip 		= COConfigurationManager.getStringParameter("Tracker IP", "127.0.0.1");
							
				// external torrents don't care whether ssl or not so just assume non-ssl for simplicity 
				
			int port = COConfigurationManager.getIntParameter("Tracker Port", TRHost.DEFAULT_PORT );
	
			try{
				TOTorrent	external_torrent = new TRHostExternalTorrent(hash, new URL( "http://" + UrlUtils.convertIPV6Host(tracker_ip) + ":" + port + "/announce"));
			
				addTorrent( external_torrent, state, true, false, date_added );	
				
			}catch( Throwable e ){
				
				Debug.printStackTrace( e );
			}
			
		}finally{
			
			this_mon.exit();
		}
	
public voidaddListener(TRHostListener l)

		try{
			this_mon.enter();
		
			listeners.addListener( l );
			
			for (int i=0;i<host_torrents.size();i++){
				
				listeners.dispatch( l, LDT_TORRENT_ADDED, host_torrents.get(i));
			}
		}finally{
			
			this_mon.exit();
		}
	
protected TRHostTorrentaddTorrent(TOTorrent torrent, int state, boolean persistent, boolean passive, long date_added)

		try{
			this_mon.enter();
		
				// non-persistent additions should know what they're doing regarding
				// announce URL
			
			if ( persistent && state != TRHostTorrent.TS_PUBLISHED ){

				if ( host_add_announce_urls ){
					
					addTrackerAnnounce( torrent );
				}
			}
			
			TRHostTorrent	ht = lookupHostTorrent( torrent );
			
			if ( ht != null ){
						
				// check that this isn't the explicit publish/host of a torrent already there
				// as an external torrent. If so then just replace the torrent
				
				try{
				
					ht = lookupHostTorrentViaHash( torrent.getHash());
				
					if ( ht instanceof TRHostTorrentHostImpl ){
						
						TRHostTorrentHostImpl hti = (TRHostTorrentHostImpl)ht;
						
						if ( hti.getTorrent() != torrent ){
							
							hti.setTorrent( torrent );	
						
							if ( persistent && !hti.isPersistent()){
								
								hti.setPersistent( true );
							}
							
							if ( passive && !hti.isPassive()){
								
								hti.setPassive( true );
							}
							
							if ( state != TRHostTorrent.TS_PUBLISHED ){
					
								startHosting( hti );
					
								if ( state == TRHostTorrent.TS_STARTED ){
								
									hti.start();
								}
							}	
							
							listeners.dispatch( LDT_TORRENT_CHANGED, ht );
						}
					}
				}catch( TOTorrentException e ){
					
					Debug.printStackTrace( e );	
				}
				
				return( ht );
			}
			
			int		port;
			boolean	ssl;
			int		protocol	= TRTrackerServerFactory.PR_TCP;
			
			if ( state == TRHostTorrent.TS_PUBLISHED ){
			
				port = COConfigurationManager.getIntParameter("Tracker Port", TRHost.DEFAULT_PORT );
				
				ssl	= false;		
			}else{
			
				URL	announce_url = torrent.getAnnounceURL();
				
				String	protocol_str = announce_url.getProtocol();
				
				ssl = protocol_str.equalsIgnoreCase("https");
				
				if ( protocol_str.equalsIgnoreCase("udp")){
					
					protocol = TRTrackerServerFactory.PR_UDP;
					
				}else if ( TorrentUtils.isDecentralised( torrent )){
					
					protocol = TRTrackerServerFactory.PR_DHT;
				}
				
				boolean force_external = COConfigurationManager.getBooleanParameter("Tracker Port Force External");
				
				port = announce_url.getPort();
				
				if ( force_external ){
					
					String 	tracker_ip 		= COConfigurationManager.getStringParameter("Tracker IP", "");
		
					if ( 	tracker_ip.length() > 0 &&
							!announce_url.getHost().equalsIgnoreCase( tracker_ip )){
							
						if ( ssl ){
			
							port = COConfigurationManager.getIntParameter("Tracker Port SSL", TRHost.DEFAULT_PORT_SSL );
							
						}else{
							
							port = COConfigurationManager.getIntParameter("Tracker Port", TRHost.DEFAULT_PORT );
							
						}
					}
				}
				
				if ( port == -1 ){
					
					port = ssl?URL_DEFAULT_PORT_SSL:URL_DEFAULT_PORT;
				}
			}
			
			TRTrackerServer server = startServer( protocol, port, ssl );
			
			TRHostTorrent host_torrent;
		
			if ( state == TRHostTorrent.TS_PUBLISHED ){
	
				TRHostTorrentPublishImpl new_torrent = new TRHostTorrentPublishImpl( this, torrent, date_added );
	
				new_torrent.setPersistent( persistent );
				
				host_torrent	= new_torrent;
			}else{
			
				TRHostTorrentHostImpl	new_torrent = new TRHostTorrentHostImpl( this, server, torrent, port, date_added );
				
				new_torrent.setPersistent( persistent );
				
				new_torrent.setPassive( passive );

				host_torrent	= new_torrent;
			}
						
			host_torrents.add( host_torrent );
			
			try{
				host_torrent_hash_map.put( new HashWrapper( torrent.getHash()), host_torrent );
				
			}catch( TOTorrentException e ){
				
				Debug.printStackTrace( e );
			}
					
			host_torrent_map.put( torrent, host_torrent );
			
			if ( state != TRHostTorrent.TS_PUBLISHED ){
			
				startHosting((TRHostTorrentHostImpl)host_torrent );
		
				if ( state == TRHostTorrent.TS_STARTED ){
						
					host_torrent.start();
				}
				
					// if not persistent, see if we can recover the stats
				
				if ( !persistent ){
					
					config.recoverStats( (TRHostTorrentHostImpl)host_torrent );
				}
			}
	
			listeners.dispatch( LDT_TORRENT_ADDED, host_torrent );
			
			config.saveRequired();
			
			return( host_torrent );
			
		}finally{
			
			this_mon.exit();
		}
	
protected voidaddTrackerAnnounce(TOTorrent torrent)

		if ( TorrentUtils.isDecentralised( torrent )){
	
			return;
		}
		
			// ensure that the tracker's announce details are in the torrent
		
		URL[][]	url_sets = TRTrackerUtils.getAnnounceURLs();

		if ( url_sets.length == 0 ){
			
				// fall back to decentralised, no tracker defined
			
			TorrentUtils.setDecentralised( torrent );
			
		}else{			
		
			URL[]	primary_urls = url_sets[0];
			
				// backwards so that they end up in right order
			
			for (int i=primary_urls.length-1;i>=0;i--){
			
				String	url_str = primary_urls[i].toString();
				
				if ( TorrentUtils.announceGroupsContainsURL( torrent, url_str )){
			
					TorrentUtils.announceGroupsSetFirst( torrent, url_str );
				
				}else{
				
					TorrentUtils.announceGroupsInsertFirst( torrent, url_str );
				}
			}
		}
	
public booleanauthenticate(java.net.URL resource, java.lang.String user, java.lang.String password)

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

		for (int i=0;i<auth_listeners.size();i++){
			
			try{
				byte[] res = ((TRHostAuthenticationListener)auth_listeners.get(i)).authenticate( resource, user );
				
				if ( res != null ){
					
					return( res );
				}
			}catch( Throwable e ){
				
				Debug.printStackTrace( e );
			}
		}
		
		return( null );
	
public voidclientCreated(TRTrackerAnnouncer client)

		try{
			this_mon.enter();
		
			tracker_client_map.put( client.getTorrent(), client );
		
			startHosting( client );
			
		}finally{
			
			this_mon.exit();
		}
	
public voidclientDestroyed(TRTrackerAnnouncer client)

		try{
			this_mon.enter();
		
			tracker_client_map.remove( client.getTorrent());
		
			stopHosting( client );
			
		}finally{
			
			this_mon.exit();
		}
	
public voidclose()

		closed	= true;
		
		config.saveConfig( true );
	
public static TRHostcreate()

	
	  
	
	
		try{
			class_mon.enter();
		
			if ( singleton == null ){
				
				singleton = new TRHostImpl();
			}
			
			return( singleton );
			
		}finally{
			
			class_mon.exit();
		}
	
public booleandenied(byte[] hash, boolean permitted)

		return( true );
	
public TRHostTorrentgetHostTorrent(TOTorrent torrent)

		return( lookupHostTorrent( torrent ));
	
public java.lang.StringgetName()

		return( TRTrackerServer.DEFAULT_NAME );
	
public TRHostTorrent[]getTorrents()

		try{
			this_mon.enter();
		
			TRHostTorrent[]	res = new TRHostTorrent[host_torrents.size()];
		
			host_torrents.toArray( res );
		
			return( res );
			
		}finally{
			
			this_mon.exit();
		}
	
protected TRTrackerAnnouncergetTrackerClient(TRHostTorrent host_torrent)

		try{
			this_mon.enter();
		
			return((TRTrackerAnnouncer)tracker_client_map.get( host_torrent.getTorrent()));
			
		}finally{
			
			this_mon.exit();
		}
	
public booleanhandleExternalRequest(java.net.InetSocketAddress client_address, java.lang.String user, java.lang.String url, java.net.URL absolute_url, java.lang.String header, java.io.InputStream is, java.io.OutputStream os)

		List	listeners_copy = listeners.getListenersCopy();
		
		for (int i=0;i<listeners_copy.size();i++){

			TRHostListener	listener = (TRHostListener)listeners_copy.get(i);
			
			if ( listener.handleExternalRequest( client_address, user, url, absolute_url, header, is, os )){
				
				return( true );
			}
		}
		
		return( false );
	
public TRHostTorrenthostTorrent(TOTorrent torrent, boolean persistent, boolean passive)

		return( addTorrent( torrent, TRHostTorrent.TS_STARTED, persistent, passive, SystemTime.getCurrentTime() ));
	
protected voidhostTorrentStateChange(TRHostTorrent host_torrent)

		try{
			this_mon.enter();
		
			TOTorrent	torrent = host_torrent.getTorrent();
			
			TRTrackerAnnouncer tc = (TRTrackerAnnouncer)tracker_client_map.get( torrent );
			
			if ( tc != null ){
				
				tc.refreshListeners();
			}
			
			// config will get saved soon anyway (periodic or on closedown) - perf issues
			// here with multiple torrent removal if we save each time
			// config.saveConfig();
			
		}finally{
			
			this_mon.exit();
		}
	
public voidinitialise(TRHostTorrentFinder finder)

		config.loadConfig( finder );
	
protected TRHostTorrentlookupHostTorrent(TOTorrent torrent)

	  if (torrent == null)
	    return null;

		try{
			return((TRHostTorrent)host_torrent_hash_map.get( torrent.getHashWrapper()));
			
		}catch( TOTorrentException e ){
			
			Debug.printStackTrace( e );
		}
		
		return( null );
	
protected TRHostTorrentlookupHostTorrentViaHash(byte[] hash)

		return((TRHostTorrent)host_torrent_hash_map.get(new HashWrapper(hash)));
	
public booleanpermitted(java.lang.String originator, byte[] hash, boolean explicit)

		try{
			this_mon.enter();
		
			TRHostTorrent ht = lookupHostTorrentViaHash( hash );
			
			if ( ht != null ){
			
				if ( !explicit ){
					
					if ( ht.getStatus() != TRHostTorrent.TS_STARTED ){
						
						return( false );
					}
				}
				
				return( true );
			}
			
			addExternalTorrent( hash, TRHostTorrent.TS_STARTED, SystemTime.getCurrentTime());
			
			return( true );
			
		}finally{
			
			this_mon.exit();
		}
	
public voidpostProcess(TRTrackerServerRequest request)

		if ( 	request.getType() 	== TRTrackerServerRequest.RT_ANNOUNCE  ||
				request.getType() 	== TRTrackerServerRequest.RT_SCRAPE ){
			
			TRTrackerServerTorrent ts_torrent = request.getTorrent();
		
				// can be null for multi-hash scrapes... should fix this sometime I guess
			
			if ( ts_torrent != null ){
				
				HashWrapper	hash_wrapper = ts_torrent.getHash();
				
				TRHostTorrent h_torrent = lookupHostTorrentViaHash( hash_wrapper.getHash());
				
				if ( h_torrent != null ){
					
					TRHostTorrentRequest	req = new TRHostTorrentRequestImpl( h_torrent, new TRHostPeerHostImpl(request.getPeer()), request );
				
					try{
						if ( h_torrent instanceof TRHostTorrentHostImpl ){
						
							((TRHostTorrentHostImpl)h_torrent).postProcess( req );
						}else{
							
							((TRHostTorrentPublishImpl)h_torrent).postProcess( req );	
						}
					}catch( TRHostException e ){
						
						throw( new TRTrackerServerException( "Post process fails", e ));
					}
				}
			}
		}
	
public voidpreProcess(TRTrackerServerRequest request)

		if ( 	request.getType() 	== TRTrackerServerRequest.RT_ANNOUNCE  ||
				request.getType() 	== TRTrackerServerRequest.RT_SCRAPE ){
			
			TRTrackerServerTorrent ts_torrent = request.getTorrent();
		
			HashWrapper	hash_wrapper = ts_torrent.getHash();
			
			TRHostTorrent h_torrent = lookupHostTorrentViaHash( hash_wrapper.getHash());
			
			if ( h_torrent != null ){
				
				TRHostTorrentRequest	req = new TRHostTorrentRequestImpl( h_torrent, new TRHostPeerHostImpl(request.getPeer()), request );
			
				try{
					if ( h_torrent instanceof TRHostTorrentHostImpl ){
					
						((TRHostTorrentHostImpl)h_torrent).preProcess( req );
					}else{
						
						((TRHostTorrentPublishImpl)h_torrent).preProcess( req );	
					}
				}catch( TRHostException e ){
					
					throw( new TRTrackerServerException( e.getMessage(), e ));
					
				}catch( Throwable e ){
					
					throw( new TRTrackerServerException( "Pre-process fails", e ));
				}
			}
		}	
	
public TRHostTorrentpublishTorrent(TOTorrent torrent)

		return( addTorrent( torrent, TRHostTorrent.TS_PUBLISHED, true, false, SystemTime.getCurrentTime()));
	
protected voidremove(TRHostTorrent host_torrent)

		try{
			this_mon.enter();
		
			if ( !host_torrents.contains( host_torrent )){
				
				return;
			}
			
			host_torrents.remove( host_torrent );
			
			TOTorrent	torrent = host_torrent.getTorrent();
			
			try{
				host_torrent_hash_map.remove(new HashWrapper(torrent.getHash()));
				
			}catch( TOTorrentException e ){
				
				Debug.printStackTrace( e );
			}
			
			host_torrent_map.remove( torrent );
			
			if ( host_torrent instanceof TRHostTorrentHostImpl ){
				
				stopHosting((TRHostTorrentHostImpl)host_torrent );
			}
			
			listeners.dispatch( LDT_TORRENT_REMOVED, host_torrent );
			
				// this'll get saved sometime soon anyway - performance problems
				// here when removing multiple torrents from a large set (e.g. 1000)
			
			// config.saveConfig();	
			
		}finally{
			
			this_mon.exit();
		}
	
public voidremoveAuthenticationListener(TRHostAuthenticationListener l)

	
		try{
			this_mon.enter();
		
			auth_listeners.remove(l);
			
			if ( auth_listeners.size() == 0 ){
				
				Iterator it = server_map.values().iterator();
				
				while( it.hasNext()){
					
					((TRTrackerServer)it.next()).removeAuthenticationListener( this );
				}
			}
		}finally{
			
			this_mon.exit();
		}
	
public voidremoveListener(TRHostListener l)

		listeners.removeListener( l );
	
public voidserverCreated(TRTrackerServer server)

		server.addRequestListener(this);
	
public voidserverDestroyed(TRTrackerServer server)

		server.removeRequestListener(this);
	
protected voidstartHosting(TRHostTorrentHostImpl host_torrent)

		TOTorrent	torrent = host_torrent.getTorrent();
		
		TRTrackerAnnouncer tc = (TRTrackerAnnouncer)tracker_client_map.get( torrent );
		
		if ( tc != null ){
			
			startHosting( host_torrent, tc );
		}
	
protected voidstartHosting(TRTrackerAnnouncer tracker_client)

		TRHostTorrent	host_torrent = (TRHostTorrent)host_torrent_map.get( tracker_client.getTorrent());
			
		if ( host_torrent instanceof TRHostTorrentHostImpl ){
			
			startHosting( (TRHostTorrentHostImpl)host_torrent, tracker_client );
		}
	
protected voidstartHosting(TRHostTorrentHostImpl host_torrent, TRTrackerAnnouncer tracker_client)

		final TOTorrent	torrent = host_torrent.getTorrent();	

			// set the ip override so that we announce ourselves to other peers via the 
			// real external address, not the local one used to connect to the tracker 
			
		URL	announce = torrent.getAnnounceURL();
		
		if ( host_add_announce_urls ){
		
			tracker_client.setIPOverride( announce.getHost());
			
		}else{
			
				// prolly a backup tracker, we only want to override the IP if we're hosting it
			
			if ( TRTrackerUtils.isHosting( announce )){
				
				tracker_client.setIPOverride( announce.getHost());

			}
		}
		
			// hook into the client so that when the announce succeeds after the refresh below
			// we can force a rescrape to pick up the new status 
		
		TRTrackerAnnouncerListener	listener = 
			new TRTrackerAnnouncerListener()
			{
				public void
				receivedTrackerResponse(
					TRTrackerAnnouncerResponse	response	)
				{	
					try{
						TRTrackerScraperFactory.getSingleton().scrape( torrent, true );
					
					}finally{
						
						tracker_client.removeListener( this );
					}
				}

				public void
				urlChanged(
					TRTrackerAnnouncer	announcer,
					URL					old_url,
					URL					new_url,
					boolean				explicit )
				{	
				}
					
				public void
				urlRefresh()
				{
				}
			};
		
		tracker_client.addListener(listener);

		tracker_client.refreshListeners();
	
protected TRTrackerServerstartServer(int protocol, int port, boolean ssl)

		try{
			this_mon.enter();
		
			String	key = ""+protocol+ ":" + port;
			
			TRTrackerServer	server = (TRTrackerServer)server_map.get( key );
				
			if ( server == null ){
					
				try{
					
					if ( ssl ){
						
						server = TRTrackerServerFactory.createSSL( "tracker", protocol, port, true, true );
					
					}else{
					
						server = TRTrackerServerFactory.create( "tracker", protocol, port, true, true );
					}
						
					server_map.put( key, server );
						
					if ( auth_listeners.size() > 0 ){
						
						server.addAuthenticationListener( this );
					}
					
					server.addListener( this );
							
				}catch( TRTrackerServerException e ){
								
					throw( new TRHostException( "startServer failed", e ));
				}
			}
			
			return( server );
			
		}finally{
			
			this_mon.exit();
		}
	
protected voidstartTorrent(TRHostTorrentHostImpl torrent)

		try{
			this_mon.enter();
			
			torrent.startSupport();
			
		}finally{
			
			this_mon.exit();
		}
	
protected voidstopHosting(TRHostTorrentHostImpl host_torrent)

		TOTorrent	torrent = host_torrent.getTorrent();
		
		TRTrackerAnnouncer tc = (TRTrackerAnnouncer)tracker_client_map.get( torrent );
		
		if ( tc != null ){
			
			stopHosting( host_torrent, tc );
		}
	
protected voidstopHosting(TRTrackerAnnouncer tracker_client)

		TRHostTorrent	host_torrent = (TRHostTorrent)host_torrent_map.get( tracker_client.getTorrent());
			
		if ( host_torrent instanceof TRHostTorrentHostImpl ){
			
				// we only "connect" the announcer and the hosted torrent if it isn't passive. This allows
				// us to make a torrent passive without losing the tracker stats by
				// 1) making it passive
				// 2) removing the Download
			
			//if ( !host_torrent.isPassive()){
			
				stopHosting( (TRHostTorrentHostImpl)host_torrent, tracker_client );
			//}
		}
	
protected voidstopHosting(TRHostTorrentHostImpl host_torrent, TRTrackerAnnouncer tracker_client)

				
			// unfortunately a lot of the "stop" operations that occur when a tracker client
			// connection is closed happen async. In particular the "stopped" message to the
			// tracker. Hence, if we switch the URL back here the "stopped" doesn't get
			// through.
			
		// for the moment stick a delay in to allow any async stuff to complete
		
		Thread thread = new AEThread("StopHosting")
			{
				public void
				runSupport()
				{
					try{
						Thread.sleep(2500);
						
					}catch( InterruptedException e ){
						
					}
					
					try{
						this_mon.enter();
						
							// got to look up the host torrent again as may have been
							// removed and re-added
						
						TRHostTorrent	ht = lookupHostTorrent( host_torrent.getTorrent());
						
							// check it's still in stopped state and hasn't been restarted
							
						if ( ht == null || 
								( 	ht == host_torrent &&
								 	ht.getStatus() == TRHostTorrent.TS_STOPPED )){
					
							tracker_client.clearIPOverride();
						}
					}finally{
						
						this_mon.exit();
					}
				}
			};
			
		thread.setDaemon(true);
		
		thread.start();
	
protected voidstopTorrent(TRHostTorrentHostImpl torrent)

		try{
			this_mon.enter();
			
			torrent.stopSupport();
			
		}finally{
			
			this_mon.exit();
		}
	
protected voidtorrentListenerRegistered()

		try{
			this_mon.enter();
		
			if ( !server_factory_listener_added ){
				
				server_factory_listener_added	= true;
				
				TRTrackerServerFactory.addListener( this );
			}
		}finally{
			
			this_mon.exit();
		}