FileDocCategorySizeDatePackage
DHTRouterNodeImpl.javaAPI DocAzureus 3.0.3.413352Mon Apr 24 14:11:08 BST 2006com.aelitis.azureus.core.dht.router.impl

DHTRouterNodeImpl

public class DHTRouterNodeImpl extends Object
author
parg

Fields Summary
private DHTRouterImpl
router
private int
depth
private boolean
contains_router_node_id
private List
buckets
private List
replacements
private DHTRouterNodeImpl
left
private DHTRouterNodeImpl
right
private long
last_lookup_time
Constructors Summary
protected DHTRouterNodeImpl(DHTRouterImpl _router, int _depth, boolean _contains_router_node_id, List _buckets)

		router					= _router;
		depth					= _depth;
		contains_router_node_id	= _contains_router_node_id;
		buckets					= _buckets;
	
Methods Summary
protected voidaddNode(DHTRouterContactImpl node)

		// MGP: notify that node added to bucket
		node.setBucketEntry();
		router.notifyAdded(node);
		
		buckets.add( node );
		
		requestNodeAdd( node, false );
	
protected com.aelitis.azureus.core.dht.router.DHTRouterContactaddReplacement(DHTRouterContactImpl replacement, int max_rep_per_node)

		if ( max_rep_per_node == 0 ){
			
			return( null );
		}
		
			// we ping the oldest bucket entry only if we "improve" matters in the replacement 
		
		boolean	try_ping	= false;
		
		if( replacements == null ){
			
			try_ping	= true;
			
			replacements = new ArrayList();
			
		}else{
				
			if ( replacements.size() == max_rep_per_node ){
				
					// if this replacement is known to be alive, replace any existing
					// replacements that haven't been known to be alive
				
				if ( replacement.hasBeenAlive() ){
					
					for (int i=0;i<replacements.size();i++){
						
						DHTRouterContactImpl	r = (DHTRouterContactImpl)replacements.get(i);
				
						if ( !r.hasBeenAlive()){
							
							try_ping	= true;
							
							// MGP: kicking out a replacement that was never alive
							router.notifyRemoved(r);
							
							replacements.remove(i);
							
							break;
						}
					}
					
						// no unknown existing replacements but this is "newer" than the existing
						// ones so replace the oldest one
					
					if ( replacements.size() == max_rep_per_node ){
						
						DHTRouterContactImpl removed = (DHTRouterContactImpl) replacements.remove(0);
						
						// MGP: kicking out the oldest replacement
						router.notifyRemoved(removed);
					}
				}else{
						
						// replace old unknown ones with newer unknown ones
					
					for (int i=0;i<replacements.size();i++){
						
						DHTRouterContactImpl	r = (DHTRouterContactImpl)replacements.get(i);
				
						if ( !r.hasBeenAlive()){
							
							// MGP: kicking out an older replacement that was never alive
							router.notifyRemoved(r);
							
							replacements.remove(i);
							
							break;
						}
					}
				}
			}else{
				
				try_ping	= true;
			}
		}
		
		if ( replacements.size() == max_rep_per_node ){
			
				// no room, drop the contact
			
			return( null );
		}
		
		// MGP: notify observers contact added as a replacement
		replacement.setReplacement();
		router.notifyAdded(replacement);
		
		replacements.add( replacement );
			
		if ( try_ping ){
			
			for (int i=0;i<buckets.size();i++){
				
				DHTRouterContactImpl	c = (DHTRouterContactImpl)buckets.get(i);
				
					// don't ping ourselves or someone already being pinged
				
				if ( !( router.isID(c.getID()) || c.getPingOutstanding())){
			
					c.setPingOutstanding( true );
					
					router.requestPing( c );
					
					break;
				}
			}
		}
		
		return( replacement );
	
