Package org.gudy.azureus2.core3.global.impl

Source Code of org.gudy.azureus2.core3.global.impl.GlobalManagerImpl

/*
* File    : GlobalManagerImpl.java
* Created : 21-Oct-2003
* By      : stuff
*
* Azureus - a Java Bittorrent client
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License.
*
* 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 ( see the LICENSE file ).
*
* 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.global.impl;

/*
* Created on 30 juin 2003
*
*/

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.util.*;

import org.gudy.azureus2.core3.category.Category;
import org.gudy.azureus2.core3.category.CategoryManager;
import org.gudy.azureus2.core3.config.COConfigurationManager;
import org.gudy.azureus2.core3.config.impl.TransferSpeedValidator;
import org.gudy.azureus2.core3.disk.DiskManagerFileInfo;
import org.gudy.azureus2.core3.download.*;
import org.gudy.azureus2.core3.download.impl.DownloadManagerAdapter;
import org.gudy.azureus2.core3.global.*;
import org.gudy.azureus2.core3.internat.MessageText;
import org.gudy.azureus2.core3.logging.LogEvent;
import org.gudy.azureus2.core3.logging.LogIDs;
import org.gudy.azureus2.core3.logging.Logger;
import org.gudy.azureus2.core3.peer.PEPeerManager;
import org.gudy.azureus2.core3.torrent.TOTorrent;
import org.gudy.azureus2.core3.torrent.TOTorrentException;
import org.gudy.azureus2.core3.tracker.client.*;
import org.gudy.azureus2.core3.tracker.util.TRTrackerUtils;
import org.gudy.azureus2.core3.tracker.util.TRTrackerUtilsListener;
import org.gudy.azureus2.core3.util.*;

import com.aelitis.azureus.core.AzureusCore;
import com.aelitis.azureus.core.AzureusCoreFactory;
import com.aelitis.azureus.core.helpers.TorrentFolderWatcher;
import com.aelitis.azureus.core.networkmanager.NetworkManager;
import com.aelitis.azureus.core.peermanager.control.PeerControlSchedulerFactory;
import com.aelitis.azureus.core.speedmanager.SpeedManager;
import com.aelitis.azureus.core.speedmanager.impl.SpeedManagerImpl;
import com.aelitis.azureus.core.util.CopyOnWriteList;

import org.gudy.azureus2.plugins.network.ConnectionManager;
import org.gudy.azureus2.plugins.dht.mainline.MainlineDHTProvider;


