Package com.aelitis.azureus.core.dht.router.impl

Source Code of com.aelitis.azureus.core.dht.router.impl.DHTRouterImpl

/*
* Created on 11-Jan-2005
* Created by Paul Gardner
* Copyright (C) 2004, 2005, 2006 Aelitis, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*
* AELITIS, SAS au capital de 46,603.30 euros
* 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
*
*/

package com.aelitis.azureus.core.dht.router.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.TreeSet;

import org.gudy.azureus2.core3.util.AEMonitor;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.SimpleTimer;
import org.gudy.azureus2.core3.util.SystemTime;
import org.gudy.azureus2.core3.util.TimerEvent;
import org.gudy.azureus2.core3.util.TimerEventPerformer;
import org.gudy.azureus2.core3.util.TimerEventPeriodic;

import com.aelitis.azureus.core.dht.DHTLogger;
import com.aelitis.azureus.core.dht.impl.DHTLog;
import com.aelitis.azureus.core.dht.router.DHTRouter;
import com.aelitis.azureus.core.dht.router.DHTRouterAdapter;
import com.aelitis.azureus.core.dht.router.DHTRouterContact;
import com.aelitis.azureus.core.dht.router.DHTRouterContactAttachment;
import com.aelitis.azureus.core.dht.router.DHTRouterObserver;
import com.aelitis.azureus.core.dht.router.DHTRouterStats;
import com.aelitis.azureus.core.util.CopyOnWriteList;
import com.aelitis.azureus.core.util.bloom.BloomFilter;
import com.aelitis.azureus.core.util.bloom.BloomFilterFactory;

/**
* @author parg
*
*/

