Package org.gudy.azureus2.core3.tracker.client.impl

Source Code of org.gudy.azureus2.core3.tracker.client.impl.TRTrackerAnnouncerMuxer

/*
* Created on Dec 4, 2009
* Created by Paul Gardner
*
* Copyright 2009 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 org.gudy.azureus2.core3.tracker.client.impl;

import java.net.URL;
import java.util.*;

import org.gudy.azureus2.core3.logging.LogEvent;
import org.gudy.azureus2.core3.logging.Logger;
import org.gudy.azureus2.core3.peer.PEPeerSource;
import org.gudy.azureus2.core3.torrent.TOTorrent;
import org.gudy.azureus2.core3.torrent.TOTorrentAnnounceURLSet;
import org.gudy.azureus2.core3.torrent.TOTorrentException;
import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncer;
import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncerDataProvider;
import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncerException;
import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncerResponse;
import org.gudy.azureus2.core3.tracker.client.impl.bt.TRTrackerBTAnnouncerImpl;
import org.gudy.azureus2.core3.tracker.client.impl.dht.TRTrackerDHTAnnouncerImpl;
import org.gudy.azureus2.core3.util.AEThread2;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.IndentWriter;
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.TorrentUtils;
import org.gudy.azureus2.plugins.download.DownloadAnnounceResult;

import com.aelitis.azureus.core.tracker.TrackerPeerSource;
import com.aelitis.azureus.core.util.CopyOnWriteList;

public class
TRTrackerAnnouncerMuxer
  extends TRTrackerAnnouncerImpl
{
  private static final int ACT_CHECK_INIT_DELAY      = 2500;
  private static final int ACT_CHECK_INTERIM_DELAY    = 10*1000;
  private static final int ACT_CHECK_IDLE_DELAY      = 30*1000;
  private static final int ACT_CHECK_SEEDING_SHORT_DELAY    = 60*1000;
  private static final int ACT_CHECK_SEEDING_LONG_DELAY    = 3*60*1000;
 
 
  private String[]      networks;
  private boolean        is_manual;
 
  private long        create_time = SystemTime.getMonotonousTime();
 
  private CopyOnWriteList<TRTrackerAnnouncerHelper>  announcers   = new CopyOnWriteList<TRTrackerAnnouncerHelper>();
  private Set<TRTrackerAnnouncerHelper>        activated  = new HashSet<TRTrackerAnnouncerHelper>();
  private long                    last_activation_time;
  private Set<String>                  failed_urls  = new HashSet<String>();
 
  private volatile TimerEvent          event;
 
  private TRTrackerAnnouncerDataProvider    provider;
  private String                ip_override;
  private boolean                complete;
  private boolean                stopped;
  private boolean                destroyed;
 
  private TRTrackerAnnouncerHelper      last_best_active;
  private long                last_best_active_set_time;
 
  private Map<String,StatusSummary>      recent_responses = new HashMap<String,StatusSummary>();
 
  private TRTrackerAnnouncerResponse      last_response_informed;

 
  protected
  TRTrackerAnnouncerMuxer(
    TOTorrent    _torrent,
    String[]    _networks,
    boolean      _manual )
 
    throws TRTrackerAnnouncerException
  {
    super( _torrent );
   
    try
      last_response_informed = new TRTrackerAnnouncerResponseImpl( null, _torrent.getHashWrapper(), TRTrackerAnnouncerResponse.ST_OFFLINE, TRTrackerAnnouncer.REFRESH_MINIMUM_SECS, "Initialising" );
     
    }catch( TOTorrentException e ){
     
      Logger.log(new LogEvent( _torrent, LOGID, "Torrent hash retrieval fails", e));
     
      throw( new TRTrackerAnnouncerException( "TRTrackerAnnouncer: URL encode fails"))
    }

    networks  = _networks;
    is_manual   = _manual;
     
    split();
  }
 
  protected void
  split()
 
    throws TRTrackerAnnouncerException
  {
    TRTrackerAnnouncerHelper to_activate = null;
   
    synchronized( this ){
     
      if ( stopped || destroyed ){
       
        return;
      }
     
      TOTorrent torrent = getTorrent();
         
      TOTorrentAnnounceURLSet[]  sets = torrent.getAnnounceURLGroup().getAnnounceURLSets();
     
        // sanitise dht entries
     
      if ( sets.length == 0 ){
       
        sets = new TOTorrentAnnounceURLSet[]{ torrent.getAnnounceURLGroup().createAnnounceURLSet( new URL[]{ torrent.getAnnounceURL()})};
       
      }else{
       
        boolean  found_decentralised = false;
        boolean  modified      = false;
       
        for ( int i=0;i<sets.length;i++ ){
         
          TOTorrentAnnounceURLSet set = sets[i];
         
          URL[] urls = set.getAnnounceURLs().clone();
         
          for (int j=0;j<urls.length;j++){
           
            URL u = urls[j];
           
            if ( u != null && TorrentUtils.isDecentralised( u )){
                           
              if ( found_decentralised ){
               
                modified = true;
               
                urls[j] = null;
               
              }else{
               
                found_decentralised = true;
              }
            }
          }
        }
       
        if ( modified ){
         
          List<TOTorrentAnnounceURLSet> s_list = new ArrayList<TOTorrentAnnounceURLSet>();
         
          for ( TOTorrentAnnounceURLSet set: sets ){
           
            URL[] urls = set.getAnnounceURLs();
           
            List<URL> u_list = new ArrayList<URL>( urls.length );
           
            for ( URL u: urls ){
             
              if ( u != null ){
               
                u_list.add( u );
              }
            }
           
            if ( u_list.size() > 0 ){
             
              s_list.add( torrent.getAnnounceURLGroup().createAnnounceURLSet( u_list.toArray( new URL[ u_list.size() ])));
            }
          }
         
          sets = s_list.toArray( new TOTorrentAnnounceURLSet[ s_list.size() ]);
        }
      }
     
      List<TOTorrentAnnounceURLSet[]>  new_sets = new ArrayList<TOTorrentAnnounceURLSet[]>();
     
      if ( is_manual || sets.length < 2 ){
         
        new_sets.add( sets );
       
      }else{
       
        List<TOTorrentAnnounceURLSet> list = new ArrayList<TOTorrentAnnounceURLSet>( Arrays.asList( sets ));
       
          // often we have http:/xxxx/ and udp:/xxxx/ as separate groups - keep these together
               
        while( list.size() > 0 ){
         
          TOTorrentAnnounceURLSet set1 = list.remove(0);
         
          boolean  done = false;
         
          URL[] urls1 = set1.getAnnounceURLs();
         
          if ( urls1.length == 1 ){
           
            URL url1 = urls1[0];
           
            String prot1 = url1.getProtocol().toLowerCase();
            String host1  = url1.getHost();
           
            for (int i=0;i<list.size();i++){
             
              TOTorrentAnnounceURLSet set2 = list.get(i);
             
              URL[] urls2 = set2.getAnnounceURLs();
             
              if ( urls2.length == 1 ){
               
                URL url2 = urls2[0];
               
                String prot2 = url2.getProtocol().toLowerCase();
                String host2 = url2.getHost();
       
                if ( host1.equals( host2 )){
                 
                  if (  ( prot1.equals( "udp" ) && prot2.startsWith( "http" )) ||
                      ( prot2.equals( "udp" ) && prot1.startsWith( "http" ))){
                   
                    list.remove( i );
                   
                    new_sets.add( new TOTorrentAnnounceURLSet[]{ set1, set2 });
                   
                    done  = true;
                  }
                }
              }
            }
          }
         
          if ( !done ){
           
            new_sets.add( new TOTorrentAnnounceURLSet[]{ set1 });
          }
        }
      }
     
        // work out the difference
     
      Iterator<TOTorrentAnnounceURLSet[]> ns_it = new_sets.iterator();
     
      List<TRTrackerAnnouncerHelper> existing_announcers   = announcers.getList();
      List<TRTrackerAnnouncerHelper> new_announcers     = new ArrayList<TRTrackerAnnouncerHelper>();
     
        // first look for unchanged sets
     
      while( ns_it.hasNext()){
       
        TOTorrentAnnounceURLSet[] ns = ns_it.next();
       
        Iterator<TRTrackerAnnouncerHelper> a_it = existing_announcers.iterator();
         
        while( a_it.hasNext()){
         
          TRTrackerAnnouncerHelper a = a_it.next();
         
          TOTorrentAnnounceURLSet[] os = a.getAnnounceSets();
         
          if ( same( ns, os )){
           
            ns_it.remove();
            a_it.remove();
           
            new_announcers.add( a );
           
            break;
          }
        }
      }
         
        // reuse existing announcers
     
        // first remove dht ones from the equation
     
      TRTrackerAnnouncerHelper   existing_dht_announcer   = null;
      TOTorrentAnnounceURLSet[]  new_dht_set        = null;

      ns_it = new_sets.iterator();
     
      while( ns_it.hasNext()){
       
        TOTorrentAnnounceURLSet[] x = ns_it.next();
       
        if ( TorrentUtils.isDecentralised( x[0].getAnnounceURLs()[0])){
         
          new_dht_set = x;
           
          ns_it.remove();
         
          break;
        }
      }
     
      Iterator<TRTrackerAnnouncerHelper>  an_it = existing_announcers.iterator();
     
      while( an_it.hasNext()){
       
        TRTrackerAnnouncerHelper a = an_it.next();
       
        TOTorrentAnnounceURLSet[] x = a.getAnnounceSets();
       
        if ( TorrentUtils.isDecentralised( x[0].getAnnounceURLs()[0])){
         
          existing_dht_announcer = a;
           
          an_it.remove();
         
          break;
        }
      }
 
      if ( existing_dht_announcer != null && new_dht_set != null ){
       
        new_announcers.add( existing_dht_announcer );
       
      }else if ( existing_dht_announcer != null ){
       
        activated.remove( existing_dht_announcer );
       
        existing_dht_announcer.destroy();
       
      }else if ( new_dht_set != null ){
       
        TRTrackerAnnouncerHelper a = create( torrent, new_dht_set );
       
        new_announcers.add( a );
      }

        // now do the non-dht ones
     
      ns_it = new_sets.iterator();

      while( ns_it.hasNext() && existing_announcers.size() > 0 ){
       
        TRTrackerAnnouncerHelper a = existing_announcers.remove(0);
       
        TOTorrentAnnounceURLSet[] s = ns_it.next();
       
        ns_it.remove();
       
        if (   activated.contains( a ) &&
            torrent.getPrivate() &&
            a instanceof TRTrackerBTAnnouncerImpl ){
         
          URL url = a.getTrackerURL();
       
          if ( url != null ){
           
            forceStop((TRTrackerBTAnnouncerImpl)a, url );
          }
        }
       
        a.setAnnounceSets( s );
       
        new_announcers.add( a );
      }
     
        // create any new ones required
     
      ns_it = new_sets.iterator();
     
      while( ns_it.hasNext()){
       
        TOTorrentAnnounceURLSet[] s = ns_it.next();
       
        TRTrackerAnnouncerHelper a = create( torrent, s );
       
        new_announcers.add( a );
      }
     
        // finally fix up the announcer list to represent the new state
     
      Iterator<TRTrackerAnnouncerHelper>  a_it = announcers.iterator();
       
      while( a_it.hasNext()){
       
        TRTrackerAnnouncerHelper a = a_it.next();
       
        if ( !new_announcers.contains( a )){
         
          a_it.remove();
         
          try{
            if (   activated.contains( a ) &&
                torrent.getPrivate() &&
                a instanceof TRTrackerBTAnnouncerImpl ){
             
              URL url = a.getTrackerURL();
           
              if ( url != null ){
               
                forceStop((TRTrackerBTAnnouncerImpl)a, url );
              }
            }
          }finally{
           
            if (Logger.isEnabled()) {
              Logger.log(new LogEvent(getTorrent(), LOGID, "Deactivating " + getString( a.getAnnounceSets())));
            }

            activated.remove( a );
                       
            a.destroy();
          }
        }
      }
     
      a_it = new_announcers.iterator();
     
      while( a_it.hasNext()){
       
        TRTrackerAnnouncerHelper a = a_it.next();
       
        if ( !announcers.contains( a )){
         
          announcers.add( a );
        }
      }
     
      if ( !is_manual && announcers.size() > 0 ){
       
        if ( activated.size() == 0 ){
         
          TRTrackerAnnouncerHelper a = announcers.get(0);
         
          if (Logger.isEnabled()) {
            Logger.log(new LogEvent(getTorrent(), LOGID, "Activating " + getString( a.getAnnounceSets())));
          }

          activated.add( a );
         
          last_activation_time = SystemTime.getMonotonousTime();
         
          if ( provider != null ){
           
            to_activate = a;
          }
        }
       
        setupActivationCheck( ACT_CHECK_INIT_DELAY );
      }
    }
   
    if ( to_activate != null ){
     
      if ( complete ){
       
        to_activate.complete( true );
       
      }else{
       
        to_activate.update( false );
      }
    }
  }
 
  protected void
  setupActivationCheck(
    int    delay )
  {
    if ( announcers.size() > activated.size()){
     
      event = SimpleTimer.addEvent(
        "TRMuxer:check",
        SystemTime.getOffsetTime( delay ),
        new TimerEventPerformer()
        {
          public void
          perform(
            TimerEvent event )
          {
            checkActivation( false );
          }
        });
    }
  }
 
  protected void
  checkActivation(
    boolean    force )
  {
    synchronized( this ){
     
      int  next_check_delay;
     
      if (   destroyed ||
          stopped ||
          announcers.size() <= activated.size()){

        return;
      }
     
      if ( provider == null ){
       
        next_check_delay = ACT_CHECK_INIT_DELAY;
       
      }else{

        boolean  activate = force;
                           
        boolean  seeding = provider.getRemaining() == 0;

        if ( seeding && activated.size() > 0 ){
       
            // when seeding we only activate on tracker fail or major lack of connections
            // as normally we rely on downloaders rotating and finding us

          int  connected  = provider.getConnectedConnectionCount();

          if ( connected < 1 ){
           
            activate = SystemTime.getMonotonousTime() - last_activation_time >= 60*1000;
           
            next_check_delay = ACT_CHECK_SEEDING_SHORT_DELAY;
           
          }else if ( connected < 3 ){
           
            next_check_delay = ACT_CHECK_SEEDING_LONG_DELAY;
           
          }else{
           
            next_check_delay = 0;
          }
        }else{
         
          int  allowed    = provider.getMaxNewConnectionsAllowed()
          int  pending    = provider.getPendingConnectionCount();
          int  connected  = provider.getConnectedConnectionCount();
         
          int  online = 0;
         
          for ( TRTrackerAnnouncerHelper a: activated ){
           
            TRTrackerAnnouncerResponse response = a.getLastResponse();
           
            if (   response != null &&
                response.getStatus() == TRTrackerAnnouncerResponse.ST_ONLINE ){
             
              online++;
            }
          }
         
          /*
          System.out.println(
            "checkActivation: announcers=" + announcers.size() +
            ", active=" + activated.size() +
            ", online=" + online +
            ", allowed=" + allowed +
            ", pending=" + pending +
            ", connected=" + connected +
            ", seeding=" + seeding );
          */
         
          if ( online == 0 ){
           
            activate = true;
           
              // no trackers online, start next and recheck soon
           
            next_check_delay = ACT_CHECK_INIT_DELAY;
           
          }else{
           
            int  potential = connected + pending;
           
            if ( potential < 10 ){
             
                // minimal connectivity
             
              activate = true;
             
              next_check_delay = ACT_CHECK_INIT_DELAY;

            }else if ( allowed >= 5 && pending < 3*allowed/4 ){
             
                // not enough to fulfill our needs
             
              activate = true;
             
              next_check_delay = ACT_CHECK_INTERIM_DELAY;
             
            }else{
                // things look good, recheck in a bit
             
              next_check_delay = ACT_CHECK_IDLE_DELAY;
            }
          }
        }
         
        if ( activate ){
         
          for ( TRTrackerAnnouncerHelper a: announcers ){
           
            if ( !activated.contains( a )){
             
              if (Logger.isEnabled()) {
                Logger.log(new LogEvent(getTorrent(), LOGID, "Activating " + getString( a.getAnnounceSets())));
              }
             
              activated.add( a );
             
              last_activation_time = SystemTime.getMonotonousTime();
             
              if ( complete ){
               
                a.complete( true );
               
              }else{
               
                a.update( false );
              }
             
              break;
            }
          }
        }
      }
     
      if ( next_check_delay > 0 ){
     
        setupActivationCheck( next_check_delay );
      }
    }
  }
 
  private String
  getString(
    TOTorrentAnnounceURLSet[]  sets )
  {
    StringBuffer str = new StringBuffer();
   
    str.append( "[" );
   
    int  num1 = 0;
   
    for ( TOTorrentAnnounceURLSet s: sets ){
     
      if ( num1++ > 0 ){
        str.append( ", ");
      }
     
      str.append( "[" );

      URL[]  urls = s.getAnnounceURLs();
     
      int  num2 = 0;
     
      for ( URL u: urls ){
       
        if ( num2++ > 0 ){
          str.append( ", ");
        }
       
        str.append( u.toExternalForm());
      }
     
      str.append( "]" );
    }
   
    str.append( "]" );
   
    return( str.toString());
  }
 
  private boolean
  same(
    TOTorrentAnnounceURLSet[]  s1,
    TOTorrentAnnounceURLSet[]  s2 )
  {
    boolean  res = sameSupport( s1, s2 );
   
    // System.out.println( "same->" + res + ": " + getString(s1) + "/" + getString(s2));
   
    return( res );
  }
 
  private boolean
  sameSupport(
    TOTorrentAnnounceURLSet[]  s1,
    TOTorrentAnnounceURLSet[]  s2 )
  {
    if ( s1.length != s2.length ){
     
      return( false );
    }
   
    for (int i=0;i<s1.length;i++){
     
      URL[] u1 = s1[i].getAnnounceURLs();
      URL[] u2 = s2[i].getAnnounceURLs();
     
      if ( u1.length != u2.length ){
       
        return( false );
      }
     
      if ( u1.length == 1 ){
       
        return( u1[0].toExternalForm().equals( u2[0].toExternalForm()));
      }
     
      Set<String> set1 = new HashSet<String>();
     
      for ( URL u: u1 ){
       
        set1.add( u.toExternalForm());
      }
     
      Set<String> set2 = new HashSet<String>();
     
      for ( URL u: u2 ){
       
        set2.add( u.toExternalForm());
      }
     
      if ( !set1.equals( set2 )){
       
        return( false );
      }
    }
   
    return( true );
  }
 
  protected void
  forceStop(
    final TRTrackerBTAnnouncerImpl    announcer,
    final URL              url )
  {
    if (Logger.isEnabled()) {
      Logger.log(new LogEvent(getTorrent(), LOGID, "Force stopping " + url + " as private torrent" ));
    }
   
    new AEThread2( "TRMux:fs", true )
    {
      public void
      run()
      {
        try{
          TRTrackerBTAnnouncerImpl an =
            new TRTrackerBTAnnouncerImpl( getTorrent(), new TOTorrentAnnounceURLSet[0], networks, true, getHelper());
         
          an.cloneFrom( announcer );
         
          an.setTrackerURL( url );
         
          an.stop( false );
         
          an.destroy();
         
        }catch( Throwable e ){
         
        }
      }
    }.start();
  }
 
 
  protected TRTrackerAnnouncerHelper
  create(
    TOTorrent            torrent,
    TOTorrentAnnounceURLSet[]    sets )
 
    throws TRTrackerAnnouncerException
  {
    TRTrackerAnnouncerHelper announcer;
   
    boolean  decentralised;
   
    if ( sets.length == 0 ){
     
      decentralised = TorrentUtils.isDecentralised( torrent.getAnnounceURL());
     
    }else{
     
      decentralised = TorrentUtils.isDecentralised( sets[0].getAnnounceURLs()[0]);
    }
   
    if ( decentralised ){
     
      announcer  = new TRTrackerDHTAnnouncerImpl( torrent, networks, is_manual, getHelper());
     
    }else{
     
      announcer = new TRTrackerBTAnnouncerImpl( torrent, sets, networks, is_manual, getHelper());
    }
   
    for ( TOTorrentAnnounceURLSet set: sets ){
     
      URL[] urls = set.getAnnounceURLs();
     
      for ( URL u: urls ){
       
        String key = u.toExternalForm();
       
        StatusSummary summary = recent_responses.get( key );
       
        if ( summary == null ){
         
          summary = new StatusSummary( announcer, u );
                   
          recent_responses.put( key, summary );
         
        }else{
         
          summary.setHelper( announcer );
        }
      }
    }
   
    if ( provider != null ){
     
      announcer.setAnnounceDataProvider( provider );
    }
   
    if ( ip_override != null ){
     
      announcer.setIPOverride( ip_override );
    }
   
    return( announcer );
  }
 
 
  public TRTrackerAnnouncerResponse
  getLastResponse()
  {
    TRTrackerAnnouncerResponse  result = null;
   
    TRTrackerAnnouncerHelper best = getBestActive();
   
    if ( best != null ){
     
      result = best.getLastResponse();
    }
   
    if ( result == null ){
     
      result = last_response_informed;
    }
   
    return( result );
  }
 

  @Override
  protected void
  informResponse(
    TRTrackerAnnouncerHelper    helper,
    TRTrackerAnnouncerResponse    response )
  {
    URL  url = response.getURL();
   
      // can be null for external plugins (e.g. mldht...)
   
    if ( url != null ){
     
      synchronized( this ){
       
        String key = url.toExternalForm();
       
        StatusSummary summary = recent_responses.get( key );
     
        if ( summary != null ){
       
          summary.updateFrom( response );
        }
      }
    }
   
    last_response_informed = response;
   
      // force recalc of best active next time
   
    last_best_active_set_time = 0;
   
    super.informResponse( helper, response );
   
    if ( response.getStatus() != TRTrackerAnnouncerResponse.ST_ONLINE ){
     
      URL  u = response.getURL();
     
      if ( u != null ){
       
        String s = u.toExternalForm();
       
        synchronized( failed_urls ){
         
          if ( failed_urls.contains( s )){
           
            return;
          }
         
          failed_urls.add( s );
        }
      }
     
      checkActivation( true );
    }
  }
 
  public boolean
  isManual()
  {
    return( is_manual );
  }

  public void
  setAnnounceDataProvider(
    TRTrackerAnnouncerDataProvider    _provider )
  {
    List<TRTrackerAnnouncerHelper>  to_set;
   
    synchronized( this ){
     
      provider  = _provider;
     
      to_set = announcers.getList();
    }
   
    for ( TRTrackerAnnouncer announcer: to_set ){
   
      announcer.setAnnounceDataProvider( provider );
    }
  }
 
  protected TRTrackerAnnouncerHelper
  getBestActive()
  {
    long  now = SystemTime.getMonotonousTime();
   
    if ( now - last_best_active_set_time < 1000 ){
     
      return( last_best_active );
    }
   
    last_best_active = getBestActiveSupport();
   
    last_best_active_set_time = now;
   
    return( last_best_active );
  }
 
  protected TRTrackerAnnouncerHelper
  getBestActiveSupport()
  {
    List<TRTrackerAnnouncerHelper> x = announcers.getList();
   
    TRTrackerAnnouncerHelper error_resp = null;
   
    for ( TRTrackerAnnouncerHelper announcer: x ){
     
      TRTrackerAnnouncerResponse response = announcer.getLastResponse();
     
      if ( response != null ){
       
        int  resp_status = response.getStatus();
       
        if ( resp_status == TRTrackerAnnouncerResponse.ST_ONLINE ){
         
          return( announcer );
         
        }else if ( error_resp == null && resp_status == TRTrackerAnnouncerResponse.ST_REPORTED_ERROR ){
         
          error_resp = announcer;
        }
      }
    }
   
    if ( error_resp != null ){
     
      return( error_resp );
    }
   
    if ( x.size() > 0 ){
     
      return( x.get(0));
    }
   
    return( null );
  }
 
  public URL
  getTrackerURL()
  {
    TRTrackerAnnouncerHelper  active = getBestActive();
   
    if ( active != null ){
     
      return( active.getTrackerURL());
    }
   
    return( null );
  }
 
  public void
  setTrackerURL(
    URL    url )
  {
    List<List<String>> groups = new ArrayList<List<String>>();
   
    List<String> group = new ArrayList<String>();
   
    group.add( url.toExternalForm());
   
    groups.add( group );
   
    TorrentUtils.listToAnnounceGroups( groups, getTorrent());
   
    resetTrackerUrl( false );
  }
 
  public void
  resetTrackerUrl(
    boolean  shuffle )
  {
    try{
      split();
     
    }catch( Throwable e ){
     
      Debug.out( e );
    }
   
    for ( TRTrackerAnnouncer announcer: announcers ){
   
      announcer.resetTrackerUrl( shuffle );
    }
  }
 
  public void
  setIPOverride(
    String    override )
  {
    List<TRTrackerAnnouncerHelper>  to_set;
   
    synchronized( this ){
     
      to_set  = announcers.getList();
     
      ip_override  = override;
    }
   
    for ( TRTrackerAnnouncer announcer: to_set ){
   
      announcer.setIPOverride( override );
    }
  }
 
  public void
  clearIPOverride()
  {
    List<TRTrackerAnnouncerHelper>  to_clear;
   
    synchronized( this ){
     
      to_clear  = announcers.getList();
     
      ip_override  = null;
    }
   
    for ( TRTrackerAnnouncer announcer: to_clear ){
   
      announcer.clearIPOverride();
    }
  }
 
  public void
  setRefreshDelayOverrides(
    int    percentage )
  {
    for ( TRTrackerAnnouncer announcer: announcers ){
   
      announcer.setRefreshDelayOverrides( percentage );
    }
  }
 
  public int
  getTimeUntilNextUpdate()
  {
    TRTrackerAnnouncerHelper  active = getBestActive();

    if ( active != null ){
     
      return( active.getTimeUntilNextUpdate());
    }
   
    return( Integer.MAX_VALUE );
  }
 
  public int
  getLastUpdateTime()
  {
    TRTrackerAnnouncerHelper  active = getBestActive();

    if ( active != null ){
     
      return( active.getLastUpdateTime());
    }
   
    return( 0 );
 
 
  public void
  update(
    boolean  force )
  {
    List<TRTrackerAnnouncerHelper> to_update;
   
    synchronized( this ){
           
      to_update = is_manual?announcers.getList():new ArrayList<TRTrackerAnnouncerHelper>( activated );
    }
   
    for ( TRTrackerAnnouncer announcer: to_update ){
   
      announcer.update(force);
    }
  }
 
  public void
  complete(
    boolean  already_reported )
  {
    List<TRTrackerAnnouncerHelper> to_complete;
   
    synchronized( this ){
     
      complete  = true;
     
      to_complete = is_manual?announcers.getList():new ArrayList<TRTrackerAnnouncerHelper>( activated );
    }
   
    for ( TRTrackerAnnouncer announcer: to_complete ){
   
      announcer.complete( already_reported );
    }
  }
 
  public void
  stop(
    boolean  for_queue )
  {
    List<TRTrackerAnnouncerHelper> to_stop;
   
    synchronized( this ){
     
      stopped  = true;
     
      to_stop = is_manual?announcers.getList():new ArrayList<TRTrackerAnnouncerHelper>( activated );
     
      activated.clear();
    }
   
    for ( TRTrackerAnnouncer announcer: to_stop ){
   
      announcer.stop( for_queue );
    }
  }
 
  public void
  destroy()
  {
    TRTrackerAnnouncerFactoryImpl.destroy( this );

    List<TRTrackerAnnouncerHelper> to_destroy;
   
    synchronized( this ){
     
      destroyed = true;
     
      to_destroy = announcers.getList();
    }
   
    for ( TRTrackerAnnouncer announcer: to_destroy ){
   
      announcer.destroy();
    }
   
    TimerEvent  ev = event;
   
    if ( ev != null ){
     
      ev.cancel();
    }
  }
 
  public int
  getStatus()
  {
    TRTrackerAnnouncer  max_announcer = getBestAnnouncer();
   
    return( max_announcer==null?-1:max_announcer.getStatus());
  }
   
  public String
  getStatusString()
  {   
    TRTrackerAnnouncer  max_announcer = getBestAnnouncer();
   
    return( max_announcer==null?"":max_announcer.getStatusString());
  }
 
  public TRTrackerAnnouncer
  getBestAnnouncer()
  {
    int  max = -1;
   
    TRTrackerAnnouncer  max_announcer = null;
   
    for ( TRTrackerAnnouncer announcer: announcers ){
     
      int  status = announcer.getStatus();
     
      if ( status > max ){
       
        max_announcer   = announcer;
        max        = status;
      }
    }
   
    return( max_announcer==null?this:max_announcer );
  }
 
  public void
  refreshListeners()
  {
    informURLRefresh();
  }
 
  public void
  setAnnounceResult(
    DownloadAnnounceResult  result )
  {
      // this is only used for setting DHT results
   
    for ( TRTrackerAnnouncer announcer: announcers ){
     
      if ( announcer instanceof TRTrackerDHTAnnouncerImpl ){
       
        announcer.setAnnounceResult( result );
       
        return;
      }
    }
   
      // TODO: we should always create a DHT entry and have it denote DHT tracking for all circustances
      // have the DHT plugin set it to offline if disabled
   
    List<TRTrackerAnnouncerHelper> x = announcers.getList();
   
    if ( x.size() > 0 ){
     
      x.get(0).setAnnounceResult( result );
    }
  }
   
  protected int
  getPeerCacheLimit()
  {
    synchronized( this ){
     
      if ( activated.size() < announcers.size()){
       
        return( 0 );
      }
    }
   
    if ( SystemTime.getMonotonousTime() - create_time < 15*1000 ){
     
      return( 0 );
    }
   
    TRTrackerAnnouncer active = getBestActive();
   
    if ( active != null && provider != null && active.getStatus() == TRTrackerAnnouncerResponse.ST_ONLINE ){
     
      if (   provider.getMaxNewConnectionsAllowed() > 0 &&
          provider.getPendingConnectionCount() == 0 ){
       
        return( 5 );
       
      }else{
       
        return( 0 );
      }
    }
   
    return( 10 );
  }
 
  public TrackerPeerSource
  getTrackerPeerSource(
    final TOTorrentAnnounceURLSet    set )
  {
    URL[]  urls = set.getAnnounceURLs();
   
    final String[] url_strs = new String[ urls.length ];
   
    for ( int i=0;i<urls.length;i++ ){
     
      url_strs[i] = urls[i].toExternalForm();
    }
   
    return(
      new TrackerPeerSource()
      {
        private StatusSummary    _summary;
        private boolean        enabled;
        private long        fixup_time;
       
        private StatusSummary
        fixup()
        {
          long now = SystemTime.getMonotonousTime();
         
          if ( now - fixup_time > 1000 ){
                       
            long      most_recent  = 0;
            StatusSummary  summary     = null;
           
            synchronized( TRTrackerAnnouncerMuxer.this ){
           
              for ( String str: url_strs ){
             
                StatusSummary s = recent_responses.get( str );
               
                if ( s != null ){
                 
                  if ( summary == null || s.getTime() > most_recent ){
                   
                    summary    = s;
                    most_recent  = s.getTime();
                  }
                }
              } 
            }
           
            if ( provider != null ){
           
              enabled = provider.isPeerSourceEnabled( PEPeerSource.PS_BT_TRACKER );
            }
           
            if ( summary != null ){
             
              _summary = summary;
            }
           
            fixup_time = now;
          }
         
          return( _summary );
        }
       
        public int
        getType()
        {
          return( TrackerPeerSource.TP_TRACKER );
        }
       
        public String
        getName()
        {
          StatusSummary summary = fixup();
         
          if ( summary != null ){
           
            String str =summary.getURL().toExternalForm();
           
            int pos = str.indexOf( '?' );
           
            if ( pos != -1 ){
             
              str = str.substring( 0, pos );
            }
           
            return( str );
          }
         
          return( url_strs[0] );
        }
       
        public int
        getStatus()
        {
          StatusSummary summary = fixup();
         
          if ( !enabled ){
           
            return( ST_DISABLED );
          }
         
          if ( summary != null ){
           
            return( summary.getStatus());
          }
         
          return( ST_QUEUED );
        }
       
        public String
        getStatusString()
        {
          StatusSummary summary = fixup();
         
          if ( summary != null && enabled ){
           
            return( summary.getStatusString());
          }
         
          return( null );
        }
           
        public int
        getSeedCount()
        {
          StatusSummary summary = fixup();
         
          if ( summary != null ){
           
            return( summary.getSeedCount());
          }
         
          return( -1 );
        }
       
        public int
        getLeecherCount()
        {
          StatusSummary summary = fixup();
         
          if ( summary != null ){
           
            return( summary.getLeecherCount());
          }
         
          return( -1 );
        }     
       
        public int
        getPeers()
        {
          StatusSummary summary = fixup();
         
          if ( summary != null ){
           
            return( summary.getPeers());
          }
         
          return( -1 );
        }     

        public int
        getSecondsToUpdate()
        {
          StatusSummary summary = fixup();
         
          if ( summary != null ){
           
            return( summary.getSecondsToUpdate());
          }
         
          return( -1 );
        }
       
        public int
        getInterval()
        {
          StatusSummary summary = fixup();
         
          if ( summary != null ){
           
            return( summary.getInterval());
          }
         
          return( -1 );
        }
       
        public int
        getMinInterval()
        {
          StatusSummary summary = fixup();
         
          if ( summary != null && enabled ){
           
            return( summary.getMinInterval());
          }
         
          return( -1 );
        }
       
        public boolean
        isUpdating()
        {
          StatusSummary summary = fixup();
         
          if ( summary != null && enabled  ){
           
            return( summary.isUpdating());
          }
         
          return( false );
        }
       
        public boolean
        canManuallyUpdate()
        {
          StatusSummary summary = fixup();
         
          if ( summary == null ){
           
            return( false );
          }
         
          return( summary.canManuallyUpdate());
        }
       
        public void
        manualUpdate()
        {
          StatusSummary summary = fixup();
         
          if ( summary != null ){
           
            summary.manualUpdate();
          }
        }
      });
  }
 
  public void
  generateEvidence(
    IndentWriter writer )
  {
    for ( TRTrackerAnnouncer announcer: announcers ){
   
      announcer.generateEvidence(writer);
    }
  }
 
  private static class
  StatusSummary
  {
    private TRTrackerAnnouncerHelper    helper;
   
    private long    time;
    private URL      url;
    private int      status;
    private String    status_str;
    private int      seeds    = -1;
    private int      leechers  = -1;
    private int      peers    = -1;
   
    private int      interval;
    private int      min_interval;
   
    protected
    StatusSummary(
      TRTrackerAnnouncerHelper    _helper,
      URL                _url )
    {
      helper  = _helper;
      url    = _url;
     
      status = TrackerPeerSource.ST_QUEUED;
    }
   
    protected void
    setHelper(
      TRTrackerAnnouncerHelper    _helper )
    {
      helper  = _helper;
    }
   
    protected void
    updateFrom(
      TRTrackerAnnouncerResponse    response )
    {     
      time  = SystemTime.getMonotonousTime();
     
      int  state = response.getStatus();
     
      if ( state == TRTrackerAnnouncerResponse.ST_ONLINE ){
       
        status = TrackerPeerSource.ST_ONLINE;
     
        seeds    = response.getScrapeCompleteCount();
        leechers  = response.getScrapeIncompleteCount();
        peers    = response.getPeers().length;
       
      }else{
       
        status = TrackerPeerSource.ST_ERROR;
       
        status_str = response.getStatusString();
      }
     
      interval     = (int)helper.getInterval();
      min_interval   = (int)helper.getMinInterval();
    }
   
    public long
    getTime()
    {
      return( time );
    }
   
    public URL
    getURL()
    {
      return( url );
    }
   
    public int
    getStatus()
    {
      return( status );
    }
   
    public String
    getStatusString()
    {
      return( status_str );
    }
   
    public int
    getSeedCount()
    {
      return( seeds );
    }
   
    public int
    getLeecherCount()
    {
      return( leechers );
    }
   
    public int
    getPeers()
    {
      return( peers );
    }
   
    public boolean
    isUpdating()
    {
      return( helper.isUpdating());
    }
   
    public int
    getInterval()
    {
      return( interval );
    }
   
    public int
    getMinInterval()
    {
      return( min_interval );
    }
   
    public int
    getSecondsToUpdate()
    {
      return( helper.getTimeUntilNextUpdate());
    }
   
    public boolean
    canManuallyUpdate()
    {
      return( ((SystemTime.getCurrentTime() / 1000 - helper.getLastUpdateTime() >= TRTrackerAnnouncer.REFRESH_MINIMUM_SECS)));
    }
   
    public void
    manualUpdate()
    {
      helper.update( true );
    }
  }
}
TOP

Related Classes of org.gudy.azureus2.core3.tracker.client.impl.TRTrackerAnnouncerMuxer

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.