Package com.aelitis.azureus.plugins.net.buddy.tracker

Source Code of com.aelitis.azureus.plugins.net.buddy.tracker.BuddyPluginTracker

/*
* Created on May 27, 2008
* Created by Paul Gardner
*
* Copyright 2008 Vuze, Inc.  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; version 2 of the License only.
*
* 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.
*/


package com.aelitis.azureus.plugins.net.buddy.tracker;

import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.util.*;

import org.gudy.azureus2.core3.global.GlobalManager;
import org.gudy.azureus2.core3.global.GlobalManagerAdapter;
import org.gudy.azureus2.core3.peer.PEPeerManager;
import org.gudy.azureus2.core3.util.AddressUtils;
import org.gudy.azureus2.core3.util.Average;
import org.gudy.azureus2.core3.util.Constants;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.HashWrapper;
import org.gudy.azureus2.core3.util.LightHashMap;
import org.gudy.azureus2.core3.util.SHA1;
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 org.gudy.azureus2.plugins.download.Download;
import org.gudy.azureus2.plugins.download.DownloadAnnounceResult;
import org.gudy.azureus2.plugins.download.DownloadListener;
import org.gudy.azureus2.plugins.download.DownloadManagerListener;
import org.gudy.azureus2.plugins.download.DownloadPeerListener;
import org.gudy.azureus2.plugins.download.DownloadScrapeResult;
import org.gudy.azureus2.plugins.download.DownloadTrackerListener;
import org.gudy.azureus2.plugins.peers.Peer;
import org.gudy.azureus2.plugins.peers.PeerEvent;
import org.gudy.azureus2.plugins.peers.PeerListener2;
import org.gudy.azureus2.plugins.peers.PeerManager;
import org.gudy.azureus2.plugins.peers.PeerManagerListener;
import org.gudy.azureus2.plugins.peers.PeerStats;
import org.gudy.azureus2.plugins.torrent.Torrent;
import org.gudy.azureus2.plugins.ui.config.BooleanParameter;
import org.gudy.azureus2.plugins.ui.config.Parameter;
import org.gudy.azureus2.plugins.ui.config.ParameterListener;
import org.gudy.azureus2.plugins.ui.model.BasicPluginConfigModel;
import org.gudy.azureus2.pluginsimpl.local.PluginCoreUtils;

import com.aelitis.azureus.core.AzureusCoreFactory;
import com.aelitis.azureus.core.util.CopyOnWriteList;
import com.aelitis.azureus.core.util.CopyOnWriteSet;
import com.aelitis.azureus.plugins.net.buddy.*;