protected voidalive(DHTRouterContactImpl contact)

		// DHTLog.log( DHTLog.getString( contact.getID()) + ": alive" );
			
		contact.setPingOutstanding( false );
		
		// record whether was alive
		boolean was_alive = contact.isAlive();
		
		if ( buckets.remove( contact )){
			
			contact.setAlive();
			
			if (!was_alive) {
				// MGP: notify observers that now alive
				router.notifyNowAlive(contact);
			}
			
			// MGP: simply reinserting, so do not notify observers that added to bucket
			buckets.add( contact );
			
		}else if ( replacements.remove( contact )){
					
			long	last_time = contact.getFirstFailOrLastAliveTime();
			
			contact.setAlive();
			
			if (!was_alive) {
				// MGP: notify observers that now alive
				router.notifyNowAlive(contact);
			}
			
				// this is a good time to probe the contacts as we know a 
				// replacement is alive and therefore in a position to replace a
				// dead bucket entry. Only do this if we haven't heard from this contact
				// recently
			
			if ( contact.getLastAliveTime() - last_time > 30000 ){
			
				for (int i=0;i<buckets.size();i++){
					
					DHTRouterContactImpl	c = (DHTRouterContactImpl)buckets.get(i);
					
						// don't ping ourselves or someone already being pinged
					
					if ( !( router.isID(c.getID()) || c.getPingOutstanding())){
				
						c.setPingOutstanding( true );
						
						router.requestPing( c );
						
						break;
					}
				}
			}
			
			// MGP: simply reinserting, so do not notify observers that added to replacments
			replacements.add( contact );		
		}
	
protected java.lang.StringcontactsToString(java.util.List contacts)

		String	res = "{";
		
		for (int i=0;i<contacts.size();i++){
			
			res += (i==0?"":", ") + ((DHTRouterContactImpl)contacts.get(i)).getString();
		}
		
		return( res + "}" );
	
protected booleancontainsRouterNodeID()

		return( contains_router_node_id );
	
protected voiddead(DHTRouterContactImpl contact, boolean force)

		// DHTLog.log( DHTLog.getString( contact.getID()) + ": dead" );
		
		contact.setPingOutstanding( false );
		
		// record whether was failing
		boolean was_failing = contact.isFailing();
		
		if ( contact.setFailed() || force ){
			
				// check the contact is still present
			
			if ( buckets.remove( contact )){
				
				if (!was_failing) {
					// MGP: first notify observers that now failing
					router.notifyNowFailing(contact);
				}
				
				// MGP: notify that removed from bucket
				router.notifyRemoved(contact);
								
				if ( replacements != null && replacements.size() > 0 ){
					
						// take most recent alive one and add to buckets
					
					boolean	replaced	= false;
					
					for (int i=replacements.size()-1;i>=0;i--){
						
						DHTRouterContactImpl	rep = (DHTRouterContactImpl)replacements.get(i);
						
						if ( rep.hasBeenAlive()){
							
							DHTLog.log( DHTLog.getString( contact.getID()) + ": using live replacement " + DHTLog.getString(rep.getID()));
							
							// MGP: notify that a replacement was promoted to the bucket
							rep.setBucketEntry();
							router.notifyLocationChanged(rep);

							replacements.remove( rep );
							
							buckets.add( rep );
							
							replaced	= true;
							
							requestNodeAdd( rep, false );
							
							break;
						}
					}
					
						// non alive - just take most recently added
					
					if ( !replaced ){
						
						DHTRouterContactImpl	rep = (DHTRouterContactImpl)replacements.remove( replacements.size() - 1 );
					
						DHTLog.log( DHTLog.getString( contact.getID()) + ": using unknown replacement " + DHTLog.getString(rep.getID()));

						// MGP: notify that a replacement was promoted to the bucket
						rep.setBucketEntry();
						router.notifyLocationChanged(rep);
						
						buckets.add( rep );
						
							// add-node logic will ping the node if its not known to
							// be alive
						
						requestNodeAdd( rep, false );
					}
				}
			}else{
				
				if (!was_failing) {
					// MGP: first notify observers that now failing
					router.notifyNowFailing(contact);
				}
				
				// MGP: notify that removed from replacement list
				router.notifyRemoved(contact);
				
				replacements.remove( contact );
			}
		}
	
protected java.util.ListgetBuckets()

		return( buckets );
	
protected intgetDepth()

		return( depth );
	
protected com.aelitis.azureus.core.dht.router.impl.DHTRouterNodeImplgetLeft()

		return( left );
	
protected java.util.ListgetReplacements()

		return( replacements );
	
protected com.aelitis.azureus.core.dht.router.impl.DHTRouterNodeImplgetRight()

		return( right );
	