/**
* @author Olivier
*
*/
public class GlobalManagerImpl
  extends DownloadManagerAdapter
  implements   GlobalManager, AEDiagnosticsEvidenceGenerator
{
  private static final LogIDs LOGID = LogIDs.CORE;
 
    // GlobalManagerListener support
    // Must be an async listener to support the non-synchronised invocation of
    // listeners when a new listener is added and existing downloads need to be
    // reported
 
  private static final int LDT_MANAGER_ADDED      = 1;
  private static final int LDT_MANAGER_REMOVED    = 2;
  private static final int LDT_DESTROY_INITIATED    = 3;
  private static final int LDT_DESTROYED        = 4;
    private static final int LDT_SEEDING_ONLY           = 5;
 
  private ListenerManager  listeners   = ListenerManager.createAsyncManager(
    "GM:ListenDispatcher",
    new ListenerManagerDispatcher()
    {
      public void
      dispatch(
        Object    _listener,
        int      type,
        Object    value )
      {
        GlobalManagerListener  target = (GlobalManagerListener)_listener;
   
        if ( type == LDT_MANAGER_ADDED ){
         
          target.downloadManagerAdded((DownloadManager)value);
         
        }else if ( type == LDT_MANAGER_REMOVED ){
         
          target.downloadManagerRemoved((DownloadManager)value);
         
        }else if ( type == LDT_DESTROY_INITIATED ){
         
          target.destroyInitiated();
         
        }else if ( type == LDT_DESTROYED ){
         
          target.destroyed();
                   
        }else if ( type == LDT_SEEDING_ONLY ){
                   
          boolean  [] temp = (boolean[])value;
         
                    target.seedingStatusChanged( temp[0], temp[1] );
                }
      }
    });
 
    // GlobalManagerDownloadWillBeRemovedListener support
    // Not async (doesn't need to be and can't be anyway coz it has an exception)
 
  private static final int LDT_MANAGER_WBR      = 1;
 
  private ListenerManager  removal_listeners   = ListenerManager.createManager(
      "GM:DLWBRMListenDispatcher",
      new ListenerManagerDispatcherWithException()
      {
        public void
        dispatchWithException(
          Object    _listener,
          int      type,
          Object    value )
       
          throws GlobalManagerDownloadRemovalVetoException
        {         
          GlobalManagerDownloadWillBeRemovedListener  target = (GlobalManagerDownloadWillBeRemovedListener)_listener;
         
          DownloadManager dm = (DownloadManager) ((Object[])value)[0];
          boolean remove_torrent = ((Boolean) ((Object[])value)[1]).booleanValue();
          boolean remove_data = ((Boolean) ((Object[])value)[2]).booleanValue();
         
          target.downloadWillBeRemoved(dm, remove_torrent, remove_data);
        }
      });
 
  private List<DownloadManager>     managers_cow  = new ArrayList<DownloadManager>();
  private AEMonitor  managers_mon  = new AEMonitor( "GM:Managers" );
 
  private Map    manager_map      = new HashMap();
   
  private GlobalMangerProgressListener  progress_listener;
 
  private Checker checker;
  private GlobalManagerStatsImpl    stats;
    private long last_swarm_stats_calc_time    = 0;
    private long last_swarm_stats        = 0;
   
    // Set this flag to disable interaction with downloads.config.
    // Do *NOT* change this - only the constructor should set it once.
  private boolean cripple_downloads_config;

  private TRTrackerScraper       trackerScraper;
  private GlobalManagerStatsWriter   stats_writer;
  private GlobalManagerHostSupport  host_support;
 
  private Map              saved_download_manager_state  = new HashMap();
 
  private int              next_seed_piece_recheck_index;
 
  private TorrentFolderWatcher torrent_folder_watcher;
 
  private ArrayList paused_list = new ArrayList();
  private final AEMonitor paused_list_mon = new AEMonitor( "GlobalManager:PL" );
 
 
 
  /* Whether the GlobalManager is active (false) or stopped (true) */
 
  private volatile boolean   isStopping;
  private volatile boolean  destroyed;
  private volatile boolean   needsSaving = false;
 
  private boolean seeding_only_mode         = false;
  private boolean potentially_seeding_only_mode  = false;
 
  private FrequencyLimitedDispatcher  check_seeding_only_state_dispatcher =
    new FrequencyLimitedDispatcher(
      new AERunnable(){ public void runSupport(){ checkSeedingOnlyStateSupport(); }}, 5000 );
 
  private boolean  force_start_non_seed_exists;
  private int   nat_status        = ConnectionManager.NAT_UNKNOWN;
  private boolean  nat_status_probably_ok;
   
   private CopyOnWriteList  dm_adapters = new CopyOnWriteList();

   /** delay loading of torrents */
   DelayedEvent loadTorrentsDelay = null;
   /** Whether loading of existing torrents is done */
   boolean loadingComplete = false;
   /** Monitor to block adding torrents while loading existing torrent list */
   AESemaphore loadingSem = new AESemaphore("Loading Torrents");

   AEMonitor addingDM_monitor = new AEMonitor("addingDM");
   /** List of torrents being added, but not added to the GM list yet */
   List addingDMs = new ArrayList();
 
   private MainlineDHTProvider provider = null;
  
   public class Checker extends AEThread {
    int loopFactor;
    private static final int waitTime = 10*1000;
    // 5 minutes save resume data interval (default)
    private int saveResumeLoopCount   = 5*60*1000 / waitTime;
    private int initSaveResumeLoopCount = 60*1000 / waitTime;
    private int natCheckLoopCount    = 30*1000 / waitTime;
    private int seedPieceCheckCount    = 30*1000 / waitTime;
          
    private AESemaphore  run_sem = new AESemaphore( "GM:Checker:run");
   

     public Checker() {
      super("Global Status Checker");
      loopFactor = 0;
      setPriority(Thread.MIN_PRIORITY);
      //determineSaveResumeDataInterval();
    }

    private void determineSaveResumeDataInterval() {
      int saveResumeInterval = COConfigurationManager.getIntParameter("Save Resume Interval", 5);
      if (saveResumeInterval >= 1 && saveResumeInterval <= 90)
        saveResumeLoopCount = saveResumeInterval * 60000 / waitTime;
    }

    public void
  runSupport()
    {     
      while ( true ){

        try{
          loopFactor++;
         
          determineSaveResumeDataInterval();
         
          if (  ( loopFactor % saveResumeLoopCount == 0 ) ||
              ( needsSaving && loadingComplete && loopFactor > initSaveResumeLoopCount )) {
             
            saveDownloads( true );
          }
                   
          if ((loopFactor % natCheckLoopCount == 0)) {
             
            computeNATStatus();
           
              // we need this periodic check to pick up on DND file state changes (someone changes
              // a file from DND to normal and consequentially changes to a non-seeding mode).
              // Doing this via listeners is too much effort
           
            checkSeedingOnlyState();
           
              // double check consistency
           
            checkForceStart( false );
          }
           
          if ((loopFactor % seedPieceCheckCount == 0)) {

            seedPieceRecheck();
          }
         
          for (Iterator it=managers_cow.iterator();it.hasNext();) {
           
            DownloadManager manager = (DownloadManager)it.next();
           
            if ( loopFactor % saveResumeLoopCount == 0 ) {
             
              manager.saveResumeData();
            }
           
                /*
                 * seeding rules have been moved to StartStopRulesDefaultPlugin
                 */
          }       

        }catch( Throwable e ){
         
          Debug.printStackTrace( e );
        }
       
        try {
          run_sem.reserve(waitTime);
         
          if ( run_sem.isReleasedForever()){
           
            break;
          }
        }
        catch (Exception e) {
          Debug.printStackTrace( e );
        }
      }
    }

    public void stopIt() {
      run_sem.releaseForever();
    }
  }

  public
  GlobalManagerImpl(
  AzureusCore            core,
  GlobalMangerProgressListener   listener,
    long               existingTorrentLoadDelay)
  {
    //Debug.dumpThreadsLoop("Active threads");
  progress_listener = listener;
 
  this.cripple_downloads_config = "1".equals(System.getProperty("azureus.disabledownloads"));
 
    AEDiagnostics.addEvidenceGenerator( this );
 
    stats = new GlobalManagerStatsImpl( this );
      
    try{
      stats_writer = new GlobalManagerStatsWriter( core );
     
    }catch( Throwable e ){
     
      Logger.log(new LogEvent(LOGID, "Stats unavailable", e ));
    }

    // Wait at least a few seconds before loading existing torrents.
    // typically the UI will call loadExistingTorrents before this runs
    // This is here in case the UI is stupid or forgets
    if (existingTorrentLoadDelay > 0) {
      loadTorrentsDelay = new DelayedEvent("GM:tld", existingTorrentLoadDelay,
          new AERunnable() {
            public void runSupport() {
              loadExistingTorrentsNow(false); // already async
            }
          });
    } else {
      // run sync
      loadDownloads();
    }

    if (progress_listener != null){
      progress_listener.reportCurrentTask(MessageText.getString("splash.initializeGM"));
    }
   
    // Initialize scraper after loadDownloads so that we can merge scrapes
    // into one request per tracker
    trackerScraper = TRTrackerScraperFactory.getSingleton();
   
    trackerScraper.setClientResolver(
      new TRTrackerScraperClientResolver()
    {
        public int
      getStatus(
        HashWrapper  torrent_hash )
        {
             DownloadManager  dm = getDownloadManager(torrent_hash);
         
          if ( dm == null ){

            return( TRTrackerScraperClientResolver.ST_NOT_FOUND );
          }
                   
          int  dm_state = dm.getState();
         
          if (   dm_state == DownloadManager.STATE_QUEUED ){
           
            return( TRTrackerScraperClientResolver.ST_QUEUED );
           
          }else if (   dm_state == DownloadManager.STATE_DOWNLOADING ||
                dm_state == DownloadManager.STATE_SEEDING ){
           
            return( TRTrackerScraperClientResolver.ST_RUNNING );
          }
         
          return( TRTrackerScraperClientResolver.ST_OTHER );
        }
       
        public boolean
      isNetworkEnabled(
        HashWrapper  hash,
        URL      url )
        {
             DownloadManager  dm = getDownloadManager(hash);
         
          if ( dm == null ){
           
            return( false );
          }
         
          String  nw = AENetworkClassifier.categoriseAddress( url.getHost());
         
          String[]  networks = dm.getDownloadState().getNetworks();
         
          for (int i=0;i<networks.length;i++){
           
            if ( networks[i] ==  nw ){
             
              return( true );
            }
          }
         
          return( false );
        }
       
        public int[]
        getCachedScrape(
          HashWrapper hash )
        {
          DownloadManager  dm = getDownloadManager(hash);
         
          if ( dm == null ){
           
            return( null );
          }
               
        long cache = dm.getDownloadState().getLongAttribute( DownloadManagerState.AT_SCRAPE_CACHE );
       
        if ( cache == -1 ){
         
          return( null );
         
        }else{
         
          int seeds     = (int)((cache>>32)&0x00ffffff);
          int leechers   = (int)(cache&0x00ffffff);
         
          return( new int[]{ seeds, leechers });
        }
        }
       
        public Object[]
        getExtensions(
          HashWrapper  hash )
        {
           DownloadManager  dm = getDownloadManager(hash);
         
           Character  state;
           String    ext;
          
          if ( dm == null ){
           
            ext    = "";
               state  = TRTrackerScraperClientResolver.FL_NONE;
              
          }else{
         
            ext = dm.getDownloadState().getTrackerClientExtensions();
         
            if ( ext == null ){
             
              ext = "";
            }
           
            boolean  comp = dm.isDownloadComplete( false );
                        
            int  dm_state = dm.getState();
           
              // treat anything not stopped or running as queued as we need to be "optimistic"
              // for torrents at the start-of-day
           
            if (   dm_state == DownloadManager.STATE_ERROR ||
                dm_state == DownloadManager.STATE_STOPPED ||
                ( dm_state == DownloadManager.STATE_STOPPING && dm.getSubState() != DownloadManager.STATE_QUEUED )){
             
                 state  = comp?TRTrackerScraperClientResolver.FL_COMPLETE_STOPPED:TRTrackerScraperClientResolver.FL_INCOMPLETE_STOPPED;
               
            }else if dm_state == DownloadManager.STATE_DOWNLOADING ||
                  dm_state == DownloadManager.STATE_SEEDING ){
               
                state  = comp?TRTrackerScraperClientResolver.FL_COMPLETE_RUNNING:TRTrackerScraperClientResolver.FL_INCOMPLETE_RUNNING;
                               
            }else{
           
              state  = comp?TRTrackerScraperClientResolver.FL_COMPLETE_QUEUED:TRTrackerScraperClientResolver.FL_INCOMPLETE_QUEUED;
            }
          }
         
          return( new Object[]{ ext, state });
        }
       
        public boolean
        redirectTrackerUrl(
          HashWrapper    hash,
          URL        old_url,
          URL        new_url )
        {
             DownloadManager  dm = getDownloadManager(hash);
           
             if ( dm == null || dm.getTorrent() == null ){
              
               return( false );
             }
            
             return( TorrentUtils.replaceAnnounceURL( dm.getTorrent(), old_url, new_url ));
        }
    });
   
    trackerScraper.addListener(
      new TRTrackerScraperListener() {
        public void scrapeReceived(TRTrackerScraperResponse response) {
          HashWrapper  hash = response.getHash();
         
           DownloadManager manager = (DownloadManager)manager_map.get( hash );
           if ( manager != null ) {
             manager.setTrackerScrapeResponse( response );
          }
        }
      });
   
    try
      host_support = new GlobalManagerHostSupport( this );

    }catch( Throwable e ){
     
      Logger.log(new LogEvent(LOGID, "Hosting unavailable", e));
    }
   
    checker = new Checker();  
        
    checker.start();
   
    torrent_folder_watcher = new TorrentFolderWatcher( this );
   
   
    TRTrackerUtils.addListener(
      new TRTrackerUtilsListener()
      {
        public void
        announceDetailsChanged()
        { 
        Logger.log( new LogEvent(LOGID, "Announce details have changed, updating trackers" ));

        List  managers = managers_cow;
       
        for (int i=0;i<managers.size();i++){
         
          DownloadManager  manager = (DownloadManager)managers.get(i);
         
          manager.requestTrackerAnnounce( true );
        }
        }
      });
  }
 
  public void loadExistingTorrentsNow(boolean async)
  {
    if (loadTorrentsDelay == null) {
      return;
    }
    loadTorrentsDelay = null;

    //System.out.println(SystemTime.getCurrentTime() + ": load via " + Debug.getCompressedStackTrace());
    if (async) {
      AEThread thread = new AEThread("load torrents", true) {
        public void runSupport() {
          loadDownloads();
        }
      };
      thread.setPriority(3);
      thread.start();
    } else {
      loadDownloads();
    }
  }

  public DownloadManager
  addDownloadManager(
      String fileName,
    String savePath)
  {
    // TODO: add optionalHash?
    return addDownloadManager(fileName, null, savePath, DownloadManager.STATE_WAITING, true);
  }
  
  public DownloadManager
  addDownloadManager(
      String     fileName,
      byte[]  optionalHash,
      String     savePath,
      int         initialState,
    boolean    persistent )
  {
     return addDownloadManager(fileName, optionalHash, savePath, initialState,
        persistent, false, null);
  }

    public DownloadManager
    addDownloadManager(
        String torrent_file_name,
        byte[] optionalHash,
      String savePath,
      int initialState,
      boolean persistent,
      boolean for_seeding,
      DownloadManagerInitialisationAdapter _adapter )
    {
      return addDownloadManager(torrent_file_name, optionalHash, savePath, null, initialState, persistent, for_seeding, _adapter);
    }
 
 
  /**
   * @return true, if the download was added
   *
   * @author Rene Leonhardt
   */
 
  public DownloadManager
  addDownloadManager(
      String torrent_file_name,
      byte[] optionalHash,
    String savePath,
    String saveFile,
    int initialState,
    boolean persistent,
    boolean for_seeding,
    DownloadManagerInitialisationAdapter _adapter )
  {
    boolean needsFixup = false;
    DownloadManager manager;

    // wait for "load existing" to complete
    loadingSem.reserve(60 * 1000);
   
    DownloadManagerInitialisationAdapter adapter = getDMAdapter(_adapter);

    /* to recover the initial state for non-persistent downloads the simplest way is to do it here
     */

    List file_priorities = null;

    if (!persistent) {

      Map save_download_state = (Map) saved_download_manager_state.get(new HashWrapper(
          optionalHash));

      if (save_download_state != null) {

        if (save_download_state.containsKey("state")) {

          int saved_state = ((Long) save_download_state.get("state")).intValue();

          if (saved_state == DownloadManager.STATE_STOPPED) {

            initialState = saved_state;
          }
        }

        file_priorities = (List) save_download_state.get("file_priorities");

        // non persistent downloads come in at random times
        // If it has a position, it's probably invalid because the
        // list has been fixed up to remove gaps.  Set a flag to
        // do another fixup after adding
        Long lPosition = (Long) save_download_state.get("position");
        if (lPosition != null) {
          if (lPosition.longValue() != -1) {
            needsFixup = true;
          }
        }
      }
    }

    File torrentDir = null;
    File fDest = null;
    HashWrapper hash = null;
    boolean deleteDest = false;
    boolean removeFromAddingDM = false;

    try {
      File f = new File(torrent_file_name);

      if (!f.exists()) {
        throw (new IOException("Torrent file '" + torrent_file_name
            + "' doesn't exist"));
      }

      if (!f.isFile()) {
        throw (new IOException("Torrent '" + torrent_file_name
            + "' is not a file"));
      }

      fDest = TorrentUtils.copyTorrentFileToSaveDir(f, persistent);

      String fName = fDest.getCanonicalPath();
     
      try {
        // Check if we already have the torrent loaded or loading

        if (optionalHash != null) {
          hash = new HashWrapper(optionalHash);
        } else {
          // This does not trigger locale decoding :)
          TOTorrent torrent = TorrentUtils.readFromFile(fDest, false);
          hash = torrent.getHashWrapper();
        }
       
        if (hash != null) {
          removeFromAddingDM = true;

          // loaded check
          DownloadManager existingDM = getDownloadManager(hash);
          if (existingDM != null) {
            deleteDest = true;
            return existingDM;
          }

          try {
            // loading check
            addingDM_monitor.enter();
           
            if (addingDMs.contains(hash)) {
              removeFromAddingDM = false;
              deleteDest = true;
              return null;
            }

            addingDMs.add(hash);
          } finally {
            addingDM_monitor.exit();
          }
        }

       
      } catch (Exception e) {
        // ignore any error.. let it bork later in case old code relies
        // on it borking later
      }

      // now do the creation!

      DownloadManager new_manager = DownloadManagerFactory.create(this,
          optionalHash, fName, savePath, saveFile, initialState, persistent, for_seeding,
          file_priorities, adapter);

      manager = addDownloadManager(new_manager, true, true);

      // if a different manager is returned then an existing manager for
      // this torrent exists and the new one isn't needed (yuck)

      if (manager == null || manager != new_manager) {
        deleteDest = true;
      }
    } catch (IOException e) {
      System.out.println("DownloadManager::addDownloadManager: fails - td = "
          + torrentDir + ", fd = " + fDest);
      Debug.printStackTrace(e);
      manager = DownloadManagerFactory.create(this, optionalHash,
          torrent_file_name, savePath, saveFile, initialState, persistent, for_seeding,
          file_priorities, adapter);
      manager = addDownloadManager(manager, true, true);
    } catch (Exception e) {
      // get here on duplicate files, no need to treat as error
      manager = DownloadManagerFactory.create(this, optionalHash,
          torrent_file_name, savePath, saveFile, initialState, persistent, for_seeding,
          file_priorities, adapter);
      manager = addDownloadManager(manager, true, true);
    } finally {
      if (deleteDest) {
        fDest.delete();
        File backupFile;
        try {
          backupFile = new File(fDest.getCanonicalPath() + ".bak");
          if (backupFile.exists())
            backupFile.delete();
        } catch (IOException e) {
        }
      }

      if (removeFromAddingDM && hash != null) {
        try {
          addingDM_monitor.enter();
         
          addingDMs.remove(hash);
        } finally {
          addingDM_monitor.exit();
        }
      }
    }

    if (needsFixup && manager != null) {
      if (manager.getPosition() <= downloadManagerCount(manager.isDownloadComplete(false))) {
        fixUpDownloadManagerPositions();
      }
    }

    return manager;
  }



   protected DownloadManager
   addDownloadManager(
       DownloadManager   download_manager,
    boolean       save,
    boolean notifyListeners)
   {
    if (!isStopping) {
      // make sure we have existing ones loaded so that existing check works
      loadExistingTorrentsNow(false);

      try{
        managers_mon.enter();
       
        int  existing_index = managers_cow.indexOf( download_manager );
       
        if (existing_index != -1) {
         
          DownloadManager existing = (DownloadManager)managers_cow.get(existing_index);
                 
          download_manager.destroy( true );
         
          return( existing );
        }
               
        DownloadManagerStats dm_stats = download_manager.getStats();

        HashWrapper hashwrapper = null;
        try {
          hashwrapper = download_manager.getTorrent().getHashWrapper();
        } catch (Exception e1) { }
       
        Map  save_download_state  = (Map)saved_download_manager_state.get(hashwrapper);
       
        long saved_data_bytes_downloaded  = 0;
        long saved_data_bytes_uploaded    = 0;
        long saved_discarded        = 0;
        long saved_hashfails        = 0;
        long saved_SecondsDownloading    = 0;
        long saved_SecondsOnlySeeding     = 0;
       
        if ( save_download_state != null ){
            // once the state's been used we remove it
         
          saved_download_manager_state.remove( hashwrapper );
                   
          int maxDL = save_download_state.get("maxdl")==null?0:((Long) save_download_state.get("maxdl")).intValue();
          int maxUL = save_download_state.get("maxul")==null?0:((Long) save_download_state.get("maxul")).intValue();
         
          Long lDownloaded = (Long) save_download_state.get("downloaded");
          Long lUploaded = (Long) save_download_state.get("uploaded");
          Long lCompleted = (Long) save_download_state.get("completed");
          Long lDiscarded = (Long) save_download_state.get("discarded");
          Long lHashFailsCount = (Long) save_download_state.get("hashfails")// old method, number of fails
          Long lHashFailsBytes = (Long) save_download_state.get("hashfailbytes")// new method, bytes failed
 
          Long nbUploads = (Long)save_download_state.get("uploads")// migrated to downloadstate in 2403

          if ( nbUploads != null ){
              // migrate anything other than the default value of 4
            int  maxUploads = nbUploads.intValue();
            if ( maxUploads != 4 ){
                // hmm, can't currently remove maxuploads as it stops people regressing to earlier
                // version. So currently we store maxuploads still and only overwrite the dm state
                // value if the stored value is non-default and the state one is
              if ( download_manager.getMaxUploads() == 4 ){
                download_manager.setMaxUploads( maxUploads );
              }
            }
          }
         
          dm_stats.setDownloadRateLimitBytesPerSecond( maxDL );
          dm_stats.setUploadRateLimitBytesPerSecond( maxUL );
         
          if (lCompleted != null) {
            dm_stats.setDownloadCompleted(lCompleted.intValue());
          }
         
          if (lDiscarded != null) {
            saved_discarded = lDiscarded.longValue();
          }
         
          if ( lHashFailsBytes != null ){
           
            saved_hashfails = lHashFailsBytes.longValue();
           
          }else if ( lHashFailsCount != null) {
           
            TOTorrent torrent = download_manager.getTorrent();
           
            if ( torrent != null ){
           
              saved_hashfails = lHashFailsCount.longValue() * torrent.getPieceLength();
            }
          }
         
          Long lPosition = (Long) save_download_state.get("position");
         
            // 2.2.0.1 - category moved to downloadstate - this here for
            // migration purposes
         
          String sCategory = null;
          if (save_download_state.containsKey("category")){
            try{
              sCategory = new String((byte[]) save_download_state.get("category"), Constants.DEFAULT_ENCODING);
            }catch( UnsupportedEncodingException e ){
             
              Debug.printStackTrace(e);
            }
          }
 
          if (sCategory != null) {
            Category cat = CategoryManager.getCategory(sCategory);
            if (cat != null) download_manager.getDownloadState().setCategory(cat);
          }
         
          download_manager.requestAssumedCompleteMode();
         
          if (lDownloaded != null && lUploaded != null) {
            boolean bCompleted = download_manager.isDownloadComplete(false);
           
            long lUploadedValue = lUploaded.longValue();
           
            long lDownloadedValue = lDownloaded.longValue();
           
            if ( bCompleted && (lDownloadedValue == 0)){
             
              //Gudy : I say if the torrent is complete, let's simply set downloaded
              //to size in order to see a meaningfull share-ratio
                //Gudy : Bypass this horrible hack, and I don't care of first priority seeding...
                /*
                if (lDownloadedValue != 0 && ((lUploadedValue * 1000) / lDownloadedValue < minQueueingShareRatio) )
                  lUploadedValue = ( download_manager.getSize()+999) * minQueueingShareRatio / 1000;
                  */
              // Parg: quite a few users have complained that they want "open-for-seeding" torrents to
              // have an infinite share ratio for seeding rules (i.e. so they're not first priority)
            
            int  dl_copies = COConfigurationManager.getIntParameter("StartStopManager_iAddForSeedingDLCopyCount");
               
            lDownloadedValue = download_manager.getSize() * dl_copies;
           
            download_manager.getDownloadState().setFlag( DownloadManagerState.FLAG_ONLY_EVER_SEEDED, true );
            }
           
            saved_data_bytes_downloaded  = lDownloadedValue;
            saved_data_bytes_uploaded    = lUploadedValue;
          }
 
          if (lPosition != null)
            download_manager.setPosition(lPosition.intValue());
          // no longer needed code
          //  else if (dm_stats.getDownloadCompleted(false) < 1000)
          //  dm.setPosition(bCompleted ? numCompleted : numDownloading);
 
          Long lSecondsDLing = (Long)save_download_state.get("secondsDownloading");
          if (lSecondsDLing != null) {
            saved_SecondsDownloading = lSecondsDLing.longValue();
          }
 
          Long lSecondsOnlySeeding = (Long)save_download_state.get("secondsOnlySeeding");
          if (lSecondsOnlySeeding != null) {
            saved_SecondsOnlySeeding = lSecondsOnlySeeding.longValue();
          }
         
          Long already_allocated = (Long)save_download_state.get( "allocated" );
          if( already_allocated != null && already_allocated.intValue() == 1 ) {
            download_manager.setDataAlreadyAllocated( true );
          }
         
          Long creation_time = (Long)save_download_state.get( "creationTime" );
         
          if ( creation_time != null ){
           
            long  ct = creation_time.longValue();
           
            if ( ct < SystemTime.getCurrentTime()){
           
              download_manager.setCreationTime( ct );
            }
          }
         
        }else{
         
            // no stats, bodge the uploaded for seeds
          
          if ( dm_stats.getDownloadCompleted(false) == 1000 ){
                
            int  dl_copies = COConfigurationManager.getIntParameter("StartStopManager_iAddForSeedingDLCopyCount");
           
            saved_data_bytes_downloaded = download_manager.getSize()*dl_copies;
          }
        }
       
        dm_stats.restoreSessionTotals(
          saved_data_bytes_downloaded,
          saved_data_bytes_uploaded,
          saved_discarded,
          saved_hashfails,
          saved_SecondsDownloading,
          saved_SecondsOnlySeeding );
       
        boolean isCompleted = download_manager.isDownloadComplete(false);
    
        if (download_manager.getPosition() == -1) {
          int endPosition = 0;
          for (int i = 0; i < managers_cow.size(); i++) {
            DownloadManager dm = (DownloadManager) managers_cow.get(i);
            boolean dmIsCompleted = dm.isDownloadComplete(false);
            if (dmIsCompleted == isCompleted)
              endPosition++;
          }
          download_manager.setPosition(endPosition + 1);
        }
       
        // Even though when the DownloadManager was created, onlySeeding was
        // most likely set to true for completed torrents (via the Initializer +
        // readTorrent), there's a chance that the torrent file didn't have the
        // resume data.  If it didn't, but we marked it as complete in our
        // downloads config file, we should set to onlySeeding
        download_manager.requestAssumedCompleteMode();

        List  new_download_managers = new ArrayList( managers_cow );
       
        new_download_managers.add(download_manager);
       
        managers_cow  = new_download_managers;
       
        TOTorrent  torrent = download_manager.getTorrent();
       
        if ( torrent != null ){
         
          try{
            manager_map.put( new HashWrapper(torrent.getHash()), download_manager );
           
          }catch( TOTorrentException e ){
           
            Debug.printStackTrace( e );
          }
        }
       
        // Old completed downloads should have their "considered for move on completion"
        // flag set, to prevent them being moved.
        if (COConfigurationManager.getBooleanParameter("Set Completion Flag For Completed Downloads On Start")) {

          // We only want to know about truly complete downloads, since we aren't able to move partially complete
          // ones yet.
          if (download_manager.isDownloadComplete(true)) {
            download_manager.getDownloadState().setFlag(DownloadManagerState.FLAG_MOVE_ON_COMPLETION_DONE, true);
          }
        }

        if (notifyListeners) {
          listeners.dispatch( LDT_MANAGER_ADDED, download_manager );
        }
       
        download_manager.addListener(this);
       
        if ( save_download_state != null ){
         
            Long lForceStart = (Long) save_download_state.get("forceStart");
            if (lForceStart == null) {
                Long lStartStopLocked = (Long) save_download_state.get("startStopLocked");
                if(lStartStopLocked != null) {
                  lForceStart = lStartStopLocked;
                }
              }    

            if(lForceStart != null) {
              if(lForceStart.intValue() == 1) {
                download_manager.setForceStart(true);
              }
            }
        }
      }finally{
       
        managers_mon.exit();
      }
      if (save){
        saveDownloads(false);
      }
     
      return( download_manager );
    }
    else {
      Logger.log(new LogEvent(LOGID, LogEvent.LT_ERROR,
        "Tried to add a DownloadManager after shutdown of GlobalManager."));
      return( null );
    }
  }

  public List getDownloadManagers() {
    return managers_cow;
  }
   
  public DownloadManager getDownloadManager(TOTorrent torrent) {
    if (torrent == null) {
      return null;
    }
    try {
      return getDownloadManager(torrent.getHashWrapper());
    } catch (TOTorrentException e) {
      return null;
    }
  }

  public DownloadManager
  getDownloadManager(HashWrapper  hw)
  {
      return (DownloadManager)manager_map.get( hw );
  }
 
  public void
  canDownloadManagerBeRemoved(
    DownloadManager manager,
    boolean remove_torrent, boolean remove_data)
 
    throws GlobalManagerDownloadRemovalVetoException
  {
    try{
      removal_listeners.dispatchWithException(LDT_MANAGER_WBR, new Object[] {
        manager,
        new Boolean(remove_torrent),
        new Boolean(remove_data)
      });
     
    }catch( Throwable e ){
      if (e instanceof GlobalManagerDownloadRemovalVetoException) {
        throw((GlobalManagerDownloadRemovalVetoException)e);
      }
      GlobalManagerDownloadRemovalVetoException gmv = new GlobalManagerDownloadRemovalVetoException("Error running veto check");
      gmv.initCause(e);
      Debug.out(e);
      throw gmv;
    }
  }
 

  public void
  removeDownloadManager(
    DownloadManager manager)
 
    throws GlobalManagerDownloadRemovalVetoException
  {
    removeDownloadManager(manager, false, false);
  }

  public void
  removeDownloadManager(
    DownloadManager manager,
    boolean  remove_torrent,
    boolean  remove_data )
 
    throws GlobalManagerDownloadRemovalVetoException
  {
      // simple protection against people calling this twice
   
    if ( !managers_cow.contains( manager )){
     
      return;
    }
   
    canDownloadManagerBeRemoved( manager, remove_torrent, remove_data );
   
    manager.stopIt(DownloadManager.STATE_STOPPED, remove_torrent, remove_data);
   
    try{
      managers_mon.enter();
     
      List new_download_managers  = new ArrayList( managers_cow );
     
      new_download_managers.remove(manager);
     
      managers_cow  = new_download_managers;
     
      TOTorrent  torrent = manager.getTorrent();
     
      if ( torrent != null ){
       
        try{
          manager_map.remove(new HashWrapper(torrent.getHash()));
         
        }catch( TOTorrentException e ){
         
          Debug.printStackTrace( e );
        }
      }

    }finally{
     
      managers_mon.exit();
    }
 
      // when we remove a download manager from Azureus this is the time to remove it from the record of
      // created torrents if present
   
    TOTorrent  torrent = manager.getTorrent();
   
    if ( torrent != null ){
     
      TorrentUtils.removeCreatedTorrent( torrent );
    }
   
  manager.destroy( false );
 
    fixUpDownloadManagerPositions();
   
    listeners.dispatch( LDT_MANAGER_REMOVED, manager );
   
    manager.removeListener(this);
   
    saveDownloads( false );

    DownloadManagerState dms = manager.getDownloadState();
   
    if ( dms.getCategory() != null){
   
      dms.setCategory(null);
    }
   
     if ( manager.getTorrent() != null ) {

      trackerScraper.remove(manager.getTorrent());
    }
   
    if ( host_support != null ){
     
      host_support.torrentRemoved( manager.getTorrentFileName(), manager.getTorrent());
    }
   
      // delete the state last as passivating a hosted torrent may require access to
      // the existing torrent state
   
    dms.delete();
  }

  /* Puts GlobalManager in a stopped state.
   * Used when closing down Azureus.
   */
  public void
  stopGlobalManager() {
    try{
      managers_mon.enter();
     
      if ( isStopping ){
       
        return;
      }
     
      isStopping  = true;
     
    }finally{
     
      managers_mon.exit();
    }
     
    stats.save();
   
  informDestroyInitiated();
 
  if ( host_support != null ){
    host_support.destroy();
  }
 
  torrent_folder_watcher.destroy();
 
    // kick off a non-daemon task. This will ensure that we hang around
    // for at least LINGER_PERIOD to run other non-daemon tasks such as writing
    // torrent resume data...
 
  try{
      NonDaemonTaskRunner.run(
          new NonDaemonTask()
          {
            public Object
            run()
            { 
              return( null );
            }
           
            public String
            getName()
            {
              return( "Stopping global manager" );
            }
          });
  }catch( Throwable e ){
    Debug.printStackTrace( e );
  }
 
  checker.stopIt();
 
  if ( COConfigurationManager.getBooleanParameter("Pause Downloads On Exit" )){
   
    pauseDownloads( true );
   
      // do this before save-downloads so paused state gets saved
   
    stopAllDownloads( true );
   
    saveDownloads( true );

  }else{
 
    saveDownloads( true );
 
    stopAllDownloads( true );
  }
  if ( stats_writer != null ){
   
    stats_writer.destroy();
  }
 
  DownloadManagerStateFactory.saveGlobalStateCache();
 
  managers_cow  = new ArrayList();
 
  manager_map.clear();
 
  informDestroyed();
  }

  public void stopAllDownloads() {
    stopAllDownloads(false);
  }
 
  protected void stopAllDownloads(boolean for_close ) {
   
  if ( for_close ){ 
    if (progress_listener != null){
        progress_listener.reportCurrentTask(MessageText.getString("splash.unloadingTorrents"));
    }
  }
 
  long  lastListenerUpdate = 0;
 
  List<DownloadManager> managers = sortForStop();
 
  int nbDownloads = managers.size();
 
    for ( int i=0;i<nbDownloads;i++){
     
      DownloadManager manager = managers.get(i);
     
      long  now = SystemTime.getCurrentTime();
     
    if(progress_listener != null &&  now - lastListenerUpdate > 100) {
      lastListenerUpdate = now;

      int  currentDownload = i+1;
     
      progress_listener.reportPercent(100 * currentDownload / nbDownloads);
      progress_listener.reportCurrentTask(MessageText.getString("splash.unloadingTorrent")
          + " " + currentDownload + " "
          + MessageText.getString("splash.of") + " " + nbDownloads
          + " : " + manager.getTorrentFileName());
    }
   
      int state = manager.getState();
     
      if( state != DownloadManager.STATE_STOPPED &&
          state != DownloadManager.STATE_STOPPING ) {
       
        manager.stopIt( for_close?DownloadManager.STATE_CLOSED:DownloadManager.STATE_STOPPED, false, false );
      }
    }
  }
 
 
  /**
   * Starts all downloads
   */
  public void startAllDownloads() {   
    for (Iterator iter = managers_cow.iterator(); iter.hasNext();) {
      DownloadManager manager = (DownloadManager) iter.next();
      if ( manager.getState() == DownloadManager.STATE_STOPPED ){

        manager.stopIt( DownloadManager.STATE_QUEUED, false, false );
      }
    }
  }
 
  public boolean
  pauseDownload(
  DownloadManager  manager )
  {
    if ( manager.getTorrent() == null ) {
   
      return( false );
    }
       
    int state = manager.getState();
       
    if (   state != DownloadManager.STATE_STOPPED &&
      state != DownloadManager.STATE_ERROR &&
      state != DownloadManager.STATE_STOPPING ) {
         
        try{
       
          HashWrapper  wrapper = manager.getTorrent().getHashWrapper();
         
          boolean  forced = manager.isForceStart();
           
            // add first so anyone picking up the ->stopped transition see it is paused
         
          try
            paused_list_mon.enter();
           
            paused_list.add( new Object[]{wrapper, new Boolean(forced)});
           
          }finally{
           
            paused_list_mon.exit()
          }  
         
          manager.stopIt( DownloadManager.STATE_STOPPED, false, false );
         
          return( true );
         
        }catch( TOTorrentException e ){
         
          Debug.printStackTrace( e )
        }
      }
   
    return( false );
  }

 
  public void
  pauseDownloads()
  {
    pauseDownloads( false );
  }
 
  protected void
  pauseDownloads(
  boolean  tag_only )
  {
  List<DownloadManager> managers = sortForStop();
 
    for( DownloadManager manager: managers ){
     
      if ( manager.getTorrent() == null ) {
        continue;
      }
     
      int state = manager.getState();
     
      if( state != DownloadManager.STATE_STOPPED &&
          state != DownloadManager.STATE_ERROR &&
          state != DownloadManager.STATE_STOPPING ) {
       
        try {
          boolean  forced = manager.isForceStart();
         
            // add before stopping so anyone picking up the ->stopped transition sees that it is
            // paused
         
            try {
              paused_list_mon.enter();
           
              paused_list.add( new Object[]{ manager.getTorrent().getHashWrapper(), new Boolean(forced)});
             
        }finally{
         
          paused_list_mon.exit()
        }
       
        if ( !tag_only ){
         
          manager.stopIt( DownloadManager.STATE_STOPPED, false, false );
        }
 
        }catch( TOTorrentException e ) {
          Debug.printStackTrace( e )
        }
      }
    }
  }
 
    public boolean
    canPauseDownload(
      DownloadManager  manager )
    { 
    
      if( manager.getTorrent() == null ) {

        return( false );
      }
     
      int state = manager.getState();
     
      if( state != DownloadManager.STATE_STOPPED &&
          state != DownloadManager.STATE_ERROR &&
          state != DownloadManager.STATE_STOPPING ) {
       
        return( true );
      }

      return false;
    }
 
  public boolean
  isPaused(
    DownloadManager  manager )
  {
    if ( paused_list.size() == 0 ){
     
      return( false );
    }
   
    try
      paused_list_mon.enter();
     
        for( int i=0; i < paused_list.size(); i++ ) {
         
            Object[]  data = (Object[])paused_list.get(i);
           
            HashWrapper hash = (HashWrapper)data[0];
           
            DownloadManager this_manager = getDownloadManager( hash );
         
            if ( this_manager == manager ){
             
              return( true );
            }
        }
       
        return( false );
       
    }finally{
     
      paused_list_mon.exit();
    }
  }
 
  public boolean
  canPauseDownloads()
  {
    for( Iterator i = managers_cow.iterator(); i.hasNext(); ) {
     
      DownloadManager manager = (DownloadManager)i.next();
     
      if ( canPauseDownload( manager )){
       
        return( true );
      }
    }
    return false;
  }


  public void
  resumeDownload(
  DownloadManager  manager )
  {
  boolean  resume_ok   = false;
  boolean force    = false;
   
  try
    paused_list_mon.enter();
   
      for( int i=0; i < paused_list.size(); i++ ) {
       
          Object[]  data = (Object[])paused_list.get(i);
         
          HashWrapper hash = (HashWrapper)data[0];
         
          force = ((Boolean)data[1]).booleanValue();
         
          DownloadManager this_manager = getDownloadManager( hash );
       
          if ( this_manager == manager ){
           
            resume_ok  = true;
           
            paused_list.remove(i);
           
            break;
          }
      }
  }finally
       
    paused_list_mon.exit()
     }
     
  if ( resume_ok ){
       
    if ( manager.getState() == DownloadManager.STATE_STOPPED ) {
             
      if ( force ){
             
            manager.setForceStart(true);
           
          }else{
           
            manager.stopIt( DownloadManager.STATE_QUEUED, false, false );
          }
        }    
  }
  }
 
  public void resumeDownloads() {
    try paused_list_mon.enter();
      for( int i=0; i < paused_list.size(); i++ ) {    
        Object[]  data = (Object[])paused_list.get(i);
       
        HashWrapper hash = (HashWrapper)data[0];
        boolean    force = ((Boolean)data[1]).booleanValue();
       
        DownloadManager manager = getDownloadManager( hash );
     
        if( manager != null && manager.getState() == DownloadManager.STATE_STOPPED ) {
         
          if ( force ){
            manager.setForceStart(true);
          }else{
           
          manager.stopIt( DownloadManager.STATE_QUEUED, false, false );
          }
        }
      }
      paused_list.clear();
    }
    finally paused_list_mon.exit()}
  }


  public boolean canResumeDownloads() {
    try paused_list_mon.enter();
      for( int i=0; i < paused_list.size(); i++ ) { 
        Object[]  data = (Object[])paused_list.get(i);
        HashWrapper hash = (HashWrapper)data[0];
        DownloadManager manager = getDownloadManager( hash );
     
        if( manager != null && manager.getState() == DownloadManager.STATE_STOPPED ) {
          return true;
        }
      }
    }
    finally paused_list_mon.exit()}
   
    return false;
  }
 
    private List<DownloadManager>
    sortForStop()
  {
      List<DownloadManager>  managers = new ArrayList<DownloadManager>( managers_cow );
     
      Collections.sort(
        managers,
        new Comparator<DownloadManager>()
        {
          public int
          compare(
            DownloadManager o1,
            DownloadManager o2)
          {
            int s1 = o1.getState();
            int s2 = o2.getState();
           
            if ( s2 == DownloadManager.STATE_QUEUED ){
             
              return( 1 );
             
            }else if ( s1 == DownloadManager.STATE_QUEUED ){

              return( -1 );
            }
           
            return( 0 );
          }
        });
     
      return( managers );
  }
 
  private void loadDownloads()
  {
    if (this.cripple_downloads_config) {
      loadingComplete = true;
      loadingSem.releaseForever();
      return;
    }

   
    try{
      DownloadManagerStateFactory.loadGlobalStateCache();
     
      int triggerOnCount = 2;
      ArrayList downloadsAdded = new ArrayList();
      long lastListenerUpdate = 0;
      try{
        if (progress_listener != null){
          progress_listener.reportCurrentTask(MessageText.getString("splash.loadingTorrents"));
        }
       
        Map map = FileUtil.readResilientConfigFile("downloads.config");
 
        boolean debug = Boolean.getBoolean("debug");
 
        Iterator iter = null;
        //v2.0.3.0+ vs older mode
        List downloads = (List) map.get("downloads");
        int nbDownloads;
        if (downloads == null) {
          //No downloads entry, then use the old way
          iter = map.values().iterator();
          nbDownloads = map.size();
        }
        else {
          //New way, downloads stored in a list
          iter = downloads.iterator();
          nbDownloads = downloads.size();
        }
        int currentDownload = 0;
        while (iter.hasNext()) {
          currentDownload++;       
          Map mDownload = (Map) iter.next();
          try {
            byte[]  torrent_hash = (byte[])mDownload.get( "torrent_hash" );
 
            Long  lPersistent = (Long)mDownload.get( "persistent" );
 
            boolean  persistent = lPersistent==null || lPersistent.longValue()==1;
 
 
            String fileName = new String((byte[]) mDownload.get("torrent"), Constants.DEFAULT_ENCODING);
 
            if(progress_listener != null &&  SystemTime.getCurrentTime() - lastListenerUpdate > 100) {
              lastListenerUpdate = SystemTime.getCurrentTime();
 
              String shortFileName = fileName;
              try {
                File f = new File(fileName);
                shortFileName = f.getName();
              } catch (Exception e) {
              // TODO: handle exception
            }
             
              progress_listener.reportPercent(100 * currentDownload / nbDownloads);
              progress_listener.reportCurrentTask(MessageText.getString("splash.loadingTorrent")
                  + " " + currentDownload + " "
                  + MessageText.getString("splash.of") + " " + nbDownloads
                  + " : " + shortFileName );
            }
 
            //migration from using a single savePath to a separate dir and file entry
            String  torrent_save_dir;
            String  torrent_save_file;
 
            byte[] torrent_save_dir_bytes   = (byte[]) mDownload.get("save_dir");
 
            if ( torrent_save_dir_bytes != null ){
 
              byte[] torrent_save_file_bytes   = (byte[]) mDownload.get("save_file");
 
              torrent_save_dir  = new String(torrent_save_dir_bytes, Constants.DEFAULT_ENCODING);
 
              if ( torrent_save_file_bytes != null ){
 
                torrent_save_file  = new String(torrent_save_file_bytes, Constants.DEFAULT_ENCODING);          
              }else{
 
                torrent_save_file  = null;
              }
            }else{
 
              byte[] savePathBytes = (byte[]) mDownload.get("path");
              torrent_save_dir   = new String(savePathBytes, Constants.DEFAULT_ENCODING);
              torrent_save_file  = null;
            }
 
 
 
            int state = DownloadManager.STATE_WAITING;
            if (debug){
 
              state = DownloadManager.STATE_STOPPED;
 
            }else {
 
              if (mDownload.containsKey("state")) {
                state = ((Long) mDownload.get("state")).intValue();
                if (state != DownloadManager.STATE_STOPPED &&
                    state != DownloadManager.STATE_QUEUED &&
                    state != DownloadManager.STATE_WAITING)
 
                  state = DownloadManager.STATE_QUEUED;
 
              }else{
 
                int stopped = ((Long) mDownload.get("stopped")).intValue();
 
                if (stopped == 1){
 
                  state = DownloadManager.STATE_STOPPED;
                }
              }
            }       
 
            Long seconds_downloading = (Long)mDownload.get("secondsDownloading");
 
            boolean  has_ever_been_started = seconds_downloading != null && seconds_downloading.longValue() > 0;
 
            if (torrent_hash != null) {
              saved_download_manager_state.put(new HashWrapper(torrent_hash),
                  mDownload);
            }
 
            // for non-persistent downloads the state will be picked up if the download is re-added
            // it won't get saved unless it is picked up, hence dead data is dropped as required
 
            if ( persistent ){
 
              List file_priorities = (List) mDownload.get("file_priorities");
 
              final DownloadManager dm =
                DownloadManagerFactory.create(
                    this, torrent_hash, fileName, torrent_save_dir, torrent_save_file,
                    state, true, true, has_ever_been_started, file_priorities );
 
              if (addDownloadManager(dm, false, false) == dm) {
                downloadsAdded.add(dm);
 
                if (downloadsAdded.size() >= triggerOnCount) {
                  triggerOnCount *= 2;
                  triggerAddListener(downloadsAdded);
                  downloadsAdded.clear();
                }
              }
            }
          }
          catch (UnsupportedEncodingException e1) {
            //Do nothing and process next.
          }
          catch (Throwable e) {
            Logger.log(new LogEvent(LOGID,
                "Error while loading downloads.  " +
                "One download may not have been added to the list.", e));
          }
        }
 
        // This is set to true by default, but once the downloads have been loaded, we have no reason to ever
        // to do this check again - we only want to do it once to upgrade the state of existing downloads
        // created before this code was around.
        COConfigurationManager.setParameter("Set Completion Flag For Completed Downloads On Start", false);
 
        //load pause/resume state
        ArrayList pause_data = (ArrayList)map.get( "pause_data" );
        if( pause_data != null ) {
          try paused_list_mon.enter();
          for( int i=0; i < pause_data.size(); i++ ) {
            Object  pd = pause_data.get(i);
 
            byte[]    key;
            boolean    force;
 
            if ( pd instanceof byte[]){
              // old style, migration purposes
              key   = (byte[])pause_data.get( i );
              force  = false;
            }else{
              Map  m = (Map)pd;
 
              key   = (byte[])m.get("hash");
              force   = ((Long)m.get("force")).intValue() == 1;
            }
            paused_list.add( new Object[]{ new HashWrapper( key ), new Boolean( force )} );
          }
          }
          finally paused_list_mon.exit()}
        }
 
 
        // Someone could have mucked with the config file and set weird positions,
        // so fix them up.
        fixUpDownloadManagerPositions();
        Logger.log(new LogEvent(LOGID, "Loaded " + managers_cow.size()
            + " torrents"));
 
      }catch( Throwable e ){
        // there's been problems with corrupted download files stopping AZ from starting
        // added this to try and prevent such foolishness
 
        Debug.printStackTrace( e );
      } finally {
        loadingComplete = true;
        triggerAddListener(downloadsAdded);
 
        loadingSem.releaseForever();
      }
       
    }finally{
     
      DownloadManagerStateFactory.discardGlobalStateCache();
    }
  }
 
  private void triggerAddListener(List downloadsToAdd) {
    try {
      managers_mon.enter();
      List listenersCopy = listeners.getListenersCopy();

      for (int j = 0; j < listenersCopy.size(); j++) {
        GlobalManagerListener gmListener = (GlobalManagerListener) listenersCopy.get(j);
        for (int i = 0; i < downloadsToAdd.size(); i++) {
          DownloadManager dm = (DownloadManager) downloadsToAdd.get(i);
          gmListener.downloadManagerAdded(dm);
        }
      }
    } finally {

      managers_mon.exit();
    }
  }


  protected void
  saveDownloads(
    boolean  immediate )
  {
    if ( !immediate ){
     
      needsSaving  = true;
     
      return;
    }
   
    if (!loadingComplete) {
      needsSaving = true;
      return;
    }

    //    if(Boolean.getBoolean("debug")) return;

    needsSaving = false;
    if (this.cripple_downloads_config) {
      return;
    }
   
    try{
      managers_mon.enter();
     
      Collections.sort(managers_cow, new Comparator () {
        public final int compare (Object a, Object b) {
          return ((DownloadManager) a).getPosition()
              - ((DownloadManager) b).getPosition();
        }
      });
   
      if (Logger.isEnabled())
        Logger.log(new LogEvent(LOGID, "Saving Download List ("
            + managers_cow.size() + " items)"));
      Map map = new HashMap();
      List list = new ArrayList(managers_cow.size());
      for (int i = 0; i < managers_cow.size(); i++) {
        DownloadManager dm = (DownloadManager) managers_cow.get(i);
       
          DownloadManagerStats dm_stats = dm.getStats();
          Map dmMap = new HashMap();
          TOTorrent  torrent = dm.getTorrent();
         
          if ( torrent != null ){
            try{
              dmMap.put( "torrent_hash", torrent.getHash());
             
            }catch( TOTorrentException e ){
             
              Debug.printStackTrace(e);
            }
          }
         
          File  save_loc = dm.getAbsoluteSaveLocation();
          dmMap.put("persistent", new Long(dm.isPersistent()?1:0));
          dmMap.put("torrent", dm.getTorrentFileName());
          dmMap.put("save_dir", save_loc.getParent());
          dmMap.put("save_file", save_loc.getName());
   
          dmMap.put("maxdl", new Long( dm_stats.getDownloadRateLimitBytesPerSecond() ));
          dmMap.put("maxul", new Long( dm_stats.getUploadRateLimitBytesPerSecond() ));
         
          int state = dm.getState();
         
          if (state == DownloadManager.STATE_ERROR ){
         
              // torrents in error state always come back stopped
           
            state = DownloadManager.STATE_STOPPED;
           
        }else if dm.getAssumedComplete() && !dm.isForceStart() &&
                state != DownloadManager.STATE_STOPPED) {
         
        state = DownloadManager.STATE_QUEUED;
           
        }else if state != DownloadManager.STATE_STOPPED &&
                      state != DownloadManager.STATE_QUEUED &&
                      state != DownloadManager.STATE_WAITING){
         
            state = DownloadManager.STATE_WAITING;
           
        }
         
          dmMap.put("state", new Long(state));         
        dmMap.put("position", new Long(dm.getPosition()));
        dmMap.put("downloaded", new Long(dm_stats.getTotalDataBytesReceived()));
        dmMap.put("uploaded", new Long(dm_stats.getTotalDataBytesSent()));
        dmMap.put("completed", new Long(dm_stats.getDownloadCompleted(true)));
        dmMap.put("discarded", new Long(dm_stats.getDiscarded()));
        dmMap.put("hashfailbytes", new Long(dm_stats.getHashFailBytes()));
        dmMap.put("forceStart", new Long(dm.isForceStart() && (dm.getState() != DownloadManager.STATE_CHECKING) ? 1 : 0));
        dmMap.put("secondsDownloading", new Long(dm_stats.getSecondsDownloading()));
        dmMap.put("secondsOnlySeeding", new Long(dm_stats.getSecondsOnlySeeding()));
     
          // although this has been migrated, keep storing it to allow regression for a while
        dmMap.put("uploads", new Long(dm.getMaxUploads()));
       
        dmMap.put("creationTime", new Long( dm.getCreationTime()));
         
          //save file priorities
      dm.saveDownload();
     
          List file_priorities = (List)dm.getData( "file_priorities" );
          if ( file_priorities != null ) dmMap.put( "file_priorities" , file_priorities );

          dmMap.put( "allocated", new Long( dm.isDataAlreadyAllocated() == true ? 1 : 0 ) );

          list.add(dmMap);
        }
    
      map.put("downloads", list);
     
      //save pause/resume state
      try paused_list_mon.enter();
        if( !paused_list.isEmpty() ) {
          ArrayList pause_data = new ArrayList();
          for( int i=0; i < paused_list.size(); i++ ) {
            Object[] data = (Object[])paused_list.get(i);
           
            HashWrapper hash   = (HashWrapper)data[0];
            Boolean    force   = (Boolean)data[1];
           
            Map  m = new HashMap();
           
            m.put( "hash", hash.getHash());
            m.put( "force", new Long(force.booleanValue()?1:0));
           
            pause_data.add( m );
          }
          map.put( "pause_data", pause_data );
        }
      }
      finally paused_list_mon.exit()}
     
       
      FileUtil.writeResilientConfigFile("downloads.config", map );
    }finally{
     
      managers_mon.exit();
    }
  }

  /**
   * @return
   */
  public TRTrackerScraper getTrackerScraper() {
    return trackerScraper;
  }

  public GlobalManagerStats
  getStats()
  {
    return( stats );
  }

  public boolean contains(DownloadManager manager) {
    if (managers_cow != null && manager != null) {
      return managers_cow.contains(manager);
    }
    return false;
  }

  public int getIndexOf(DownloadManager manager) {
    if (managers_cow != null && manager != null)
      return managers_cow.indexOf(manager);
    return -1;
  }

  public boolean isMoveableUp(DownloadManager manager) {

    if ((manager.isDownloadComplete(false)) &&
        (COConfigurationManager.getIntParameter("StartStopManager_iRankType") != 0) &&
        (COConfigurationManager.getBooleanParameter("StartStopManager_bAutoReposition")))
      return false;

    return manager.getPosition() > 1;
  }
 
  public int downloadManagerCount(boolean bCompleted) {
    int numInGroup = 0;
    for (Iterator it = managers_cow.iterator();it.hasNext();) {
      DownloadManager dm = (DownloadManager)it.next();
      if (dm.isDownloadComplete(false) == bCompleted)
        numInGroup++;
    }
    return numInGroup;
  }

  public boolean isMoveableDown(DownloadManager manager) {

    boolean isCompleted = manager.isDownloadComplete(false);

    if (isCompleted &&
        (COConfigurationManager.getIntParameter("StartStopManager_iRankType") != 0) &&
        (COConfigurationManager.getBooleanParameter("StartStopManager_bAutoReposition")))
      return false;

    return manager.getPosition() < downloadManagerCount(isCompleted);
  }

  public void moveUp(DownloadManager manager) {
    moveTo(manager, manager.getPosition() - 1);
  }

  public void moveDown(DownloadManager manager) {
    moveTo(manager, manager.getPosition() + 1);
  }

  public void moveTop(DownloadManager[] manager) {
      try{
        managers_mon.enter();
     
        int newPosition = 1;
        for (int i = 0; i < manager.length; i++)
          moveTo(manager[i], newPosition++);
      }finally{
       
        managers_mon.exit();
      }
  }

  public void moveEnd(DownloadManager[] manager) {
       try{
        managers_mon.enter();
     
        int endPosComplete = 0;
        int endPosIncomplete = 0;
        for (int j = 0; j < managers_cow.size(); j++) {
          DownloadManager dm = (DownloadManager) managers_cow.get(j);
          if (dm.isDownloadComplete(false))
            endPosComplete++;
          else
            endPosIncomplete++;
        }
        for (int i = manager.length - 1; i >= 0; i--) {
          if (manager[i].isDownloadComplete(false) && endPosComplete > 0) {
            moveTo(manager[i], endPosComplete--);
          } else if (endPosIncomplete > 0) {
            moveTo(manager[i], endPosIncomplete--);
          }
        }
      }finally{
        managers_mon.exit();
      }
  }
 
  public void moveTo(DownloadManager manager, int newPosition) {
    boolean curCompleted = manager.isDownloadComplete(false);

    if (newPosition < 1 || newPosition > downloadManagerCount(curCompleted))
      return;

      try{
        managers_mon.enter();
     
        int curPosition = manager.getPosition();
        if (newPosition > curPosition) {
          // move [manager] down
          // move everything between [curPosition+1] and [newPosition] up(-) 1
          int numToMove = newPosition - curPosition;
          for (int i = 0; i < managers_cow.size(); i++) {
            DownloadManager dm = (DownloadManager) managers_cow.get(i);
            boolean dmCompleted = (dm.isDownloadComplete(false));
            if (dmCompleted == curCompleted) {
              int dmPosition = dm.getPosition();
              if ((dmPosition > curPosition) && (dmPosition <= newPosition)) {
                dm.setPosition(dmPosition - 1);
                numToMove--;
                if (numToMove <= 0)
                  break;
              }
            }
          }
         
          manager.setPosition(newPosition);
        }
        else if (newPosition < curPosition && curPosition > 1) {
          // move [manager] up
          // move everything between [newPosition] and [curPosition-1] down(+) 1
          int numToMove = curPosition - newPosition;
 
          for (int i = 0; i < managers_cow.size(); i++) {
            DownloadManager dm = (DownloadManager) managers_cow.get(i);
            boolean dmCompleted = (dm.isDownloadComplete(false));
            int dmPosition = dm.getPosition();
            if ((dmCompleted == curCompleted) &&
                (dmPosition >= newPosition) &&
                (dmPosition < curPosition)
               ) {
              dm.setPosition(dmPosition + 1);
              numToMove--;
              if (numToMove <= 0)
                break;
            }
          }
          manager.setPosition(newPosition);
        }
      }finally{
       
        managers_mon.exit();
      }
  }
 
  public void fixUpDownloadManagerPositions() {
      try{
        managers_mon.enter();
     
        int posComplete = 1;
        int posIncomplete = 1;
        Collections.sort(managers_cow, new Comparator () {
            public final int compare (Object a, Object b) {
              int i = ((DownloadManager)a).getPosition() - ((DownloadManager)b).getPosition();
              if (i != 0) {
                return i;
              }
             
              // non persistent before persistent
              if (((DownloadManager)a).isPersistent()) {
                return 1;
              } else if (((DownloadManager)b).isPersistent()) {
                return -1;
              }

              return 0;
            }
          } );
        for (int i = 0; i < managers_cow.size(); i++) {
          DownloadManager dm = (DownloadManager) managers_cow.get(i);
          if (dm.isDownloadComplete(false))
            dm.setPosition(posComplete++);
           else
            dm.setPosition(posIncomplete++);
        }
      }finally{
       
        managers_mon.exit();
      }
    }
 
  protected void  informDestroyed() {
      if ( destroyed )
      {
        return;
      }
     
      destroyed = true;
     
      /*
    Thread t = new Thread("Azureus: destroy checker")
      {
        public void
        run()
        {
          long  start = SystemTime.getCurrentTime();
             
          while(true){
               
            try{
              Thread.sleep(2500);
            }catch( Throwable e ){
              e.printStackTrace();
            }
               
            if ( SystemTime.getCurrentTime() - start > 10000 ){
                 
                // java web start problem here...
               
              // Debug.dumpThreads("Azureus: slow stop - thread dump");
             
              // Debug.killAWTThreads(); doesn't work
            }
          }
        }           
      };
         
    t.setDaemon(true);
       
    t.start();
    */

      listeners.dispatch( LDT_DESTROYED, null, true );
  }
   
  public void
  informDestroyInitiated() 
  {
    listeners.dispatch( LDT_DESTROY_INITIATED, null, true );   
  }
   
   public void
  addListener(
    GlobalManagerListener  listener )
  {
     addListener(listener, true);
  }

   public void
  addListener(
    GlobalManagerListener  listener,
    boolean trigger )
  {
    if ( isStopping ){
       
      listener.destroyed();
       
    }else{     
             
      listeners.addListener(listener);
     
      if (!trigger) {
        return;
      }

      // Don't use Dispatch.. async is bad (esp for plugin initialization)
      try{
        managers_mon.enter();
       
        List managers = managers_cow;
     
        for (int i=0;i<managers.size();i++){
         
          listener.downloadManagerAdded((DownloadManager)managers.get(i));
       
      }finally{
       
        managers_mon.exit();
      }
    }
  }
   
  public void
   removeListener(
    GlobalManagerListener  listener )
  {     
    listeners.removeListener(listener);
  }
 
  public void
  addDownloadWillBeRemovedListener(
    GlobalManagerDownloadWillBeRemovedListener  l )
  {
    removal_listeners.addListener( l );
  }
 
  public void
  removeDownloadWillBeRemovedListener(
    GlobalManagerDownloadWillBeRemovedListener  l )
  {
    removal_listeners.removeListener( l );
  }
 
  // DownloadManagerListener
  public void
  stateChanged(
  DownloadManager   manager,
  int         new_state )
  {
    needsSaving = true//make sure we update 'downloads.config' on state changes

    //run seeding-only-mode check

    PEPeerManager  pm_manager = manager.getPeerManager();

    if (   new_state == DownloadManager.STATE_DOWNLOADING &&
        pm_manager != null &&
        pm_manager.hasDownloadablePiece()){

      //the new state is downloading, so can skip the full check

      setSeedingOnlyState( false, false );

    }else{

      checkSeedingOnlyState();
    }
             
    checkForceStart( manager.isForceStart() && new_state == DownloadManager.STATE_DOWNLOADING );
  }
 
  protected void
  checkForceStart(
   boolean  known_to_exist )
  {
    boolean  exists;
   
    if ( known_to_exist ){
     
      exists  = true;
     
    }else{
     
      exists  = false;
     
      if ( force_start_non_seed_exists ){
       
        List managers = managers_cow;
       
        for( int i=0; i < managers.size(); i++ ) {
             
          DownloadManager dm = (DownloadManager)managers.get( i );
 
          if ( dm.isForceStart() && dm.getState() == DownloadManager.STATE_DOWNLOADING  ){
           
            exists = true;
           
            break;
          }
        }
      }
    }
   
    if ( exists != force_start_non_seed_exists ){
     
      force_start_non_seed_exists = exists;
     
      Logger.log(new LogEvent(LOGID, "Force start download " + (force_start_non_seed_exists?"exists":"doesn't exist") + ", modifying download weighting" ));
     
      //System.out.println( "force_start_exists->" + force_start_non_seed_exists );
     
      PeerControlSchedulerFactory.overrideWeightedPriorities( force_start_non_seed_exists  );
    }
  }
 
  protected void
  checkSeedingOnlyState()
  {
  check_seeding_only_state_dispatcher.dispatch()
  }
 
  protected void
  checkSeedingOnlyStateSupport()
  {
    boolean seeding       = false;
    boolean seeding_set       = false;
    boolean  potentially_seeding  = false;
   
    List managers = managers_cow;
   
    for( int i=0; i < managers.size(); i++ ) {
       
        DownloadManager dm = (DownloadManager)managers.get( i );

        PEPeerManager pm = dm.getPeerManager();
       
        int  state = dm.getState();

        if ( dm.getDiskManager() == null || pm == null ){
         
            // download not running
         
          if ( state == DownloadManager.STATE_QUEUED ){
            
            if ( dm.isDownloadComplete( false )){
             
              potentially_seeding = true;
            } else {

              // Queued and Incomplete means we aren't "only seeding".  This
              // download WILL start, it just hasn't yet for various reasons
              // (1st Priority, etc)
              seeding       = false;
             
              // can't break out, because we still need to calculate
              // potentially_seeding.  Set a flag so we don't set "seeding"
              // back to true
              seeding_set = true;

            }
          }
         
          continue;
        }
       
        if ( state == DownloadManager.STATE_DOWNLOADING ){
         
          if (!pm.hasDownloadablePiece()){
           
              // complete DND file

            if (!seeding_set) {
             
              seeding = true;
            }
           
          }else{
           
            seeding       = false;
            potentially_seeding  = false;
           
            break;
          }
        }else if ( state == DownloadManager.STATE_SEEDING ){
         
          if (!seeding_set) {

            seeding = true;
          }
        }
    }
   
    if ( seeding ){
     
      potentially_seeding = true;
    }
   
    setSeedingOnlyState( seeding, potentially_seeding );
  }
 
 
  protected void
  setSeedingOnlyState(
    boolean    seeding,
    boolean    potentially_seeding )
  {
    synchronized( this ){
     
      if (   seeding       != seeding_only_mode ||
          potentially_seeding != potentially_seeding_only_mode ){
         
          seeding_only_mode       = seeding;
          potentially_seeding_only_mode  = potentially_seeding;
         
          // System.out.println( "dispatching " + seeding_only_mode + "/" + potentially_seeding_only_mode );
         
          listeners.dispatch( LDT_SEEDING_ONLY, new boolean[]{ seeding_only_mode, potentially_seeding_only_mode });
      }
    }
  }
   
  public boolean
  isSeedingOnly()
  {
    return( seeding_only_mode );
  }
 
  public boolean
  isPotentiallySeedingOnly()
  {
    return( potentially_seeding_only_mode );
  }
 
  public long
  getTotalSwarmsPeerRate(
    boolean   downloading,
    boolean   seeding )
  {
    long  now = SystemTime.getCurrentTime();
   
    if (   now < last_swarm_stats_calc_time ||
        now - last_swarm_stats_calc_time >= 1000 ){
     
      long  total = 0;
         
      List  managers = managers_cow;
     
      for (int i=0;i<managers.size();i++){
       
        DownloadManager  manager = (DownloadManager)managers.get(i);

        boolean  is_seeding = manager.getState() == DownloadManager.STATE_SEEDING;
       
        if (  ( downloading && !is_seeding ) ||
            ( seeding && is_seeding )){
         
          total += manager.getStats().getTotalAveragePerPeer();
        }
      }
     
      last_swarm_stats  = total;
     
      last_swarm_stats_calc_time  = now;
    }
   
    return( last_swarm_stats );
  }
 
  protected void
  computeNATStatus()
  {
    int  num_ok      = 0;
    int num_probably_ok  = 0;
    int  num_bad      = 0;
   
        for (Iterator it=managers_cow.iterator();it.hasNext();) {
           
          DownloadManager manager = (DownloadManager)it.next();

          int  status = manager.getNATStatus();
         
          if ( status == ConnectionManager.NAT_OK ){
           
            num_ok++;
           
          }else if ( status == ConnectionManager.NAT_PROBABLY_OK ){
           
            num_probably_ok++;
           
          }else if ( status == ConnectionManager.NAT_BAD ){
               
              num_bad++;
          }
        }
       
        if ( num_ok > 0 ){
         
          nat_status = ConnectionManager.NAT_OK;
         
        }else if ( num_probably_ok > 0 || nat_status_probably_ok ){
         
          nat_status         = ConnectionManager.NAT_PROBABLY_OK;
         
          nat_status_probably_ok  = true;
         
        }else if ( num_bad > 0 ){
         
          nat_status = ConnectionManager.NAT_BAD;
         
        }else{
         
          nat_status = ConnectionManager.NAT_UNKNOWN;
        }
  }
 
  public int
  getNATStatus()
  { 
    return( nat_status );
  }
 
  protected void
  seedPieceRecheck()
  {
    List  managers = managers_cow;
   
    if ( next_seed_piece_recheck_index >= managers.size()){
     
      next_seed_piece_recheck_index  = 0;
    }
   
    for (int i=next_seed_piece_recheck_index;i<managers.size();i++){
     
      DownloadManager manager = (DownloadManager)managers.get(i);
     
      if ( seedPieceRecheck( manager )){
       
        next_seed_piece_recheck_index = i+1;
       
        if ( next_seed_piece_recheck_index >= managers.size()){
         
          next_seed_piece_recheck_index  = 0;
        }
       
        return;
      }
    }
   
    for (int i=0;i<next_seed_piece_recheck_index;i++){
     
      DownloadManager manager = (DownloadManager)managers.get(i);
     
      if ( seedPieceRecheck( manager )){
       
        next_seed_piece_recheck_index = i+1;
       
        if ( next_seed_piece_recheck_index >= managers.size()){
         
          next_seed_piece_recheck_index  = 0;
        }
       
        return;
      }
    }
  }

  protected boolean
  seedPieceRecheck(
    DownloadManager  manager )
  {
    if ( manager.getState() != DownloadManager.STATE_SEEDING ){
     
      return( false );
    }
   
    return( manager.seedPieceRecheck());
  }
 
  protected DownloadManagerInitialisationAdapter
  getDMAdapter(
    DownloadManagerInitialisationAdapter  adapter )
  {
    List  adapters = dm_adapters.getList();
   
    if ( adapters.size() == 0 ){
     
      return( adapter )
    }
       
    if ( adapter != null ){

        // musn't update the copy-on-write list
     
      adapters = new ArrayList( adapters );

      adapters.add( adapter );
    }
   
    if ( adapters.size() == 1 ){
     
      return((DownloadManagerInitialisationAdapter)adapters.get(0));
    }
   
    final List  f_adapters = adapters;
   
    return( new DownloadManagerInitialisationAdapter()
        {
          public void
          initialised(
            DownloadManager    manager )
          {
            for (int i=0;i<f_adapters.size();i++){
             
              try{
                ((DownloadManagerInitialisationAdapter)f_adapters.get(i)).initialised( manager );
               
              }catch( Throwable e ){
               
                Debug.printStackTrace(e);
              }
            }
           
            if (Constants.isOSX) {
              fixLongFileName(manager);
            }
           
            if ( COConfigurationManager.getBooleanParameter( "Rename Incomplete Files")){
             
              String  ext = COConfigurationManager.getStringParameter( "Rename Incomplete Files Extension" ).trim();
             
              DownloadManagerState state = manager.getDownloadState();
             
              String existing_ext = state.getAttribute( DownloadManagerState.AT_INCOMP_FILE_SUFFIX );

              if ( ext.length() > 0 && existing_ext == null ){
                 
                ext = FileUtil.convertOSSpecificChars( ext, false );
               
                DiskManagerFileInfo[] fileInfos = manager.getDiskManagerFileInfo();
               
                try{
                  state.suppressStateSave(true);
                                 
                  for ( int i=0; i<fileInfos.length; i++ ){
                   
                    DiskManagerFileInfo fileInfo = fileInfos[i];
                   
                    File base_file = fileInfo.getFile( false );
                   
                    File existing_link = state.getFileLink( base_file );
                   
                    if ( existing_link == null && base_file.exists()){
                     
                        // file already exists, do nothing as probably adding for seeding
                     
                    }else if ( existing_link == null || !existing_link.exists()){
                     
                      File  new_link;
                     
                      if ( existing_link == null ){
                       
                        new_link = new File( base_file.getParentFile(), base_file.getName() + ext );
                       
                      }else{
                       
                        new_link = new File( existing_link.getParentFile(), existing_link.getName() + ext );
                      }
                     
                      state.setFileLink( base_file,new_link );
                    }
                  }
                }finally{
                 
                  state.setAttribute( DownloadManagerState.AT_INCOMP_FILE_SUFFIX, ext );
                 
                  state.suppressStateSave(false);
                }
              }
            }
          }
        });
  }
 
  private void fixLongFileName(DownloadManager manager) {
    // "File name too long" test
    // Note: This only addresses the case where the filename is too
    //       long, not the parent directory
    DiskManagerFileInfo[] fileInfos = manager.getDiskManagerFileInfo();

    DownloadManagerState state = manager.getDownloadState();

    try {
      state.suppressStateSave(true);

      for (int i = 0; i < fileInfos.length; i++) {

        DiskManagerFileInfo fileInfo = fileInfos[i];

        File base_file = fileInfo.getFile(false);

        File existing_link = state.getFileLink(base_file);

        if (existing_link == null && !base_file.exists()) {

          String name = base_file.getName();
          String ext = FileUtil.getExtension(name);
          int extLength = ext.length();
          name = name.substring(0, name.length() - extLength);

          // Java appears to be pretending
          // each unicode character is 3 bytes long.  If the limit
          // on a name is 256 two byte characters, then in theory,
          // the limit is 170 unicode characters.  Instead of assuming
          // that's the case, let's just walk back until the name
          // is accepted.
          // Bail at 50 just for the fun of it (plus most filenames
          // are short, so we can skip the Canonical call)
          int origLength = name.length();
          if (origLength > 50) {
            File parentFile = base_file.getParentFile();

            // We don't get "File name too long" on getCanonicalPath
            // unless the dir is there
            // I Wonder if we should remove the dirs after using them
            // FMFileImpl will create dirs again
            parentFile.mkdirs();

            File newFile = null;
            boolean first = true;
            while (name.length() > 50) {
              try {
                newFile = new File(parentFile, name + ext);
                newFile.getCanonicalPath();

                if (first) {
                  break;
                }

                // it worked, but the new name might already exist
                int fixNameID = 0xFF; // always 3 digits :)
                boolean redo;
                do {
                  redo = false;
                  for (int j = 0; j < i; j++) {
                    DiskManagerFileInfo convertedFileInfo = fileInfos[j];
                    if (newFile.equals(convertedFileInfo.getFile(true))) {
                      do {
                        fixNameID++;
                        if (fixNameID >= 0xFFF) {
                          // exit, will fail later :(
                          break;
                        }
                        name = name.substring(0, name.length() - 3)
                            + Integer.toHexString(fixNameID);
                        newFile = new File(parentFile, name + ext);
                      } while (newFile.equals(convertedFileInfo.getFile(true)));
                      redo = fixNameID <= 0xFFF;
                      break;
                    }
                  }
                } while (redo);

                if (fixNameID <= 0xFFF) {
                  state.setFileLink(base_file, newFile);
                }
                break;
              } catch (IOException e) {
                first = false;
                name = name.substring(0, name.length() - 1);
              } catch (Throwable t) {
                Debug.out(t);
              }
            }
          }

        }
      }
    } finally {

      state.suppressStateSave(false);
    }
  }

  public void
  addDownloadManagerInitialisationAdapter(
    DownloadManagerInitialisationAdapter  adapter )
  {
    dm_adapters.add( adapter );
  }
 
  public void
  removeDownloadManagerInitialisationAdapter(
    DownloadManagerInitialisationAdapter  adapter )
  {
    dm_adapters.remove( adapter );
  }
 
  public void
  generate(
    IndentWriter    writer )
  {
    writer.println( "Global Manager" );

    try{
      writer.indent();
   
        managers_mon.enter();
       
      writer.println( "  managers: " + managers_cow.size());
   
      for (int i=0;i<managers_cow.size();i++){
       
        DownloadManager  manager = (DownloadManager)managers_cow.get(i);
       
        try{
          writer.indent();
         
          manager.generateEvidence( writer );
         
        }finally{
         
          writer.exdent();
        }
      }

      }finally{
     
      managers_mon.exit();
     
      writer.exdent();
      }
  }
 
  public static void
  main(
    String[]  args )
  {
    if ( args.length == 0 ){
      args = new String[]{
          "C:\\temp\\downloads.config",
          "C:\\temp\\downloads-9-3-05.config",
          "C:\\temp\\merged.config" };
     
    }else if ( args.length != 3 ){
     
      System.out.println( "Usage: newer_config_file older_config_file save_config_file" );
     
      return;
    }
   
    try{
      Map  map1 = FileUtil.readResilientFile( new File(args[0]));
      Map  map2 = FileUtil.readResilientFile( new File(args[1]));
     
      List  downloads1 = (List)map1.get( "downloads" );
      List  downloads2 = (List)map2.get( "downloads" );
     
      Set  torrents = new HashSet();
     
      Iterator  it1 = downloads1.iterator();
     
      while( it1.hasNext()){
       
        Map  m = (Map)it1.next();
       
        byte[]  hash = (byte[])m.get( "torrent_hash" );
       
        System.out.println( "1:" + ByteFormatter.nicePrint(hash));
       
        torrents.add( new HashWrapper( hash ));
      }
     
      List  to_add = new ArrayList();
     
      Iterator  it2 = downloads2.iterator();
     
      while( it2.hasNext()){
       
        Map  m = (Map)it2.next();
       
        byte[]  hash = (byte[])m.get( "torrent_hash" );
       
        HashWrapper  wrapper = new HashWrapper( hash );
       
        if ( torrents.contains( wrapper )){
         
          System.out.println( "-:" + ByteFormatter.nicePrint(hash));
         
        }else{
         
          System.out.println( "2:" + ByteFormatter.nicePrint(hash));
         
          to_add.add( m );
        }
      }
     
      downloads1.addAll( to_add );
     
      System.out.println( to_add.size() + " copied from " + args[1] + " to " + args[2]);
     
      FileUtil.writeResilientFile( new File( args[2]), map1 );
     
    }catch( Throwable e ){
     
      e.printStackTrace();
    }
  }
 
  public void setMainlineDHTProvider(MainlineDHTProvider provider) {
    this.provider = provider;
  }
 
  public MainlineDHTProvider getMainlineDHTProvider() {
    return this.provider;
  }
 
  public void
  statsRequest(
    Map    request,
    Map    reply )
  {
    AzureusCore core = AzureusCoreFactory.getSingleton();
   
    Map  glob = new HashMap();
   
    reply.put( "gm", glob );
   
    try{
      glob.put( "u_rate", new Long( stats.getDataAndProtocolSendRate()));
      glob.put( "d_rate", new Long( stats.getDataAndProtocolReceiveRate()));
     
      glob.put( "d_lim", new Long( TransferSpeedValidator.getGlobalDownloadRateLimitBytesPerSecond()));
     
      boolean auto_up = TransferSpeedValidator.isAutoSpeedActive(this) && TransferSpeedValidator.isAutoUploadAvailable( core );
 
      glob.put( "auto_up", new Long(auto_up?COConfigurationManager.getLongParameter( SpeedManagerImpl.CONFIG_VERSION ):0));
 
      long up_lim = NetworkManager.getMaxUploadRateBPSNormal();
 
      boolean  seeding_only = NetworkManager.isSeedingOnlyUploadRate();
     
      glob.put( "so", new Long(seeding_only?1:0));
     
      if ( seeding_only ){
       
        up_lim = NetworkManager.getMaxUploadRateBPSSeedingOnly();
      }
     
      glob.put( "u_lim", new Long( up_lim ));
     
      SpeedManager sm = core.getSpeedManager();
     
      if ( sm != null ){
       
        glob.put( "u_cap", new Long( sm.getEstimatedUploadCapacityBytesPerSec().getBytesPerSec()));
        glob.put( "d_cap", new Long( sm.getEstimatedDownloadCapacityBytesPerSec().getBytesPerSec()));
      }
     
      List<DownloadManager> dms = getDownloadManagers();
     
      int  comp   = 0;
      int  incomp  = 0;
     
      long comp_up     = 0;
      long incomp_up    = 0;
      long incomp_down  = 0;
     
      for ( DownloadManager dm: dms ){
       
        int state = dm.getState();
       
        if ( state == DownloadManager.STATE_SEEDING || state == DownloadManager.STATE_DOWNLOADING ){
         
          DownloadManagerStats stats = dm.getStats();
         
          if ( dm.isDownloadComplete( false )){
           
            comp++;
           
            comp_up += stats.getProtocolSendRate() + stats.getDataSendRate();
           
          }else{
           
            incomp++;
           
            incomp_up   += stats.getProtocolSendRate() + stats.getDataSendRate();
            incomp_down += stats.getProtocolReceiveRate() + stats.getDataReceiveRate();
          }
        }
      }
     
      glob.put( "dm_i", new Long( incomp ));
      glob.put( "dm_c", new Long( comp ));
     
      glob.put( "dm_i_u", new Long( incomp_up ));
      glob.put( "dm_i_d", new Long( incomp_down ));
      glob.put( "dm_c_u", new Long( comp_up ));

      glob.put( "nat", new Long( nat_status ));
     
      boolean  request_limiting = COConfigurationManager.getBooleanParameter( "Use Request Limiting" );
     
      glob.put( "req_lim", new Long( request_limiting?1:0 ));

      if ( request_limiting ){
       
        glob.put( "req_focus", new Long( COConfigurationManager.getBooleanParameter( "Use Request Limiting Priorities" )?1:0 ));
      }
     
      boolean bias_up = COConfigurationManager.getBooleanParameter( "Bias Upload Enable" );
     
      glob.put( "bias_up", new Long( bias_up?1:0 ));
     
      if ( bias_up ){
     
        glob.put( "bias_slack", new Long( COConfigurationManager.getLongParameter( "Bias Upload Slack KBs" )));
       
        glob.put( "bias_ulim", new Long( COConfigurationManager.getBooleanParameter( "Bias Upload Handle No Limit" )?1:0 ));
      }
    }catch( Throwable e ){
    }
  }
}
TOP

Related Classes of org.gudy.azureus2.core3.global.impl.GlobalManagerImpl

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.