public class
BuddyPluginTracker
  implements BuddyPluginListener, DownloadManagerListener, BuddyPluginAZ2TrackerListener, DownloadPeerListener
{
  private  static final Object  PEER_KEY    = new Object();    // maps to Download object
 
  private static final Object  PEER_STATS_KEY  = new Object();
 
  public static final int BUDDY_NETWORK_IDLE    = 1;
  public static final int BUDDY_NETWORK_OUTBOUND  = 2;
  public static final int BUDDY_NETWORK_INBOUND  = 3;
 
  private static final int  TRACK_CHECK_PERIOD    = 15*1000;
  private static final int  TRACK_CHECK_TICKS    = TRACK_CHECK_PERIOD/BuddyPlugin.TIMER_PERIOD;

  private static final int  PEER_CHECK_PERIOD    = 60*1000;
  private static final int  PEER_CHECK_TICKS    = PEER_CHECK_PERIOD/BuddyPlugin.TIMER_PERIOD;
 
  private static final int  PEER_RECHECK_PERIOD    = 120*1000;
  private static final int  PEER_RECHECK_TICKS    = PEER_RECHECK_PERIOD/BuddyPlugin.TIMER_PERIOD;

  private static final int  TRACK_INTERVAL      = 10*60*1000;
 
  private static final int  SHORT_ID_SIZE      = 4;
  private static final int  FULL_ID_SIZE      = 20;
 
  private static final int  REQUEST_TRACKER_SUMMARY  = 1;
  private static final int  REPLY_TRACKER_SUMMARY  = 2;
  private static final int  REQUEST_TRACKER_STATUS  = 3;
  private static final int  REPLY_TRACKER_STATUS  = 4;
  private static final int  REQUEST_TRACKER_CHANGE  = 5;
  private static final int  REPLY_TRACKER_CHANGE  = 6;
  private static final int  REQUEST_TRACKER_ADD    = 7;
  private static final int  REPLY_TRACKER_ADD    = 8;
 
  private static final int  RETRY_SEND_MIN      = 5*60*1000;
  private static final int  RETRY_SEND_MAX      = 60*60*1000;
 
  private static final int  BUDDY_NO    = 0;
  private static final int  BUDDY_MAYBE    = 1;
  private static final int  BUDDY_YES    = 2;
 
  private BuddyPlugin    plugin;
 
  private boolean      plugin_enabled;
  private boolean      tracker_enabled;
  private boolean      seeding_only;
 
  private boolean      old_plugin_enabled;
  private boolean      old_tracker_enabled;
  private boolean      old_seeding_only;
 
  private int        network_status = BUDDY_NETWORK_IDLE;
 
  private Set        online_buddies       = new HashSet();
  private Map        online_buddy_ips    = new HashMap();
 
  private Set        tracked_downloads    = new HashSet();
  private int        download_set_id;
 
  private Set        last_processed_download_set;
  private int        last_processed_download_set_id;
 
  private Map        short_id_map  = new HashMap();
  private Map        full_id_map    = new HashMap();
 
  private Set        actively_tracking  = new HashSet();
   
  private CopyOnWriteSet  buddy_peers  = new CopyOnWriteSet();
 
  private CopyOnWriteList  listeners = new CopyOnWriteList();
 
  private TimerEventPeriodic  buddy_stats_timer;
 
  private Average buddy_receive_speed = Average.getInstance(1000, 10);
 
  private Average buddy_send_speed   = Average.getInstance(1000, 10);
 
  public
  BuddyPluginTracker(
    BuddyPlugin          _plugin,
    BasicPluginConfigModel    _config )
  {
    plugin    = _plugin;
   
    final BooleanParameter te = _config.addBooleanParameter2("azbuddy.tracker.enabled", "azbuddy.tracker.enabled", true );
   
    tracker_enabled = te.getValue();
   
    te.addListener(
      new ParameterListener()
      {
        public void
        parameterChanged(
          Parameter param )
        {
          tracker_enabled = te.getValue();
         
          checkEnabledState();
        }
      });
   
    // Assumed if we already have a plugin reference, that the
    // Azureus Core is available
    GlobalManager gm = AzureusCoreFactory.getSingleton().getGlobalManager();
   
    gm.addListener(
      new GlobalManagerAdapter()
      {
        public void
        seedingStatusChanged(
          boolean seeding_only_mode,
          boolean potentially_seeding_only )
        {
          seeding_only = potentially_seeding_only;
         
          checkEnabledState();
        }
      }, false );
   
    seeding_only = gm.isPotentiallySeedingOnly();
   
    checkEnabledState();
  }
 
  public void
  initialise()
  {
    plugin_enabled = plugin.isEnabled();
   
    checkEnabledState();
   
    List buddies = plugin.getBuddies();
   
    for (int i=0;i<buddies.size();i++){
     
      buddyAdded((BuddyPluginBuddy)buddies.get(i));
    }
   
    plugin.addListener( this );
   
    plugin.getAZ2Handler().addTrackerListener( this );

    plugin.getPluginInterface().getDownloadManager().addListener( this, true );
  }
 
  public void
  tick(
    int    tick_count )
  {
    if ( tick_count % TRACK_CHECK_TICKS == 0 ){
     
      checkTracking();
    }
   
    if ( ( tick_count-1 ) % TRACK_CHECK_TICKS == 0 ){
     
      doTracking();
    }
   
    if ( tick_count % PEER_CHECK_TICKS == 0 ){
     
      checkPeers();
    }
   
    if ( tick_count % PEER_RECHECK_TICKS == 0 ){
     
      recheckPeers();
    }
  }
 
  public int
  getNetworkStatus()
  {
    return( network_status );
  }
 
  public long
  getNetworkReceiveBytesPerSecond()
  {
    return( buddy_receive_speed.getAverage());
  }
 
  public long
  getNetworkSendBytesPerSecond()
  {
    return( buddy_send_speed.getAverage());
  }
 
  protected void
  doTracking()
  {
    if ( !( plugin_enabled && tracker_enabled )){
     
      return;
    }

    Map  to_do = new HashMap();
   
    Set active_set = new HashSet();
   
    synchronized( online_buddies ){

      Iterator it = online_buddies.iterator();
       
      while( it.hasNext()){
       
        BuddyPluginBuddy  buddy = (BuddyPluginBuddy)it.next();
       
        buddyData buddy_data = getBuddyData( buddy );
       
        Map active = buddy_data.getDownloadsToTrack();
       
        if ( active.size() > 0 ){
         
          Iterator it2 = active.entrySet().iterator();
         
          List track_now = new ArrayList();
         
          while( it2.hasNext()){
           
            Map.Entry entry = (Map.Entry)it2.next();
           
            Download   dl   = (Download)entry.getKey();
            boolean    now = ((Boolean)entry.getValue()).booleanValue();
           
            if ( now ){
             
              track_now.add( dl );
            }
           
            active_set.add( dl );
          }
         
          if( track_now.size() > 0 ){
         
            to_do.put( buddy, track_now );
          }
        }
      }
    }
   
    synchronized( actively_tracking ){
     
      Iterator it = active_set.iterator();
     
      while( it.hasNext()){
       
        Download dl = (Download)it.next();
       
        if ( !actively_tracking.contains( dl )){
         
          actively_tracking.add( dl );
         
          trackPeers( dl );
        }
      }
     
      it = actively_tracking.iterator();
     
      while( it.hasNext()){
       
        Download dl = (Download)it.next();
       
        if ( !active_set.contains( dl )){
         
          it.remove();
         
          untrackPeers( dl );
        }
      }
    }
   
    Iterator it = to_do.entrySet().iterator();
   
    while( it.hasNext()){
     
      Map.Entry  entry = (Map.Entry)it.next();
         
      BuddyPluginBuddy buddy = (BuddyPluginBuddy)entry.getKey();
     
      if ( !buddy.isOnline( false )){
       
        continue;
      }
     
      InetAddress ip       = buddy.getAdjustedIP();
     
      if ( ip == null ){
       
        continue;
      }
     
      int      tcp_port  = buddy.getTCPPort();
      int      udp_port  = buddy.getUDPPort();
     
      List  downloads = (List)entry.getValue();
     
      for (int i=0;i<downloads.size();i++){
       
        Download  download = (Download)downloads.get(i);
       
        PeerManager pm = download.getPeerManager();
       
        if ( pm == null ){
         
          continue;
        }
       
        Peer[] existing_peers = pm.getPeers( ip.getHostAddress());
     
        boolean  connected = false;
       
        for (int j=0;j<existing_peers.length;j++){
         
          Peer peer = existing_peers[j];
         
          if (   peer.getTCPListenPort() == tcp_port ||
              peer.getUDPListenPort() == udp_port ){
           
            connected = true;
           
            break;
         
        }
       
        if ( connected ){
         
          log( download.getName() + " - peer " + ip.getHostAddress() + " already connected" );
         
          continue;
        }
       
        log( download.getName() + " - connecting to peer " + ip.getHostAddress());

        PEPeerManager c_pm = PluginCoreUtils.unwrap( pm );
       
        Map  user_data = new LightHashMap();
       
        user_data.put( PEER_KEY, download );
       
        user_data.put( Peer.PR_PRIORITY_CONNECTION, new Boolean( true ));
       
        c_pm.addPeer( ip.getHostAddress(), tcp_port, udp_port, true, user_data );
      }
    }
  }
 
  protected void
  checkTracking()
  {
    if ( !( plugin_enabled && tracker_enabled )){
     
      return;
    }
   
    List  online;
   
    synchronized( online_buddies ){

      online = new ArrayList( online_buddies );
    }
   
    Set      downloads;
    int      downloads_id;
   
    synchronized( tracked_downloads ){
     
      boolean downloads_changed = last_processed_download_set_id != download_set_id;
     
      if ( downloads_changed ){
       
        last_processed_download_set   = new HashSet( tracked_downloads );
        last_processed_download_set_id  = download_set_id;
      }
     
      downloads     = last_processed_download_set;
      downloads_id  = last_processed_download_set_id;
    }
   
    Map  diff_map = new HashMap();
   
    for (int i=0;i<online.size();i++){
     
      BuddyPluginBuddy  buddy = (BuddyPluginBuddy)online.get(i);
     
      buddyData buddy_data = getBuddyData( buddy );
     
      buddy_data.updateLocal( downloads, downloads_id, diff_map );
    }
  }   
 
  public void
  initialised(
    boolean    available )
  { 
  }
 
  public void
  buddyAdded(
    BuddyPluginBuddy  buddy )
  {
    buddyChanged( buddy );
  }
 
  public void
  buddyRemoved(
    BuddyPluginBuddy  buddy )
  {
    buddyChanged( buddy );
  }

  public void
  buddyChanged(
    BuddyPluginBuddy  buddy )
  { 
    if ( buddy.isOnline( false )){
     
      addBuddy( buddy );
     
    }else{
     
      removeBuddy( buddy );
    }
  }
 
  protected buddyData
  getBuddyData(
    BuddyPluginBuddy    buddy )
  {
    synchronized( online_buddies ){
     
      buddyData buddy_data = (buddyData)buddy.getUserData( BuddyPluginTracker.class );

      if ( buddy_data == null ){
       
        buddy_data = new buddyData( buddy );
       
        buddy.setUserData( BuddyPluginTracker.class, buddy_data );
      }
     
      return( buddy_data );
    }
  }
 
  protected buddyData
  addBuddy(
    BuddyPluginBuddy    buddy )
  {
    synchronized( online_buddies ){
     
      if ( !online_buddies.contains( buddy )){
       
        online_buddies.add( buddy );
      }
     
      buddyData bd = getBuddyData( buddy );
     
      if ( bd.hasIPChanged()){
       
        String  ip = bd.getIP();
       
        if ( ip != null ){
         
          List  l = (List)online_buddy_ips.get( ip );
         
          if ( l != null ){
           
            l.remove( buddy );
           
            if ( l.size() == 0 ){
             
              online_buddy_ips.remove( ip );
            }
          }
        }
       
        bd.updateIP();
       
        ip = bd.getIP();
       
        if ( ip != null ){
         
          List l = (List)online_buddy_ips.get( ip );
         
          if ( l == null ){
           
            l = new ArrayList();
           
            online_buddy_ips.put( ip, l );
          }
       
          l.add( buddy );
        }
      }
     
      return( bd );
    }
  }
   
  protected void
  removeBuddy(
    BuddyPluginBuddy    buddy )
  {   
    synchronized( online_buddies ){

      if ( online_buddies.contains( buddy )){
       
        buddyData bd = getBuddyData( buddy );

        online_buddies.remove( buddy );
       
        String  ip = bd.getIP();
       
        if ( ip != null ){
         
          List  l = (List)online_buddy_ips.get( ip );
         
          if ( l != null ){
           
            l.remove( buddy );
           
            if ( l.size() == 0 ){
             
              online_buddy_ips.remove( ip );
            }
          }
        }
      }
    }
  }
 
  protected int
  isBuddy(
    Peer    peer )
  {
    String  peer_ip = peer.getIp();
   
    List ips = AddressUtils.getLANAddresses( peer_ip );

    synchronized( online_buddies ){
     
      int  result = BUDDY_NO;
   
      String  tested = "";
outer: 
      for (int i=0;i<ips.size();i++){

        String ip = (String)ips.get(i);
       
        tested += ip;
       
        List buddies =(List)online_buddy_ips.get( ip  );
     
        if ( buddies != null ){
                 
          if ( peer.getTCPListenPort() == 0 && peer.getUDPListenPort() == 0 ){
           
            result = BUDDY_MAYBE;
           
          }else{
         
            for (int j=0;j<buddies.size();j++){
               
              BuddyPluginBuddy  buddy = (BuddyPluginBuddy)buddies.get(j);
               
              if buddy.getTCPPort() == peer.getTCPListenPort() &&
                  buddy.getTCPPort() != 0 ){
               
                result =  BUDDY_YES;
               
                break outer;
              }
                 
              if buddy.getUDPPort() == peer.getUDPListenPort() &&
                  buddy.getUDPPort() != 0 ){
               
                result =  BUDDY_YES;
               
                break outer;
              }
            }
          }
        }
      }
     
      // log( "isBuddy: " + peer_ip + " -> " + result + ",tested=" + tested );
     
      return( result );
    }
  }
 
  public void
  messageLogged(
    String    str,
    boolean    error )
  { 
  }
 
  public void
  enabledStateChanged(
    boolean   _enabled )
  {
    plugin_enabled = _enabled;
   
    checkEnabledState();
  }
 
  public boolean
  isEnabled()
  {
    synchronized( this ){
     
      return( plugin_enabled && tracker_enabled );
    }
  }
 
  protected void
  checkEnabledState()
  {
    boolean  seeding_change   = false;
    boolean  enabled_change  = false;
   
    synchronized( this ){
     
      boolean  old_enabled = old_plugin_enabled && old_tracker_enabled;
     
      if ( plugin_enabled != old_plugin_enabled ){
       
        log( "Plugin enabled state changed to " + plugin_enabled );
       
        old_plugin_enabled = plugin_enabled;
      }
     
      if ( tracker_enabled != old_tracker_enabled ){
       
        log( "Tracker enabled state changed to " + tracker_enabled );
       
        old_tracker_enabled = tracker_enabled;
      }
     
      if ( seeding_only != old_seeding_only ){
       
        log( "Seeding-only state changed to " + seeding_only );
       
        old_seeding_only = seeding_only;
       
        seeding_change = true;
      }
     
      enabled_change = old_enabled != ( plugin_enabled && tracker_enabled );
    }
   
    if ( seeding_change ){
     
      updateSeedingMode();
    }
   
    if ( enabled_change ){
     
      fireEnabledChanged( isEnabled());
    }
  }
 
  protected void
  updateSeedingMode()
  {
    updateNetworkStatus();
   
    List  online;
   
    synchronized( online_buddies ){

      online = new ArrayList( online_buddies );
    }
   
    for (int i=0;i<online.size();i++){
     
      buddyData buddy_data = getBuddyData((BuddyPluginBuddy)online.get(i));
     
      if ( buddy_data.hasDownloadsInCommon()){
       
        buddy_data.updateStatus();
      }
    }
  }
 
  public void
  downloadAdded(
    final Download  download )
  {
    Torrent t = download.getTorrent();
   
    if ( t == null ){
     
      return;
    }
   
    if ( t.isPrivate()){
     
      download.addTrackerListener(
        new DownloadTrackerListener()
        {
          public void
          scrapeResult(
            DownloadScrapeResult result )
          {         
          }
 
          public void
          announceResult(
            DownloadAnnounceResult result)
          {
            if ( okToTrack( download )){
             
              trackDownload( download );
             
            }else{
             
              untrackDownload( download );
            }           
          }
        },
        false );
    }
   
    if ( okToTrack( download )){
     
      trackDownload( download );
    }
   
    download.addListener(
      new DownloadListener()
      {
        public void
        stateChanged(
          Download    download,
          int        old_state,
          int        new_state )
        {
          if ( okToTrack( download )){
           
            trackDownload( download );
           
          }else{
           
            untrackDownload( download );
          }
        }

        public void
        positionChanged(
          Download  download,
          int     oldPosition,
          int     newPosition )
        {
        }
      });
  }
 
  public void
  downloadRemoved(
    Download  download )
  {
    untrackDownload( download );
  }
 
  protected void
  trackDownload(
    Download  download )
  {
    synchronized( tracked_downloads ){
     
      if ( tracked_downloads.contains( download )){
       
        return;
      }
             
      downloadData download_data = new downloadData( download );
       
      download.setUserData( BuddyPluginTracker.class, download_data );
     
      HashWrapper  full_id    = download_data.getID();
     
      HashWrapper short_id   = new HashWrapper( full_id.getHash(), 0, 4 );
     
      full_id_map.put( full_id, download );
     
      List  dls = (List)short_id_map.get( short_id );
     
      if ( dls == null ){
       
        dls = new ArrayList();
       
        short_id_map.put( short_id, dls );
      }
     
      dls.add( download );
     
      tracked_downloads.add( download );
     
      download_set_id++;
    }
  }
 
  protected void
  untrackDownload(
    Download  download )
  {
    synchronized( tracked_downloads ){
     
      if ( tracked_downloads.remove( download )){
       
        download_set_id++;
       
        downloadData download_data = (downloadData)download.getUserData( BuddyPluginTracker.class );
               
        download.setUserData( BuddyPluginTracker.class, null );
       
        HashWrapper  full_id    = download_data.getID();
       
        full_id_map.remove( full_id );
       
        HashWrapper short_id   = new HashWrapper( full_id.getHash(), 0, SHORT_ID_SIZE );
       
        List  dls = (List)short_id_map.get( short_id );

        if ( dls != null ){
         
          dls.remove( download );
         
          if ( dls.size() == 0 ){
           
            short_id_map.remove( short_id );
          }
        }
      }
    }
   
    synchronized( online_buddies ){

      Iterator it = online_buddies.iterator();
       
      while( it.hasNext()){
       
        BuddyPluginBuddy  buddy = (BuddyPluginBuddy)it.next();
       
        buddyData buddy_data = getBuddyData( buddy );
       
        buddy_data.resetTracking( download );
      }
    }
   
    synchronized( actively_tracking ){

      actively_tracking.remove( download );
    }
  }
 
  protected void
  trackPeers(
    final Download    download )
  {
    PeerManager pm = download.getPeerManager();
   
      // not running
   
    if ( pm == null ){

      synchronized( actively_tracking ){

        actively_tracking.remove( download );
      }
    }else{
     
      log( "Tracking peers for " + download.getName());
           
      download.addPeerListener( this );
    }
  }
 
  public void
  peerManagerAdded(
    Download    download,
    PeerManager    peer_manager )
  {
    trackPeers( download, peer_manager );
  }
 
  public void
  peerManagerRemoved(
    Download    download,
    PeerManager    peer_manager )
  {
    synchronized( actively_tracking ){

      actively_tracking.remove( download );
    }
   
    download.removePeerListener( this );
  }
 
  protected void
  trackPeers(
    final Download  download,
    PeerManager    pm )
  {
    pm.addListener(
      new PeerManagerListener()
      {
        public void
        peerAdded(
          PeerManager    manager,
          Peer      peer )
        {
          synchronized( actively_tracking ){
           
            if ( !actively_tracking.contains( download )){
             
              manager.removeListener( this );
             
              return;
            }
          }
       
          trackPeer( download, peer );
        }
       
        public void
        peerRemoved(
          PeerManager  manager,
          Peer    peer )
        {
        }
      });
   
    Peer[] peers = pm.getPeers();
   
    for (int i=0;i<peers.length;i++){
     
      trackPeer( download, peers[i] );
    }
  }
 
  protected void
  trackPeer(
    final Download  download,
    final Peer    peer )
  {
    int type = isBuddy( peer );
   
    if ( type == BUDDY_YES ){
     
      markBuddyPeer( download, peer );
     
    }else if ( type == BUDDY_MAYBE ){
     
        // mark as peer early so that we get optimistic disconnect if needed
     
      markBuddyPeer( download, peer );
     
      PeerListener2 listener =
        new PeerListener2()
        {
          public void
          eventOccurred(
            PeerEvent event )
          {
            if ( event.getType() == PeerEvent.ET_STATE_CHANGED ){
             
              if (((Integer)event.getData()).intValue() == Peer.TRANSFERING ){
               
                peer.removeListener( this );
               
                  // withdraw buddy marker if it turns out our earlier optimism
                  // was misplaced
               
                if ( isBuddy( peer ) != BUDDY_YES ){
                 
                  unmarkBuddyPeer( peer );
                }
              }
            }
          }
        };
       
      peer.addListener( listener );

      if ( peer.getState() == Peer.TRANSFERING ){
       
        peer.removeListener( listener );
       
          // withdraw buddy marker if it turns out our earlier optimism
          // was misplaced
     
        if ( isBuddy( peer ) != BUDDY_YES ){
         
          unmarkBuddyPeer( peer );
        }
      }
    }
  }
 
  protected void
  untrackPeers(
    Download    download )
  {
    log( "Not tracking peers for " + download.getName());
   
    download.removePeerListener( this );
   
    PeerManager pm = download.getPeerManager();
   
    if ( pm != null ){

      Peer[] peers = pm.getPeers();
     
      for (int i=0;i<peers.length;i++){
       
        Peer  peer = peers[i];
       
        unmarkBuddyPeer( peer );
      }
    }
  }
 
  protected void
  markBuddyPeer(
    final Download    download,
    final Peer      peer )
  {
    boolean  state_changed   = false;
   
    synchronized( buddy_peers ){
     
      if ( !buddy_peers.contains( peer )){
       
        log( "Adding buddy peer " + peer.getIp());
       
        if ( buddy_peers.size() == 0 ){
         
          if ( buddy_stats_timer == null ){
           
            buddy_stats_timer =
              SimpleTimer.addPeriodicEvent(
                "BuddyTracker:stats",
                1000,
                new TimerEventPerformer()
                {
                  public void
                  perform(
                    TimerEvent event )
                  {
                    Iterator it = buddy_peers.iterator();
                   
                    long  total_sent    = 0;
                    long  total_received  = 0;
                   
                    while( it.hasNext()){
                     
                      Peer  p = (Peer)it.next();
                     
                      PeerStats ps = p.getStats();
                     
                      long sent    = ps.getTotalSent();
                      long received   = ps.getTotalReceived();
                     
                      long[]  last = (long[])p.getUserData( PEER_STATS_KEY );
                     
                      if ( last != null ){
                       
                        total_sent     += sent - last[0];
                        total_received  += received - last[1];
                      }
                     
                      p.setUserData( PEER_STATS_KEY, new long[]{ sent, received });
                    }
                   
                    buddy_receive_speed.addValue( total_received );
                    buddy_send_speed.addValue( total_sent );
                  }
                });
          }
         
          state_changed   = true;
        }
       
        buddy_peers.add( peer );
       
        peer.setUserData( PEER_KEY, download );
       
        peer.setPriorityConnection( true );
               
        log( download.getName() + ": adding buddy peer " + peer.getIp());

        peer.addListener(
          new PeerListener2()
          {
            public void
            eventOccurred(
              PeerEvent event )
            {
              if ( event.getType() == PeerEvent.ET_STATE_CHANGED ){
               
                int  state = ((Integer)event.getData()).intValue();
               
                if ( state == Peer.CLOSING || state == Peer.DISCONNECTED ){
                 
                  peer.removeListener( this );
                 
                  unmarkBuddyPeer( peer );
                }
              } 
            }
          });
      }
    }
   
    if ( peer.getState() == Peer.CLOSING || peer.getState() == Peer.DISCONNECTED ){
     
      unmarkBuddyPeer( peer );
    }
   
    if ( state_changed ){
     
      updateNetworkStatus();
    }
  }
 
  protected void
  unmarkBuddyPeer(
    Peer    peer )
  {
    boolean  state_changed = false;
   
    synchronized( buddy_peers ){

      Download download = (Download)peer.getUserData( PEER_KEY );
       
      if ( download == null ){
       
        return;
      }

      if ( buddy_peers.remove( peer )){
       
        if ( buddy_peers.size() == 0 ){
         
          state_changed = true;
         
          if ( buddy_stats_timer != null ){
           
            buddy_stats_timer.cancel();
           
            buddy_stats_timer = null;
          }
        }
       
        log( download.getName() + ": removing buddy peer " + peer.getIp());
      }
     
      peer.setUserData( PEER_KEY, null );
     
      peer.setPriorityConnection( false );
   
   
    if ( state_changed ){
     
      updateNetworkStatus();
    }
  }
 
  protected void
  checkPeers()
  {
    List  to_unmark = new ArrayList();
   
    synchronized( buddy_peers ){

      Iterator  it = buddy_peers.iterator();
     
      while( it.hasNext()){
       
        Peer  peer = (Peer)it.next();
       
        if ( peer.getState() == Peer.CLOSING || peer.getState() == Peer.DISCONNECTED ){
         
          to_unmark.add( peer );
        }
      }
    }
   
    for (int i=0;i<to_unmark.size();i++){
     
      unmarkBuddyPeer((Peer)to_unmark.get(i));
    }
  }
 
  protected void
  recheckPeers()
  {
      // go over peers for active torrents to see if we've missed and. can really only
      // happen with multi-homed LAN setups where a new (and utilised) route is found
      // after we start tracking
   
    synchronized( actively_tracking ){
     
      Iterator it = actively_tracking.iterator();
     
      while( it.hasNext()){
       
        Download download = (Download)it.next();
       
        PeerManager pm = download.getPeerManager();
       
        if ( pm != null ){
         
          Peer[] peers = pm.getPeers();
         
          for (int i=0;i<peers.length;i++){
           
            trackPeer( download, peers[i] );
          }
        }
      }
    }
  }
 
  protected void
  updateNetworkStatus()
  { 
    int    new_status;
    boolean  changed = false;
   
    synchronized( buddy_peers ){
 
      if ( buddy_peers.size() == 0 ){
       
        new_status   = BUDDY_NETWORK_IDLE;
       
      }else{
       
        new_status  = seeding_only?BUDDY_NETWORK_OUTBOUND:BUDDY_NETWORK_INBOUND;
      }
     
      if ( new_status != network_status ){
       
        network_status  = new_status;
       
        changed  = true;
      }
    }
   
    if ( changed ){
   
      fireStateChange( new_status );
    }
  }
 
  public void
  addListener(
    BuddyPluginTrackerListener    l )
  {
    listeners.add( l );
  }
 
  public void
  removeListener(
    BuddyPluginTrackerListener    l )
  {
    listeners.remove( l );
  }
 
  protected void
  fireStateChange(
    int    state )
  {
    Iterator  it = listeners.iterator();
   
    while( it.hasNext()){
     
      try{
        ((BuddyPluginTrackerListener)it.next()).networkStatusChanged( this, state );
       
      }catch( Throwable e ){
       
        Debug.out( e );
      }
    }
  }
 
  protected void
  fireEnabledChanged(
    boolean  enabled )
  {
    Iterator  it = listeners.iterator();
   
    while( it.hasNext()){
     
      try{
        ((BuddyPluginTrackerListener)it.next()).enabledStateChanged( this, enabled );
       
      }catch( Throwable e ){
       
        Debug.out( e );
      }
    }
  }
 
  protected void
  sendMessage(
    BuddyPluginBuddy  buddy,
    int          type,
    Map          body )
  {
    Map  msg = new HashMap();
   
    msg.put( "type", new Long( type ));
    msg.put( "msg", body );
   
    plugin.getAZ2Handler().sendAZ2TrackerMessage(
        buddy,
        msg,
        BuddyPluginTracker.this );
  }
 
  public Map
  messageReceived(
    BuddyPluginBuddy  buddy,
    Map          message )
  {
    buddyData buddy_data = buddyAlive( buddy );
   
    int type = ((Long)message.get( "type" )).intValue();
   
    Map msg = (Map)message.get( "msg" );
   
    return( buddy_data.receiveMessage( type, msg ));
  }
 
  public void
  messageFailed(
    BuddyPluginBuddy  buddy,
    Throwable      cause )
  {
    log( "Failed to send message to " + buddy.getName(), cause );
   
    buddyDead( buddy );
  }
 
  protected buddyData
  buddyAlive(
    BuddyPluginBuddy    buddy )
  {
    buddyData buddy_data = addBuddy( buddy );
   
    buddy_data.setAlive( true );
   
    return( buddy_data );
  }
 
  protected void
  buddyDead(
    BuddyPluginBuddy    buddy )
  {
    buddyData buddy_data = getBuddyData( buddy );

    if ( buddy_data != null ){
     
      buddy_data.setAlive( false );
    }
  }
 
  protected boolean
  okToTrack(
    Download  d )
  {
    Torrent  t = d.getTorrent();
   
    if ( t == null ){
     
      return( false );
    }
   
      // only track private torrents if we have successfully received peers from tracker
      // which means we have the torrent legitimately. As this rule is enforced by both
      // ends of the tracking operation it means we will only track between peers that
      // both have a legitimate copy of the torrent.
       
    if ( t.isPrivate()){
     
      DownloadAnnounceResult announce = d.getLastAnnounceResult();

      if (   announce == null ||
          announce.getResponseType() != DownloadAnnounceResult.RT_SUCCESS ||
          announce.getPeers().length < 2 ){
       
        return( false );
      }
    }
   
    int state = d.getState();
   
    return(   state != Download.ST_ERROR &&
          state != Download.ST_STOPPING &&
          state != Download.ST_STOPPED );
  }
 
  protected void
  log(
    String    str )
  {
    plugin.log( "Tracker: " + str );
  }
 
  protected void
  log(
    String    str,
    boolean    verbose )
  {
    if ( verbose ){
     
      if ( Constants.isCVSVersion()){
     
        log( str );
      }
    }else{
     
      log( str );
    }
  }
 
  protected void
  log(
    String    str,
    Throwable   e )
  {
    plugin.log( "Tracker: " + str, e );
  }
 
  private class
  buddyData
  {
    private BuddyPluginBuddy    buddy;
   
    private Set  downloads_sent;
    private int  downloads_sent_id;
   
    private Map    downloads_in_common;
    private boolean  buddy_seeding_only;
   
    private int    consecutive_fails;
    private long  last_fail;
   
    private String  current_ip;
   
    protected
    buddyData(
      BuddyPluginBuddy    _buddy )
    {
      buddy  = _buddy;
    }
   
    protected void
    updateIP()
    {
      current_ip  = buddy.getAdjustedIP().getHostAddress();
     
      log( "IP set to " + current_ip );
    }
     
    protected boolean
    hasIPChanged()
    {
      InetAddress  latest_ip = buddy.getAdjustedIP();
     
      if ( latest_ip == null && current_ip == null ){
       
        return( false );
       
      }else if ( latest_ip == null || current_ip == null ){
       
        return( true );
       
      }else{
     
        return!current_ip.equals( latest_ip.getHostAddress()));
      }
    }
   
    protected String
    getIP()
    {
      return( current_ip );
    }
   
    protected boolean
    hasDownloadsInCommon()
    {
      synchronized( this ){
     
        return( downloads_in_common != null );
      }
    }
   
    protected void
    setAlive(
      boolean    alive )
    {
      synchronized( this ){
       
        if ( alive ){
         
          consecutive_fails    = 0;
          last_fail        = 0;

        }else{
         
          consecutive_fails++;
         
          last_fail  = SystemTime.getMonotonousTime();
        }
      }
    }
   
    protected void
    updateLocal(
      Set    downloads,
      int    id,
      Map    diff_map )
    {
      if ( consecutive_fails > 0 ){
       
        long  retry_millis = RETRY_SEND_MIN;
       
        for (int i=0;i<consecutive_fails-1;i++){
         
          retry_millis <<= 2;
         
          if ( retry_millis > RETRY_SEND_MAX ){
           
            retry_millis = RETRY_SEND_MAX;
           
            break;
          }
        }
       
        long  now = SystemTime.getMonotonousTime();
       
        if ( now - last_fail >= retry_millis ){
         
          last_fail      = now;  // assume we're going to fail so we avoid
                        // falling through here multiple times before
                        // actuallt failing again
         
          downloads_sent     = null;
          downloads_sent_id  = 0;
        }
      }
     
        // first check to see if completion state changed for any common downloads
     
      List  comp_changed = new ArrayList();
     
      synchronized( this ){
       
        if ( downloads_in_common != null ){
         
          Iterator it = downloads_in_common.entrySet().iterator();
         
          while( it.hasNext()){
           
            Map.Entry  entry = (Map.Entry)it.next();
           
            Download d = (Download)entry.getKey();

            buddyDownloadData  bdd = (buddyDownloadData)entry.getValue();
           
            boolean  local_complete = d.isComplete( false );
           
            if ( local_complete != bdd.isLocalComplete()){
             
              bdd.setLocalComplete( local_complete );
             
              comp_changed.add( d );
            }
          }
        }
      }
     
      if ( comp_changed.size() > 0 ){
       
        byte[][] change_details = exportFullIDs( comp_changed );
       
        if( change_details[0].length > 0 ){
         
          Map  msg = new HashMap();
                   
          msg.put( "seeding", new Long( seeding_only?1:0 ));
         
          msg.put( "change",     change_details[0] );
          msg.put( "change_s",   change_details[1] );

          sendMessage( buddy, REQUEST_TRACKER_CHANGE, msg );
        }
      }
     
      if ( id == downloads_sent_id ){
       
        return;
      }
     
      Long  key = new Long(((long)id) << 32 | (long)downloads_sent_id);
       
      Object[]  diffs = (Object[])diff_map.get( key );
     
      boolean  incremental = downloads_sent != null;
     
      byte[]  added_bytes;
      byte[]  removed_bytes;
     
      if ( diffs == null ){
       
        List  added;
        List  removed  = new ArrayList();
       

        if ( downloads_sent == null ){
         
          added   = new ArrayList( downloads );
         
        }else{
         
          added  = new ArrayList();

          Iterator  it1 = downloads.iterator();
         
          while( it1.hasNext()){
         
            Download download = (Download)it1.next();
           
            if ( okToTrack( download )){
             
              if ( !downloads_sent.contains( download )){
               
                added.add( download );
              }
            }
          }
         
          Iterator  it2 = downloads_sent.iterator();
         
          while( it2.hasNext()){
         
            Download download = (Download)it2.next();
           
            if ( !downloads.contains( download )){
             
              removed.add( download );
            }
          }
        }
       
        added_bytes   = exportShortIDs( added );
        removed_bytes  = exportFullIDs( removed )[0];
       
        diff_map.put( key, new Object[]{ added_bytes, removed_bytes });
      }else{
       
        added_bytes   = (byte[])diffs[0];
        removed_bytes   = (byte[])diffs[1];
      }
       
      downloads_sent     = downloads;
      downloads_sent_id  = id;
     
      if ( added_bytes.length == 0 && removed_bytes.length == 0 ){
       
        return;
      }
     
      Map  msg = new HashMap();
     
      if ( added_bytes.length > 0 ){
       
        msg.put( "added",   added_bytes );
      }
     
      if ( removed_bytes.length > 0 ){
       
        msg.put( "removed", removed_bytes );
      }
     
      msg.put( "inc",   new Long( incremental?1:0 ));
      msg.put( "seeding", new Long( seeding_only?1:0 ));
     
      sendMessage( buddy, REQUEST_TRACKER_SUMMARY, msg );
   
   
    protected Map
    updateRemote(
      Map    msg )
    {     
      List  added   = importShortIDs((byte[])msg.get( "added" ));
     
      Map  reply = new HashMap();
     
      byte[][] add_details = exportFullIDs( added );
     
      if( add_details[0].length > 0 ){
     
        reply.put( "added",   add_details[0] );
        reply.put( "added_s",   add_details[1] );
      }
     
      synchronized( this ){

        if ( downloads_in_common != null ){

          Map removed = importFullIDs( (byte[])msg.get( "removed" ), null );
         
          Iterator it = removed.keySet().iterator();
         
          while( it.hasNext()){
           
            Download d = (Download)it.next();
           
            if ( downloads_in_common.remove( d ) != null ){
             
              log( "Removed " + d.getName() + " common download", false, true );
            }
          }
       
          if ( downloads_in_common.size() == 0 ){
           
            downloads_in_common = null;
          }
        }
      }
     
      return( reply );
    }
   
    protected void
    updateCommonDownloads(
      Map      downloads,
      boolean    incremental )
    {
      synchronized( this ){

        if ( downloads_in_common == null ){
         
          downloads_in_common = new HashMap();
         
        }else{
         
            // if not incremental then remove any downloads that no longer
            // are in common
         
          if ( !incremental ){
           
            Iterator it = downloads_in_common.keySet().iterator();
           
            while( it.hasNext()){
             
              Download download = (Download)it.next();
             
              if ( !downloads.containsKey( download )){
               
                log( "Removing " + download.getName() + " from common downloads", false, true );

                it.remove();
              }
            }
          }
        }
       
        Iterator it = downloads.entrySet().iterator();
       
        while( it.hasNext()){
         
          Map.Entry  entry = (Map.Entry)it.next();
     
          Download d = (Download)entry.getKey();

          buddyDownloadData  bdd = (buddyDownloadData)entry.getValue();
         
          buddyDownloadData existing = (buddyDownloadData)downloads_in_common.get( d );
         
          if ( existing == null ){
           
            log( "Adding " + d.getName() + " to common downloads (bdd=" + bdd.getString() + ")", false, true );
           
            downloads_in_common.put( d, bdd );
           
          }else{
           
            boolean  old_rc = existing.isRemoteComplete();
            boolean  new_rc = bdd.isRemoteComplete();
           
            if ( old_rc != new_rc ){
           
              existing.setRemoteComplete( new_rc );
             
              log( "Changing " + d.getName() + " common downloads (bdd=" + existing.getString() + ")", false, true );
            }       
          }
        }
       
        if ( downloads_in_common.size() == 0 ){
         
          downloads_in_common = null;
        }
      }
    }
   
    protected void
    updateStatus()
    {
      Map  msg = new HashMap();
     
      msg.put( "seeding", new Long( seeding_only?1:0 ));
     
      sendMessage( buddy, REQUEST_TRACKER_STATUS, msg );
    }
   
    protected Map
    receiveMessage(
      int      type,
      Map      msg_in )
    {
      int  reply_type  = -1;
      Map  msg_out    = null;

      Long  l_seeding = (Long)msg_in.get( "seeding" );
     
      if( l_seeding != null ){
       
        boolean old = buddy_seeding_only;
       
        buddy_seeding_only = l_seeding.intValue() == 1;
       
        if ( old != buddy_seeding_only ){
         
          log( "Seeding only changed to " + buddy_seeding_only );
        }
      }
     
      if ( type == REQUEST_TRACKER_SUMMARY ){
   
        reply_type  = REPLY_TRACKER_SUMMARY;
       
        msg_out = updateRemote( msg_in );
       
        msg_out.put( "inc", msg_in.get( "inc" ));
               
      }else if ( type == REQUEST_TRACKER_STATUS ){
       
        reply_type  = REPLY_TRACKER_STATUS;
       
      }else if ( type == REQUEST_TRACKER_CHANGE ){

        reply_type  = REPLY_TRACKER_STATUS;
                 
        Map downloads = importFullIDs( (byte[])msg_in.get( "changed" ), (byte[])msg_in.get( "changed_s" ) );

        updateCommonDownloads( downloads, true );
               
      }else if ( type == REQUEST_TRACKER_ADD ){

        reply_type  = REPLY_TRACKER_ADD;
                   
        Map downloads = importFullIDs( (byte[])msg_in.get( "added" ), (byte[])msg_in.get( "added_s" ) );

        updateCommonDownloads( downloads, true );

      }else if ( type == REPLY_TRACKER_SUMMARY ){
       
          // full hashes on reply
       
        byte[]  possible_matches     = (byte[])msg_in.get( "added" );
        byte[]  possible_match_states   = (byte[])msg_in.get( "added_s" );

        boolean  incremental = ((Long)msg_in.get( "inc" )).intValue() == 1;
       
        if ( possible_matches != null && possible_match_states != null ){
             
          Map downloads = importFullIDs( possible_matches, possible_match_states );
           
          if ( downloads.size() > 0 ){
                       
            updateCommonDownloads( downloads, incremental );
           
            byte[][] common_details = exportFullIDs( new ArrayList( downloads.keySet()));
           
            if( common_details[0].length > 0 ){
             
              Map  msg = new HashMap();
                       
              msg.put( "seeding", new Long( seeding_only?1:0 ));
             
              msg.put( "added",   common_details[0] );
              msg.put( "added_s", common_details[1] );

              sendMessage( buddy, REQUEST_TRACKER_ADD, msg );
            }
          }
        }
               
      }else if (   type == REPLY_TRACKER_CHANGE ||
            type == REPLY_TRACKER_STATUS ||
            type == REPLY_TRACKER_ADD ){
       
          // nothing interesting in reply for these
      }else{
       
        log( "Unrecognised type " + type );
      }
     
      if ( reply_type != -1 ){
       
        Map  reply = new HashMap();
     
        reply.put( "type", new Long( reply_type ));
   
        if ( msg_out == null ){
         
          msg_out = new HashMap();
        }
       
        msg_out.put( "seeding", new Long( seeding_only?1:0 ));
     
        reply.put( "msg", msg_out );

        return( reply );
      }
     
      return( null );
    }
   
    protected byte[]
    exportShortIDs(
      List  downloads )
    {
      byte[]  res = new byte[ SHORT_ID_SIZE * downloads.size() ];
     
      for (int i=0;i<downloads.size();i++ ){
       
        Download download = (Download)downloads.get(i);
       
        downloadData download_data = (downloadData)download.getUserData( BuddyPluginTracker.class );
       
        if ( download_data != null ){

          System.arraycopy(
            download_data.getID().getBytes(),
            0,
            res,
            i * SHORT_ID_SIZE,
            SHORT_ID_SIZE );
        }
      }
     
      return( res );
    }
   
    protected List
    importShortIDs(
      byte[]    ids )
    {
      List  res = new ArrayList();
     
      if ( ids != null ){
       
        synchronized( tracked_downloads ){

          for (int i=0;i<ids.length;i+= SHORT_ID_SIZE ){
         
            List dls = (List)short_id_map.get( new HashWrapper( ids, i, SHORT_ID_SIZE ));
           
            if ( dls != null ){
             
              res.addAll( dls );
            }
          }
        }
      }
     
      return( res );
    }
   
    protected byte[][]
       exportFullIDs(
         List  downloads )
       {
         byte[]  hashes   = new byte[ FULL_ID_SIZE * downloads.size() ];
         byte[]   states  = new byte[ downloads.size()];
        
         for (int i=0;i<downloads.size();i++ ){
          
           Download download = (Download)downloads.get(i);
          
           downloadData download_data = (downloadData)download.getUserData( BuddyPluginTracker.class );
          
           if ( download_data != null ){

             System.arraycopy(
               download_data.getID().getBytes(),
               0,
               hashes,
               i * FULL_ID_SIZE,
               FULL_ID_SIZE );
            
             states[i] = download.isComplete( false )?(byte)0x01:(byte)0x00;
           }
         }
        
         return( new byte[][]{ hashes, states });
       }
   
    protected Map
    importFullIDs(
      byte[]    ids,
      byte[]    states )
    {
      Map  res = new HashMap();
     
      if ( ids != null ){
       
        synchronized( tracked_downloads ){

          for (int i=0;i<ids.length;i+= FULL_ID_SIZE ){
         
            Download dl = (Download)full_id_map.get( new HashWrapper( ids, i, FULL_ID_SIZE ));
           
            if ( dl != null ){
             
              buddyDownloadData bdd = new buddyDownloadData( dl );
             
              if ( states != null ){
             
                bdd.setRemoteComplete(( states[i/FULL_ID_SIZE] & 0x01 ) != 0 );
              }
             
              res.put( dl, bdd );
            }
          }
        }
      }
     
      return( res );
    }
   
    protected Map
    getDownloadsToTrack()
    {
      Map  res = new HashMap();

      if ( seeding_only == buddy_seeding_only ){
       
        log( "Not tracking, buddy and me both " + (seeding_only?"seeding":"downloading"), true, false );

        return( res );
      }     
   
      long  now = SystemTime.getMonotonousTime();
     
      synchronized( this ){

        if ( downloads_in_common == null ){
         
          log( "Not tracking, buddy has nothing in common", true, false );

          return( res );
        }
       
        Iterator it = downloads_in_common.entrySet().iterator();
       
        while( it.hasNext()){
         
          Map.Entry  entry = (Map.Entry)it.next();
     
          Download d = (Download)entry.getKey();

          buddyDownloadData  bdd = (buddyDownloadData)entry.getValue();
         
          if ( d.isComplete( false ) && bdd.isRemoteComplete()){
           
              // both complete, nothing to do!
           
            log( d.getName() + " - not tracking, both complete", true, true );
           
          }else{
           
            long  last_track = bdd.getTrackTime();
           
            if (   last_track == 0 ||
                now - last_track >= TRACK_INTERVAL ){
             
              log( d.getName() + " - tracking", false, true );

              bdd.setTrackTime( now );
             
              res.put( d, new Boolean( true ));
             
            }else{
             
              res.put( d, new Boolean( false ));
            }
          }
        }
      }
     
      return( res );
    }
   
    protected void
    resetTracking(
      Download    download )
    {
      synchronized( this ){
       
        if ( downloads_in_common == null ){
         
          return;
        }
       
        buddyDownloadData bdd = (buddyDownloadData)downloads_in_common.get( download );
       
        if ( bdd != null ){
         
          bdd.resetTrackTime();
        }
      }
    }
   
    protected void
    log(
      String  str )
    {
      BuddyPluginTracker.this.log( buddy.getName() + ": " + str );
    }
   
    protected void
    log(
      String  str,
      boolean  verbose,
      boolean  no_buddy )
    {
      BuddyPluginTracker.this.log( (no_buddy?"":( buddy.getName() + ": ")) + str, verbose );
    }
  }
 
  private static class
  buddyDownloadData
  {
    private boolean  local_is_complete;
    private boolean  remote_is_complete;
    private long  last_track;
   
    protected
    buddyDownloadData(
      Download    download )
    {
      local_is_complete = download.isComplete( false );
    }
   
    protected void
    setLocalComplete(
      boolean    b )
    {
      local_is_complete  = b;
    }
 
    protected boolean
    isLocalComplete()
    {
      return( local_is_complete );
    }
   
    protected void
    setRemoteComplete(
      boolean    b )
    {
      remote_is_complete  = b;
    }
 
    protected boolean
    isRemoteComplete()
    {
      return( remote_is_complete );
    }
   
    protected void
    setTrackTime(
      long  time )
    {
      last_track  = time;
    }
   
    protected long
    getTrackTime()
    {
      return( last_track );
    }
   
    protected void
    resetTrackTime()
    {
      last_track  = 0;
    }
   
    protected String
    getString()
    {
      return( "lic=" + local_is_complete + ",ric=" + remote_is_complete + ",lt=" + last_track );
    }
  }
 
  private static class
  downloadData
  {
    private static final byte[]  IV = {(byte)0x7A, (byte)0x7A, (byte)0xAD, (byte)0xAB, (byte)0x8E, (byte)0xBF, (byte)0xCD, (byte)0x39, (byte)0x87, (byte)0x0, (byte)0xA4, (byte)0xB8, (byte)0xFE, (byte)0x40, (byte)0xA2, (byte)0xE8 };
     
    private HashWrapper  id;
   
    protected
    downloadData(
      Download  download )
    {
      Torrent t = download.getTorrent();
     
      if ( t != null ){
       
        byte[]  hash = t.getHash();
       
        SHA1  sha1 = new SHA1();
     
        sha1.update( ByteBuffer.wrap( IV ));
        sha1.update( ByteBuffer.wrap( hash ));
       
        id = new HashWrapper( sha1.digest() );
      }
    }
   
    protected HashWrapper
    getID()
    {
      return( id );
    }
  }
}
TOP

Related Classes of com.aelitis.azureus.plugins.net.buddy.tracker.BuddyPluginTracker

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.