public class
DHTRouterImpl
  implements DHTRouter
{
  private static final int  SMALLEST_SUBTREE_MAX_EXCESS  = 10*1024;
 
  private int    K;
  private int    B;
  private int    max_rep_per_node;
 
  private DHTLogger    logger;
 
  private int    smallest_subtree_max;
 
  private DHTRouterAdapter    adapter;
 
  private DHTRouterContactImpl  local_contact;
  private byte[]          router_node_id;
 
  private DHTRouterNodeImpl    root;
  private DHTRouterNodeImpl    smallest_subtree;
 
  private int            consecutive_dead;
 
  private static long        random_seed  = SystemTime.getCurrentTime();
  private Random          random;
 
  private List          outstanding_pings  = new ArrayList();
  private List          outstanding_adds  = new ArrayList();
 
  private DHTRouterStatsImpl    stats  = new DHTRouterStatsImpl( this );
 
  private AEMonitor  this_mon  = new AEMonitor( "DHTRouter" );

  private static AEMonitor  class_mon  = new AEMonitor( "DHTRouter:class" );
 
  private final CopyOnWriteList  observers = new CopyOnWriteList();

  private BloomFilter recent_contact_bloom =
    BloomFilterFactory.createRotating(
      BloomFilterFactory.createAddOnly(10*1024),
      2 );
 
  private TimerEventPeriodic  timer_event;
 
  private volatile int seed_in_ticks;
 
  private static final int  TICK_PERIOD     = 10*1000;
  private static final int  SEED_DELAY_PERIOD  = 60*1000;
  private static final int  SEED_DELAY_TICKS  = SEED_DELAY_PERIOD/TICK_PERIOD;
 
 
  public
  DHTRouterImpl(
    int                    _K,
    int                    _B,
    int                    _max_rep_per_node,
    byte[]                  _router_node_id,
    DHTRouterContactAttachment        _attachment,
    DHTLogger                _logger )
  {
    try{
        // only needed for in-process multi-router testing :P
     
      class_mon.enter();
     
      random = new Random( random_seed++ );
     
    }finally{
     
      class_mon.exit();
    }
       
    K          = _K;
    B          = _B;
    max_rep_per_node  = _max_rep_per_node;
    logger        = _logger;
   
   
    smallest_subtree_max  = 1;
   
    for (int i=0;i<B;i++){
     
      smallest_subtree_max  *= 2;
    }
   
    smallest_subtree_max  += SMALLEST_SUBTREE_MAX_EXCESS;
   
    router_node_id  = _router_node_id;
   
    List  buckets = new ArrayList();
   
    local_contact = new DHTRouterContactImpl( router_node_id, _attachment, true );
   
    buckets.add( local_contact );
   
    root  = new DHTRouterNodeImpl( this, 0, true, buckets );
   
    timer_event = SimpleTimer.addPeriodicEvent(
      "DHTRouter:pinger",
      TICK_PERIOD,
      new TimerEventPerformer()
      {
        public void
        perform(
          TimerEvent event )
        {
          pingeroonies();
         
          if ( seed_in_ticks > 0 ){
           
            seed_in_ticks--;
           
            if ( seed_in_ticks == 0 ){
             
              seedSupport();
            }
          }
        }
      });
  }
 
  protected void notifyAdded(DHTRouterContact contact) {
    for (Iterator i = observers.iterator(); i.hasNext(); ) {
      DHTRouterObserver rto = (DHTRouterObserver) i.next();
      try{
        rto.added(contact);
      }catch( Throwable e ){
        Debug.printStackTrace(e);
      }
    }
  }
 
  protected void notifyRemoved(DHTRouterContact contact) {
    for (Iterator i = observers.iterator(); i.hasNext(); ) {
      DHTRouterObserver rto = (DHTRouterObserver) i.next();
      try{
        rto.removed(contact);
      }catch( Throwable e ){
        Debug.printStackTrace(e);
      }
    }
  }
 
  protected void notifyLocationChanged(DHTRouterContact contact) {
    for (Iterator i = observers.iterator(); i.hasNext(); ) {
      DHTRouterObserver rto = (DHTRouterObserver) i.next();
      try{
        rto.locationChanged(contact);
      }catch( Throwable e ){
        Debug.printStackTrace(e);
      }
    }
  }
 
  protected void notifyNowAlive(DHTRouterContact contact) {
    for (Iterator i = observers.iterator(); i.hasNext(); ) {
      DHTRouterObserver rto = (DHTRouterObserver) i.next();
      try{
        rto.nowAlive(contact);
      }catch( Throwable e ){
        Debug.printStackTrace(e);
      }
    }
  }
 
  protected void notifyNowFailing(DHTRouterContact contact) {
    for (Iterator i = observers.iterator(); i.hasNext(); ) {
      DHTRouterObserver rto = (DHTRouterObserver) i.next();
      try{
        rto.nowFailing(contact);
      }catch( Throwable e ){
        Debug.printStackTrace(e);
      }
    }
  }
 
  protected void notifyDead() {
    for (Iterator i = observers.iterator(); i.hasNext(); ) {
      DHTRouterObserver rto = (DHTRouterObserver) i.next();
      try{
        rto.destroyed(this);
      }catch( Throwable e ){
        Debug.printStackTrace(e);
      }
    }
  }
 
  public boolean addObserver(DHTRouterObserver rto) {
    if ((rto != null) && !observers.contains(rto)) {
      observers.add(rto);
      return true;
    }
    return false;
  }
 
  public boolean containsObserver(DHTRouterObserver rto) {
    return ((rto != null) && observers.contains(rto));
  }
 
  public boolean removeObserver(DHTRouterObserver rto) {
    return ((rto != null) && observers.remove(rto));
  }
 
  public DHTRouterStats
  getStats()
  {
    return( stats );
  }
 
  public int
  getK()
  {
    return( K );
  }
 
 
  public byte[]
  getID()
  {
    return( router_node_id );
  }
 
  public boolean
  isID(
    byte[]  id )
  {
    return( Arrays.equals( id, router_node_id ));
  }
 
  public DHTRouterContact
  getLocalContact()
  {
    return( local_contact );
  }
 
  public void
  setAdapter(
    DHTRouterAdapter  _adapter )
  {
    adapter  = _adapter;
  }
 
  public void
  contactKnown(
    byte[]            node_id,
    DHTRouterContactAttachment  attachment )
  {
    if ( recent_contact_bloom.contains( node_id )){

      return;
    }
   
    recent_contact_bloom.add( node_id );
   
    addContact( node_id, attachment, false );
  }
 
  public DHTRouterContact
  contactAlive(
    byte[]            node_id,
    DHTRouterContactAttachment  attachment )
  {
    return( addContact( node_id, attachment, true ));
  }
 
  // all incoming node actions come through either contactDead or addContact
  // A side effect of processing
  // the node is that either a ping can be requested (if a replacement node
  // is available and the router wants to check the liveness of an existing node)
  // or a new node can be added (either directly to a node or indirectly via
  // a replacement becoming "live"
  // To avoid requesting these actions while synchronised these are recorded
  // in lists and then kicked off separately here


  public DHTRouterContact
  contactDead(
    byte[]            node_id,
    boolean            force )
  {
    if ( Arrays.equals( router_node_id, node_id )){
     
        // we should never become dead ourselves as this screws up things like
        // checking that stored values are close enough to the K livest nodes (as if we are
        // dead we don't return ourselves and it all goes doo daa )
     
      Debug.out( "DHTRouter: contactDead called on router node!" );
     
      return( local_contact );
    }
   
    try{
      try{
        this_mon.enter();

        consecutive_dead++;
       
        /*
        if ( consecutive_dead != 0 && consecutive_dead % 10 == 0 ){
         
          System.out.println( "consecutive_dead: " + consecutive_dead );
        }     
        */
       
        Object[]  res = findContactSupport( node_id );
         
        DHTRouterNodeImpl    node  = (DHTRouterNodeImpl)res[0];
        DHTRouterContactImpl  contact = (DHTRouterContactImpl)res[1];
         
        if ( contact != null ){

          // some protection against network drop outs - start ignoring dead
          // notifications if we're getting significant continous fails
       
          if ( consecutive_dead < 100 || force ){

            node.dead( contact, force );
          }
        }
       
        return( contact );
       
      }finally{
       
        this_mon.exit();
      }
    }finally{

      dispatchPings();
     
      dispatchNodeAdds();
    }
  }
 
  public void
  contactRemoved(
    byte[]            node_id )
  {
   
  }
 
  public DHTRouterContact
  addContact(
    byte[]            node_id,
    DHTRouterContactAttachment  attachment,
    boolean            known_to_be_alive )
  { 
    try{
      try{
     
        this_mon.enter();
       
        if ( known_to_be_alive ){
         
          consecutive_dead  = 0;
        }
       
        return( addContactSupport( node_id, attachment, known_to_be_alive ));
      }finally{
       
        this_mon.exit();
      }
    }finally{
     
      dispatchPings();
     
      dispatchNodeAdds();
    }
  }
 
  protected DHTRouterContact
  addContactSupport(
    byte[]            node_id,
    DHTRouterContactAttachment  attachment,
    boolean            known_to_be_alive )
  {   
    if ( Arrays.equals( router_node_id, node_id )){
     
        // as we have reduced node id space the chance of us sharing a node id is higher. Easiest way to handle this is
        // just to bail out here
     
      return( local_contact );
    }
   
    DHTRouterNodeImpl  current_node = root;
     
    boolean  part_of_smallest_subtree  = false;
   
    for (int i=0;i<node_id.length;i++){
     
      byte  b = node_id[i];
     
      int  j = 7;
     
      while( j >= 0 ){
         
        if ( current_node == smallest_subtree ){
         
          part_of_smallest_subtree  = true;
        }
       
        boolean  bit = ((b>>j)&0x01)==1?true:false;
               
        DHTRouterNodeImpl  next_node;
       
        if ( bit ){
         
          next_node = current_node.getLeft();
         
        }else{
         
          next_node = current_node.getRight();
        }
       
        if ( next_node == null ){
   
          DHTRouterContact  existing_contact = current_node.updateExistingNode( node_id, attachment, known_to_be_alive );
         
          if ( existing_contact != null ){
           
            return( existing_contact );
          }

          List  buckets = current_node.getBuckets();

          if ( buckets.size() == K ){
           
              // split if either
              // 1) this list contains router_node_id or
              // 2) depth % B is not 0
              // 3) this is part of the smallest subtree
           
            boolean  contains_router_node_id = current_node.containsRouterNodeID();
            int    depth          = current_node.getDepth();
           
            boolean  too_deep_to_split = depth % B == 0// note this will be true for 0 but other
                                  // conditions will allow the split
           
            if (   contains_router_node_id ||
                (!too_deep_to_split||
                part_of_smallest_subtree ){
             
                // the smallest-subtree bit is to ensure that we remember all of
                // our closest neighbours as ultimately they are the ones responsible
                // for returning our identity to queries (due to binary choppery in
                // general the query will home in on our neighbours before
                // hitting us. It is therefore important that we keep ourselves live
                // in their tree by refreshing. If we blindly chopped at K entries
                // (down to B levels) then a highly unbalanced tree would result in
                // us dropping some of them and therefore not refreshing them and
                // therefore dropping out of their trees. There are also other benefits
                // of maintaining this tree regarding stored value refresh
             
                // Note that it is rare for such an unbalanced tree.
                // However, a possible DOS here would be for a rogue node to
                // deliberately try and create such a tree with a large number
                // of entries.
               
              if (   part_of_smallest_subtree &&
                  too_deep_to_split &&
                  ( !contains_router_node_id ) &&
                  getContactCount( smallest_subtree ) > smallest_subtree_max ){
               
                Debug.out( "DHTRouter: smallest subtree max size violation" );
               
                return( null )
              }
             
                // split!!!!
             
              List  left_buckets   = new ArrayList();
              List  right_buckets   = new ArrayList();
             
              for (int k=0;k<buckets.size();k++){
               
                DHTRouterContactImpl  contact = (DHTRouterContactImpl)buckets.get(k);
               
                byte[]  bucket_id = contact.getID();
               
                if (((bucket_id[depth/8]>>(7-(depth%8)))&0x01 ) == 0 ){
                 
                  right_buckets.add( contact );
                 
                }else{
                 
                  left_buckets.add( contact );
                }
              }
       
              boolean  right_contains_rid = false;
              boolean left_contains_rid = false;
             
              if ( contains_router_node_id ){
               
                right_contains_rid =
                    ((router_node_id[depth/8]>>(7-(depth%8)))&0x01 ) == 0;
             
                left_contains_rid  = !right_contains_rid;
              }
             
              DHTRouterNodeImpl  new_left   = new DHTRouterNodeImpl( this, depth+1, left_contains_rid, left_buckets );
              DHTRouterNodeImpl  new_right   = new DHTRouterNodeImpl( this, depth+1, right_contains_rid, right_buckets );
             
              current_node.split( new_left, new_right );
             
              if ( right_contains_rid ){
             
                  // we've created a new smallest subtree
                  // TODO: tidy up old smallest subtree - remember to factor in B...
               
                smallest_subtree = new_left;
               
              }else if ( left_contains_rid ){
               
                  // TODO: tidy up old smallest subtree - remember to factor in B...
               
                smallest_subtree = new_right;
              }
             
                // not complete, retry addition
             
            }else{
               
                // split not appropriate, add as a replacemnet
             
              DHTRouterContactImpl new_contact = new DHTRouterContactImpl( node_id, attachment, known_to_be_alive );
             
              return( current_node.addReplacement( new_contact, max_rep_per_node ));         
            }
          }else{
           
              // bucket space free, just add it
   
            DHTRouterContactImpl new_contact = new DHTRouterContactImpl( node_id, attachment, known_to_be_alive );
             
            current_node.addNode( new_contact )// complete - added to bucket
           
            return( new_contact );
          }           
        }else{
           
          current_node = next_node;
       
          j--;       
        }
      }
    }
   
    Debug.out( "DHTRouter inconsistency" );
   
    return( null );
  }
 
  public List
  findClosestContacts(
    byte[]    node_id,
    int      num_to_return,
    boolean    live_only )
  {
      // find the num_to_return-ish closest nodes - consider all buckets, not just the closest

    try{
      this_mon.enter();
   
      List res = new ArrayList();
       
      findClosestContacts( node_id, num_to_return, 0, root, live_only, res );
   
      return( res );
     
    }finally{
     
      this_mon.exit();
    }
  }
   
  protected void
  findClosestContacts(
    byte[]          node_id,
    int            num_to_return,
    int            depth,
    DHTRouterNodeImpl    current_node,
    boolean          live_only,
    List          res )
  {
    List  buckets = current_node.getBuckets();
   
    if ( buckets != null ){
     
        // add everything from the buckets - caller will sort and select
        // the best ones as required
     
      for (int i=0;i<buckets.size();i++){
       
        DHTRouterContactImpl  contact = (DHTRouterContactImpl)buckets.get(i);

          // use !failing at the moment to include unknown ones
       
        if ( ! ( live_only && contact.isFailing())){
         
          res.add( contact );
        }
      }     
    }else{
   
      boolean bit = ((node_id[depth/8]>>(7-(depth%8)))&0x01 ) == 1;
         
      DHTRouterNodeImpl  best_node;
      DHTRouterNodeImpl  worse_node;
         
      if ( bit ){
           
        best_node = current_node.getLeft();
       
        worse_node = current_node.getRight();
      }else{
           
        best_node = current_node.getRight();
       
        worse_node = current_node.getLeft();
      }
 
      findClosestContacts( node_id, num_to_return, depth+1, best_node, live_only, res  );
     
      if ( res.size() < num_to_return ){
       
        findClosestContacts( node_id, num_to_return, depth+1, worse_node, live_only, res );
      }
    }
  }
 
  public DHTRouterContact
  findContact(
    byte[]    node_id )
  {
    Object[]  res = findContactSupport( node_id );
       
    return((DHTRouterContact)res[1]);
  }
 
  protected DHTRouterNodeImpl
  findNode(
    byte[]  node_id )
  {
    Object[]  res = findContactSupport( node_id );
       
    return((DHTRouterNodeImpl)res[0])
  }
 
  protected Object[]
  findContactSupport(
    byte[]    node_id )
  {
    try{
      this_mon.enter();
   
      DHTRouterNodeImpl  current_node  = root;
     
      for (int i=0;i<node_id.length;i++){
       
        if ( current_node.getBuckets() != null ){
       
          break;
        }
 
        byte  b = node_id[i];
       
        int  j = 7;
       
        while( j >= 0 ){
           
          boolean  bit = ((b>>j)&0x01)==1?true:false;
         
          if ( current_node.getBuckets() != null ){
           
            break;
          }
                 
          if ( bit ){
           
            current_node = current_node.getLeft();
           
          }else{
           
            current_node = current_node.getRight();
          }
         
          j--;
        }
      }
     
      List  buckets = current_node.getBuckets();
     
      for (int k=0;k<buckets.size();k++){
       
        DHTRouterContactImpl  contact = (DHTRouterContactImpl)buckets.get(k);
       
        if ( Arrays.equals(node_id, contact.getID())){
 
          return( new Object[]{ current_node, contact });
        }
      }
     
      return( new Object[]{ current_node, null });
     
    }finally{
     
      this_mon.exit();
    }
  }
 
  protected long
  getNodeCount()
  {
    return( getNodeCount( root ));
  }
 
  protected long
  getNodeCount(
    DHTRouterNodeImpl  node )
  {
    if ( node.getBuckets() != null ){
     
      return( 1 );
     
    }else{
     
      return( 1 + getNodeCount( node.getLeft())) + getNodeCount( node.getRight());
    }
  }
 
  protected long
  getContactCount()
  {
    return( getContactCount( root ));
  }
 
  protected long
  getContactCount(
    DHTRouterNodeImpl  node )
  {
    if ( node.getBuckets() != null ){
     
      return( node.getBuckets().size());
     
    }else{
     
      return( getContactCount( node.getLeft())) + getContactCount( node.getRight());
    }
  }
 
  public List
  findBestContacts(
    int    max )
  {
    Set  set =
      new TreeSet(
          new Comparator()
          {
            public int
            compare(
              Object  o1,
              Object  o2 )
            {
              DHTRouterContactImpl  c1 = (DHTRouterContactImpl)o1;
              DHTRouterContactImpl  c2 = (DHTRouterContactImpl)o2;
             
              return((int)( c2.getTimeAlive() - c1.getTimeAlive()));
            }
          });
   
   
    try{
      this_mon.enter();

      findAllContacts( set, root );
     
    }finally{
     
      this_mon.exit();
    }
   
    List  result = new ArrayList( max );
 
    Iterator  it = set.iterator();
   
    while( it.hasNext() && (max <= 0 || result.size() < max )){
     
      result.add( it.next());
    }
   
    return( result );
  }
 
  public List
  getAllContacts()
  {
    try{
      this_mon.enter();

      List  l = new ArrayList();
     
      findAllContacts( l, root );
     
      return( l );
     
    }finally{
     
      this_mon.exit();
    }
  }
 
  protected void
  findAllContacts(
    Set          set,
    DHTRouterNodeImpl  node )
  {
    List  buckets = node.getBuckets();
   
    if ( buckets == null ){
     
      findAllContacts( set, node.getLeft());
     
      findAllContacts( set, node.getRight());
    }else{
     
      for (int i=0;i<buckets.size();i++){
       
        DHTRouterContactImpl  contact = (DHTRouterContactImpl)buckets.get(i);
               
        set.add( contact );
      }
    }
  }
 
  protected void
  findAllContacts(
    List        list,
    DHTRouterNodeImpl  node )
  {
    List  buckets = node.getBuckets();
   
    if ( buckets == null ){
     
      findAllContacts( list, node.getLeft());
     
      findAllContacts( list, node.getRight());
    }else{
     
      for (int i=0;i<buckets.size();i++){
       
        DHTRouterContactImpl  contact = (DHTRouterContactImpl)buckets.get(i);
               
        list.add( contact );
      }
    }
  }
 
  public void
  seed()
  {
      // defer this a while to see how much refreshing is done by the normal DHT traffic
   
    seed_in_ticks = SEED_DELAY_TICKS;
  }
 
  protected void
  seedSupport()
  {
     
      // refresh all buckets apart from closest neighbour
   
    byte[]  path = new byte[router_node_id.length];
   
    List  ids = new ArrayList();
   
    try{
      this_mon.enter();
     
      refreshNodes( ids, root, path, true, SEED_DELAY_PERIOD * 2 );
     
    }finally{
     
      this_mon.exit();
    }
   
    for (int i=0;i<ids.size();i++){
     
      requestLookup((byte[])ids.get(i), "Seeding DHT" );
    }
  }
 
  protected void
  refreshNodes(
    List        nodes_to_refresh,
    DHTRouterNodeImpl  node,
    byte[]        path,
    boolean        seeding,
    long        max_permitted_idle // 0 -> don't check
  {
      // when seeding we don't do the smallest subtree
   
    if ( seeding && node == smallest_subtree ){
     
      return;
    }
   
    if ( max_permitted_idle != 0 ){
     
      if ( node.getTimeSinceLastLookup() <= max_permitted_idle ){
       
        return;
      }
    }
   
    if ( node.getBuckets() != null ){
   
        // and we also don't refresh the bucket containing the router id when seeding
     
      if ( seeding && node.containsRouterNodeID()){
       
        return;
      }
     
      refreshNode( nodes_to_refresh, node, path );   
    }
   
      // synchronous refresh may result in this bucket being split
      // so we retest here to refresh sub-buckets as required
   
    if ( node.getBuckets() == null ){
     
      int  depth = node.getDepth();
     
      byte  mask = (byte)( 0x01<<(7-(depth%8)));
     
      path[depth/8] = (byte)( path[depth/8] | mask );
     
      refreshNodes( nodes_to_refresh, node.getLeft(), path,seeding, max_permitted_idle  );
     
      path[depth/8] = (byte)( path[depth/8] & ~mask );
   
      refreshNodes( nodes_to_refresh, node.getRight(), path,seeding, max_permitted_idle  );
    }
  }
 
  protected void
  refreshNode(
    List        nodes_to_refresh,
    DHTRouterNodeImpl  node,
    byte[]        path )
  {
      // pick a random id in the node's range.
   
    byte[]  id = new byte[router_node_id.length];
   
    random.nextBytes( id );
   
    int  depth = node.getDepth();
   
    for (int i=0;i<depth;i++){
     
      byte  mask = (byte)( 0x01<<(7-(i%8)));
     
      boolean bit = ((path[i/8]>>(7-(i%8)))&0x01 ) == 1;

      if ( bit ){
       
        id[i/8] = (byte)( id[i/8] | mask );

      }else{
       
        id[i/8] = (byte)( id[i/8] & ~mask )
      }
    }
   
    nodes_to_refresh.add( id );
  }
 
  protected DHTRouterNodeImpl
  getSmallestSubtree()
  {
    return( smallest_subtree );
  }
 
  public void
  recordLookup(
    byte[]  node_id )
  {
    findNode( node_id ).setLastLookupTime();
  }
 
  public void
  refreshIdleLeaves(
    long  idle_max)
  {
    // while we are synchronously refreshing the smallest subtree the tree can mutate underneath us
    // as new contacts are discovered. We NEVER merge things back together
   
    byte[]  path = new byte[router_node_id.length];
   
    List  ids = new ArrayList();
   
    try{
      this_mon.enter();
     
      refreshNodes( ids, root, path, false, idle_max );
     
    }finally{
     
      this_mon.exit();
    }
   
    for (int i=0;i<ids.size();i++){
     
      requestLookup((byte[])ids.get(i), "Idle leaf refresh" );
    }
  }
 
  public boolean
  requestPing(
    byte[]    node_id )
  {
    Object[] res = findContactSupport( node_id );
 
    DHTRouterContactImpl  contact = (DHTRouterContactImpl)res[1];
   
    if ( contact != null ){
     
      adapter.requestPing( contact );
     
      return( true );
    }
   
    return( false );
  }
 
  protected void
  requestPing(
    DHTRouterContactImpl  contact )
  {
      // make sure we don't do the ping when synchronised
   
    DHTLog.log( "DHTRouter: requestPing:" + DHTLog.getString( contact.getID()));
   
    if ( contact == local_contact ){
     
      Debug.out( "pinging local contact" );
    }
   
    try{
      this_mon.enter();
     
      if ( !outstanding_pings.contains( contact )){
     
        outstanding_pings.add( contact );
      }
    }finally{
     
      this_mon.exit();
    }
  }
 
  protected void
  dispatchPings()
  {
    if ( outstanding_pings.size() == 0 ){
     
      return;
    }
   
    List  pings;
   
    try{
      this_mon.enter();
   
      pings  = outstanding_pings;
     
      outstanding_pings = new ArrayList();
     
    }finally{
     
      this_mon.exit();
    }
   
    for (int i=0;i<pings.size();i++){
     
      adapter.requestPing((DHTRouterContactImpl)pings.get(i));
    }
  }
 
  protected void
  pingeroonies()
  {
    try{
      this_mon.enter();
   
      DHTRouterNodeImpl  node = root;
     
      LinkedList  stack = new LinkedList();
     
      while( true ){
       
        List  buckets = node.getBuckets();
       
        if ( buckets == null ){
         
          if ( random.nextBoolean()){
           
            stack.add( node.getRight());

            node = node.getLeft();           
           
          }else{
           
            stack.add( node.getLeft());

            node = node.getRight();           
          }
        }else{
         
          int           max_fails       = 0;
          DHTRouterContactImpl  max_fails_contact  = null;
         
          for (int i=0;i<buckets.size();i++){
           
            DHTRouterContactImpl  contact = (DHTRouterContactImpl)buckets.get(i);

            if ( !contact.getPingOutstanding()){
             
              int  fails = contact.getFailCount();
             
              if ( fails > max_fails ){
               
                max_fails      = fails;
                max_fails_contact  = contact;
              }
            }
          }
         
          if ( max_fails_contact != null ){
                       
            requestPing( max_fails_contact );
           
            return;
          }
         
          if ( stack.size() == 0 ){
           
            break;
          }
         
          node = (DHTRouterNodeImpl)stack.removeLast();
        }
      }
    }finally{
     
      this_mon.exit();
     
      dispatchPings();
    }
  }
 
  protected void
  requestNodeAdd(
    DHTRouterContactImpl  contact )
  {
      // make sure we don't do the addition when synchronised
   
    DHTLog.log( "DHTRouter: requestNodeAdd:" + DHTLog.getString( contact.getID()));
   
    if ( contact == local_contact ){

      Debug.out( "adding local contact" );
    }
   
    try{
      this_mon.enter();
     
      if ( !outstanding_adds.contains( contact )){
     
        outstanding_adds.add( contact );
      }
    }finally{
     
      this_mon.exit();
    }
  }
 
  protected void
  dispatchNodeAdds()
  {
    if ( outstanding_adds.size() == 0 ){
     
      return;
    }
   
    List  adds;
   
    try{
      this_mon.enter();
   
      adds  = outstanding_adds;
     
      outstanding_adds = new ArrayList();
     
    }finally{
     
      this_mon.exit();
    }
   
    for (int i=0;i<adds.size();i++){
     
      adapter.requestAdd((DHTRouterContactImpl)adds.get(i));
    }
  }

  public byte[]
  refreshRandom()
  {
    byte[]  id = new byte[router_node_id.length];
   
    random.nextBytes( id );
 
    requestLookup( id, "Random Refresh" );
   
    return( id );
  }
 
  protected void
  requestLookup(
    byte[]    id,
    String    description )
  {
    DHTLog.log( "DHTRouter: requestLookup:" + DHTLog.getString( id ));
   
    adapter.requestLookup( id, description );
  }
 
  protected void
  getStatsSupport(
    long[]        stats_array,
    DHTRouterNodeImpl  node )
  {
    stats_array[DHTRouterStats.ST_NODES]++;
   
    List  buckets = node.getBuckets();
   
    if ( buckets == null ){
     
      getStatsSupport( stats_array, node.getLeft());
     
      getStatsSupport( stats_array, node.getRight());
     
    }else{
     
      stats_array[DHTRouterStats.ST_LEAVES]++;
     
      stats_array[DHTRouterStats.ST_CONTACTS] += buckets.size();
     
      for (int i=0;i<buckets.size();i++){
       
        DHTRouterContactImpl  contact = (DHTRouterContactImpl)buckets.get(i);
       
        if ( contact.getFirstFailTime() > 0 ){
         
          stats_array[DHTRouterStats.ST_CONTACTS_DEAD]++;
         
        }else if ( contact.hasBeenAlive()){
         
          stats_array[DHTRouterStats.ST_CONTACTS_LIVE]++;
         
        }else{
         
          stats_array[DHTRouterStats.ST_CONTACTS_UNKNOWN]++;
        }
      }
     
      List  rep = node.getReplacements();
     
      if ( rep != null ){
       
        stats_array[DHTRouterStats.ST_REPLACEMENTS] += rep.size();
      }
    }
  }
 
  protected long[]
  getStatsSupport()
  {
     /* number of nodes
     * number of leaves
     * number of contacts
     * number of replacements
     * number of live contacts
     * number of unknown contacts
     * number of dying contacts
     */
   
    try{
      this_mon.enter();
   
      long[]  res = new long[7];
   
      getStatsSupport( res, root );
   
      return( res );
     
    }finally{
     
      this_mon.exit();
    }
  }
 
  protected void
  log(
    String  str )
  {
    logger.log( str );
  }
 
  public void
  print()
  {
    try{
      this_mon.enter();
   
      log( "DHT: " + DHTLog.getString2(router_node_id) + ", node count = " + getNodeCount()+ ", contacts =" + getContactCount());
   
      root.print( "", "" );
     
    }finally{
     
      this_mon.exit();
    }
  }
 
  public void
  destroy()
  {
    timer_event.cancel();
   
    notifyDead();
  }
}
TOP

Related Classes of com.aelitis.azureus.core.dht.router.impl.DHTRouterImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.