protected longgetTimeSinceLastLookup()

		long	now = SystemTime.getCurrentTime();
		
		if ( now < last_lookup_time ){
			
				// clock changed, don't know so make as large as possible
			
			return( Long.MAX_VALUE );
		}
		
		return( now - last_lookup_time );
	
public voidprint(java.lang.String indent, java.lang.String prefix)

		if ( left == null ){
			
			router.log( 
					indent + prefix + 
					": buckets = " + buckets.size() + contactsToString( buckets) + 
					", replacements = " + (replacements==null?"null":( replacements.size() + contactsToString( replacements ))) + 
					(contains_router_node_id?" *":" ") +
					(this==router.getSmallestSubtree()?"SST":"") +
					" tsll=" + getTimeSinceLastLookup());
			
		}else{
			
			router.log( indent + prefix + ":" + (contains_router_node_id?" *":" ") +
							(this==router.getSmallestSubtree()?"SST":""));
			
			left.print( indent + "  ", prefix + "1"  );
						
			right.print( indent + "  ", prefix + "0" );
		}
	
protected voidrequestNodeAdd(DHTRouterContactImpl contact, boolean definite_change)

		// DOS problem here - if a node deliberately flicked between
		// instance IDs we'll get into an update frenzy. 

		long	now = SystemTime.getCurrentTime();
		
		if ( now - contact.getLastAddedTime() > 10000 ){

			contact.setLastAddedTime( now );
			
			router.requestNodeAdd( contact );
			
		}else{
			
				// only produce a warning if this is a definite change from one id to
				// another (as opposed to a change from "unknown" to another)
			
			if ( definite_change ){
				
				router.log( "requestNodeAdd for " + contact.getString() + " denied as too soon after previous ");
			}
		}
	
protected voidsetLastLookupTime()

		last_lookup_time = SystemTime.getCurrentTime();
	
protected voidsplit(com.aelitis.azureus.core.dht.router.impl.DHTRouterNodeImpl new_left, com.aelitis.azureus.core.dht.router.impl.DHTRouterNodeImpl new_right)

		buckets	= null;
		
		if ( replacements != null ){
			
			Debug.out( "DHTRouterNode: inconsistenct - splitting a node with replacements" );
		}
		
		left	= new_left;
		right	= new_right;
	
protected DHTRouterContactImplupdateExistingNode(byte[] node_id, com.aelitis.azureus.core.dht.router.DHTRouterContactAttachment attachment, boolean known_to_be_alive)

		for (int k=0;k<buckets.size();k++){
			
			DHTRouterContactImpl	contact = (DHTRouterContactImpl)buckets.get(k);
			
			if ( Arrays.equals(node_id, contact.getID())){

				if ( known_to_be_alive ){
					
					// MGP: will update observers of updated status in this method
					alive( contact );
				}
				
					// might be the same node but back after a restart. we need to
					// treat this differently as we need to kick off the "store"
					// events as required. 
		
				int	new_id	= attachment.getInstanceID();

					// if the new-id is zero this represents us hearing about a contact
					// indirectly (imported or returned as a query). In this case we 
					// don't use this information as an indication of the target's
					// instance identity because it isn't!
				
				if ( new_id != 0 ){
					
					int	old_id 	= contact.getAttachment().getInstanceID();
					
					if (  old_id != new_id ){
						
						DHTLog.log( "Instance ID changed for " + 
									DHTLog.getString( contact.getID())+ 
									": old = " + old_id + ", new = " + new_id );
											
						contact.setAttachment( attachment );
						
							// if the instance id was 0, this means that it was unknown
							// (e.g. contact imported). We still need to go ahead and treat 
							// as a new node 
						
						requestNodeAdd( contact, old_id != 0 );
					}
				}

				return( contact );
			}
		}
		
			// check replacements as well
			
		if ( replacements != null ){
			
			for (int k=0;k<replacements.size();k++){
				
				DHTRouterContactImpl	contact = (DHTRouterContactImpl)replacements.get(k);
				
				if ( Arrays.equals(node_id, contact.getID())){
	
					if ( known_to_be_alive ){
						
						// MGP: will update observers of updated status in this method
						alive( contact );
					}
	
					return( contact );
				}
			}
		}
		
		return( null );