⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 downloadmanagerimpl.java

📁 一个基于JAVA的多torrent下载程序
💻 JAVA
📖 第 1 页 / 共 4 页
字号:
/*
 * File    : DownloadManagerImpl.java
 * Created : 19-Oct-2003
 * By      : parg
 * 
 * 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.download.impl;
/*
 * Created on 30 juin 2003
 *
 */
 
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.*;
import java.net.*;


import org.gudy.azureus2.core3.config.*;
import org.gudy.azureus2.core3.disk.*;
import org.gudy.azureus2.core3.global.GlobalManager;
import org.gudy.azureus2.core3.internat.*;
import org.gudy.azureus2.core3.logging.LogRelation;
import org.gudy.azureus2.core3.peer.*;
import org.gudy.azureus2.core3.tracker.client.*;
import org.gudy.azureus2.core3.torrent.*;
import org.gudy.azureus2.core3.util.*;
import org.gudy.azureus2.core3.download.*;

import org.gudy.azureus2.plugins.download.DownloadAnnounceResult;
import org.gudy.azureus2.plugins.download.DownloadScrapeResult;
import org.gudy.azureus2.plugins.network.ConnectionManager;

/**
 * @author Olivier
 * 
 */

public class 
DownloadManagerImpl 
	extends LogRelation
	implements DownloadManager
{
		// DownloadManager listeners
	
	private static final int LDT_STATECHANGED		= 1;
	private static final int LDT_DOWNLOADCOMPLETE	= 2;
	private static final int LDT_COMPLETIONCHANGED 	= 3;
	private static final int LDT_POSITIONCHANGED 	= 4;
	
	private AEMonitor	listeners_mon	= new AEMonitor( "DM:DownloadManager:L" );

	private static ListenerManager	listeners_aggregator 	= ListenerManager.createAsyncManager(
			"DM:ListenAggregatorDispatcher",
			new ListenerManagerDispatcher()
			{
				public void
				dispatch(
					Object		_listener,
					int			type,
					Object		_value )
				{
					DownloadManagerListener	listener = (DownloadManagerListener)_listener;
					
					Object[]	value = (Object[])_value;
					
					DownloadManagerImpl	dm = (DownloadManagerImpl)value[0];
					
					if ( type == LDT_STATECHANGED ){
						
						listener.stateChanged(dm, ((Integer)value[1]).intValue());
						
					}else if ( type == LDT_DOWNLOADCOMPLETE ){
						
						listener.downloadComplete(dm);

					}else if ( type == LDT_COMPLETIONCHANGED ){
						
						listener.completionChanged(dm, ((Boolean)value[1]).booleanValue());

					}else if ( type == LDT_POSITIONCHANGED ){
												
						listener.positionChanged( dm, ((Integer)value[1]).intValue(), ((Integer)value[2]).intValue());
						                         
					}
				}
			});		
	
	private ListenerManager	listeners 	= ListenerManager.createManager(
			"DM:ListenDispatcher",
			new ListenerManagerDispatcher()
			{
				public void
				dispatch(
					Object		listener,
					int			type,
					Object		value )
				{
					listeners_aggregator.dispatch( listener, type, value );
				}
			});	
	
		// TrackerListeners
	
	private static final int LDT_TL_ANNOUNCERESULT		= 1;
	private static final int LDT_TL_SCRAPERESULT		= 2;
	
	private ListenerManager	tracker_listeners 	= ListenerManager.createManager(
			"DM:TrackerListenDispatcher",
			new ListenerManagerDispatcher()
			{
				public void
				dispatch(
					Object		_listener,
					int			type,
					Object		value )
				{
					DownloadManagerTrackerListener	listener = (DownloadManagerTrackerListener)_listener;
					
					if ( type == LDT_TL_ANNOUNCERESULT ){
						
						listener.announceResult((TRTrackerAnnouncerResponse)value);
						
					}else if ( type == LDT_TL_SCRAPERESULT ){
						
						listener.scrapeResult((TRTrackerScraperResponse)value);
					}
				}
			});	

	// PeerListeners
	
	private static final int LDT_PE_PEER_ADDED		= 1;
	private static final int LDT_PE_PEER_REMOVED	= 2;
	private static final int LDT_PE_PIECE_ADDED		= 3;
	private static final int LDT_PE_PIECE_REMOVED	= 4;
	private static final int LDT_PE_PM_ADDED		= 5;
	private static final int LDT_PE_PM_REMOVED		= 6;
	
		// one static async manager for them all
	
	private static ListenerManager	peer_listeners_aggregator 	= ListenerManager.createAsyncManager(
			"DM:PeerListenAggregatorDispatcher",
			new ListenerManagerDispatcher()
			{
				public void
				dispatch(
					Object		_listener,
					int			type,
					Object		value )
				{
					DownloadManagerPeerListener	listener = (DownloadManagerPeerListener)_listener;
					
					if ( type == LDT_PE_PEER_ADDED ){
						
						listener.peerAdded((PEPeer)value);
						
					}else if ( type == LDT_PE_PEER_REMOVED ){
						
						listener.peerRemoved((PEPeer)value);
						
					}else if ( type == LDT_PE_PIECE_ADDED ){
						
						listener.pieceAdded((PEPiece)value);
						
					}else if ( type == LDT_PE_PIECE_REMOVED ){
						
						listener.pieceRemoved((PEPiece)value);
						
					}else if ( type == LDT_PE_PM_ADDED ){
						
						listener.peerManagerAdded((PEPeerManager)value);
						
					}else if ( type == LDT_PE_PM_REMOVED ){
						
						listener.peerManagerRemoved((PEPeerManager)value);
					}			
				}
			});

	private ListenerManager	peer_listeners 	= ListenerManager.createManager(
			"DM:PeerListenDispatcher",
			new ListenerManagerDispatcher()
			{
				public void
				dispatch(
					Object		listener,
					int			type,
					Object		value )
				{
					peer_listeners_aggregator.dispatch( listener, type, value );
				}
			});	
	
	private AEMonitor	peer_listeners_mon	= new AEMonitor( "DM:DownloadManager:PL" );
	
	private List	current_peers 	= new ArrayList();
	private List	current_pieces	= new ArrayList();
  
	private DownloadManagerController	controller;
	private DownloadManagerStatsImpl	stats;

	protected AEMonitor					this_mon = new AEMonitor( "DM:DownloadManager" );
	
	private boolean		persistent;

	/**
	 * Only seed this torrent. Never download or allocate<P>
	 * Current Implementation:
	 * - implies that the user completed the download at one point
	 * - Checks if there's Data Missing when torrent is done (or torrent load)
	 *
	 * Perhaps a better name would be "bCompleted"
	 */
	private boolean onlySeeding;
	
	/**
	 * forceStarted torrents can't/shouldn't be automatically stopped
	 */
	
	private int			last_informed_state	= STATE_START_OF_DAY;
	private boolean		latest_informed_force_start;

	private GlobalManager globalManager;

	private String torrentFileName;
	
	private String	display_name	= "";
	private String	internal_name	= "";
	
		// for simple torrents this refers to the torrent file itself. For non-simple it refers to the
		// folder containing the torrent's files
	
	private File	torrent_save_location;	
  
	// Position in Queue
	private int position = -1;
	
	private Object[]					read_torrent_state;
	private	DownloadManagerState		download_manager_state;
	
	private TOTorrent		torrent;
	private String 			torrent_comment;
	private String 			torrent_created_by;
	
	private TRTrackerAnnouncer 				tracker_client;
	private TRTrackerAnnouncerListener		tracker_client_listener = 
			new TRTrackerAnnouncerListener() 
			{
				public void 
				receivedTrackerResponse(
					TRTrackerAnnouncerResponse	response) 
				{
					PEPeerManager pm = controller.getPeerManager();
      
					if ( pm != null ) {
        
						pm.processTrackerResponse( response );
					}

					tracker_listeners.dispatch( LDT_TL_ANNOUNCERESULT, response );
				}

				public void 
				urlChanged(
					String 	url, 
					boolean explicit) 
				{
					if ( explicit ){
						checkTracker( true );
					}
				}

				public void 
				urlRefresh() 
				{
					checkTracker( true );
				}
			};
	
				// a second listener used to catch and propagate the "stopped" event
				
	private TRTrackerAnnouncerListener		stopping_tracker_client_listener = 
		new TRTrackerAnnouncerListener() 
		{
			public void 
			receivedTrackerResponse(
				TRTrackerAnnouncerResponse	response) 
			{
				tracker_listeners.dispatch( LDT_TL_ANNOUNCERESULT, response );
			}

			public void 
			urlChanged(
				String 	url, 
				boolean explicit) 
			{
			}

			public void 
			urlRefresh() 
			{
			}
		};
		
		
	private long						scrape_random_seed	= SystemTime.getCurrentTime();
	
  
	private HashMap data;
  
	private boolean data_already_allocated = false;
  
	private long	creation_time	= SystemTime.getCurrentTime();
  
	private int iSeedingRank;

	private boolean az_messaging_enabled = true;
   
	private boolean	dl_identity_obtained;
	private byte[]	dl_identity;
    private int 	dl_identity_hashcode;

    private int		max_connections;
    private int		max_uploads_when_seeding;
    private boolean	max_uploads_when_seeding_enabled;
    
    
	// Only call this with STATE_QUEUED, STATE_WAITING, or STATE_STOPPED unless you know what you are doing
	
	
	public 
	DownloadManagerImpl(
		GlobalManager 	_gm,
		byte[]			_torrent_hash,
		String 			_torrentFileName, 
		String 			_torrent_save_dir,
		String			_torrent_save_file,
		int   			_initialState,
		boolean			_persistent,
		boolean			_recovered,
		boolean			_open_for_seeding,
		boolean			_has_ever_been_started ) 
	{
		if ( 	_initialState != STATE_WAITING &&
				_initialState != STATE_STOPPED &&
				_initialState != STATE_QUEUED ){
			
			Debug.out( "DownloadManagerImpl: Illegal start state, " + _initialState );
		}
		
		persistent		= _persistent;
		globalManager 	= _gm;
		

		stats = new DownloadManagerStatsImpl( this );
  	
		controller	= new DownloadManagerController( this );

		stats.setMaxUploads( COConfigurationManager.getIntParameter("Max Uploads") );
	 	
		torrentFileName = _torrentFileName;
		
		while( _torrent_save_dir.endsWith( File.separator )){
			
			_torrent_save_dir = _torrent_save_dir.substring(0, _torrent_save_dir.length()-1 );
		}
		
			// readTorrent adjusts the save dir and file to be sensible values
			
		readTorrent( 	_torrent_save_dir, _torrent_save_file, _torrent_hash, 
						persistent && !_recovered, _open_for_seeding, _has_ever_been_started,
						_initialState );		

	}


	private void 
	readTorrent(
		String		torrent_save_dir,
		String		torrent_save_file,
		byte[]		torrent_hash,		// can be null for initial torrents
		boolean		new_torrent,		// probably equivalend to (torrent_hash == null)????
		boolean		open_for_seeding,
		boolean		has_ever_been_started,
		int			initial_state )
	{		
		try{
			display_name				= torrentFileName;	// default if things go wrong decoding it
			internal_name				= "";
			torrent_comment				= "";
			torrent_created_by			= "";
			
			try{
	
					// this is the first thing we do and most likely to go wrong - hence its
					// existence is used below to indicate success or not
				
				 download_manager_state	= 
					 	DownloadManagerStateImpl.getDownloadState(
					 			this, torrentFileName, torrent_hash );
				 
				 readParameters();
				 
					// establish any file links
					
				 download_manager_state.addListener(
						new DownloadManagerStateListener()
						{
							public void
							stateChanged(
								DownloadManagerState			state,
								DownloadManagerStateEvent		event )
							{
								if ( event.getType() == DownloadManagerStateEvent.ET_ATTRIBUTE_WRITTEN ){
									
									String	attribute_name = (String)event.getData();
									
									if ( attribute_name.equals( DownloadManagerState.AT_FILE_LINKS )){
										
										setFileLinks();
										
									}else if ( attribute_name.equals( DownloadManagerState.AT_PARAMETERS )){
										
										readParameters();
									}
								}
							}
						});
						
				 torrent	= download_manager_state.getTorrent();
				 
				 setFileLinks();
				 
				 	// We can't have the identity of this download changing as this will screw up
				 	// anyone who tries to maintain a unique set of downloads (e.g. the GlobalManager)
				 	//
				 
				 if ( !dl_identity_obtained ){
					 
					 	// flag set true below
					 
					 dl_identity			= torrent_hash==null?torrent.getHash():torrent_hash;
	                 
	                 this.dl_identity_hashcode = new String( dl_identity ).hashCode();		 
				 }
					 
				 if ( !Arrays.equals( dl_identity, torrent.getHash())){
						 
					 torrent	= null;	// prevent this download from being used
					 
					 throw( new Exception( "Download identity changed - please remove and re-add the download" ));
				 }
				 
				 read_torrent_state	= null;	// no longer needed if we saved it
	
				 LocaleUtilDecoder	locale_decoder = LocaleUtil.getSingleton().getTorrentEncoding( torrent );
						 
				 	// if its a simple torrent and an explicit save file wasn't supplied, use
				 	// the torrent name itself
				 
				 display_name = locale_decoder.decodeString( torrent.getName());
	             
				 display_name = FileUtil.convertOSSpecificChars( display_name );
			
				 internal_name = ByteFormatter.nicePrint(torrent.getHash(),true);
	
				 	// now we know if its a simple torrent or not we can make some choices about
				 	// the save dir and file. On initial entry the save_dir will have the user-selected
				 	// save location and the save_file will be null
				 
				 File	save_dir_file	= new File( torrent_save_dir );
				 
				 // System.out.println( "before: " + torrent_save_dir + "/" + torrent_save_file );
				 
				 	// if save file is non-null then things have already been sorted out
				 
				 if ( torrent_save_file == null ){
				 		 	
				 		// make sure we're working off a canonical save dir if possible
				 	
				 	try{
				 		if ( save_dir_file.exists()){
				 			
				 			save_dir_file = save_dir_file.getCanonicalFile();
				 		}
				 	}catch( Throwable e ){
				 			
				 		Debug.printStackTrace(e);
				 	}
		
				 	if ( torrent.isSimpleTorrent()){
				 		
				 			// if target save location is a directory then we use that as the save
				 			// dir and use the torrent display name as the target. Otherwise we
				 			// use the file name
				 		
				 		if ( save_dir_file.exists()){
				 			
				 			if ( save_dir_file.isDirectory()){
				 				
				 				torrent_save_file	= display_name;
				 				
				 			}else{
				 				
				 				torrent_save_dir	= save_dir_file.getParent().toString();
				 				
				 				torrent_save_file	= save_dir_file.getName();
				 			}
				 		}else{
				 			
				 				// doesn't exist, assume it refers directly to the file
				 			
				 			if ( save_dir_file.getParent() == null ){
				 				
				 				throw( new Exception( "Data location '" + torrent_save_dir + "' is invalid" ));
	
				 			}
				 			
			 				torrent_save_dir	= save_dir_file.getParent().toString();
			 				
			 				torrent_save_file	= save_dir_file.getName(); 			
				 		}
				 		
				 	}else{
				 	
				 			// torrent is a folder. It is possible that the natural location
				 			// for the folder is X/Y and that in fact 'Y' already exists and
				 			// has been selected. If ths is the case the select X as the dir and Y
				 			// as the file name
				 		
				 		if ( save_dir_file.exists()){
				 			
				 			if ( !save_dir_file.isDirectory()){
				 				
				 				throw( new Exception( "'" + torrent_save_dir + "' is not a directory" ));
				 			}
				 			
				 			if ( save_dir_file.getName().equals( display_name )){
				 				
				 				torrent_save_dir	= save_dir_file.getParent().toString();
				 			}
				 		}
				 		
				 		torrent_save_file	= display_name;		
				 	}
				 }
	
				 torrent_save_location = new File( torrent_save_dir, torrent_save_file );
				 
				 	// final validity test must be based of potentially linked target location as file
				 	// may have been re-targetted
	
				 File	linked_target = getSaveLocation();
				 
				 if ( !linked_target.exists()){
				 	
				 		// if this isn't a new torrent then we treat the absence of the enclosing folder
				 		// as a fatal error. This is in particular to solve a problem with the use of
				 		// externally mounted torrent data on OSX, whereby a re-start with the drive unmounted
				 		// results in the creation of a local diretory in /Volumes that subsequently stuffs
				 		// up recovery when the volume is mounted
				 	
				 		// changed this to only report the error on non-windows platforms 
				 	
				 	if ( !(new_torrent || Constants.isWindows )){
				 		
							// another exception here - if the torrent has never been started then we can
							// fairly safely continue as its in a stopped state
						
						if ( has_ever_been_started ){
				 		
							throw( new Exception( MessageText.getString("DownloadManager.error.datamissing") + " " + linked_target.toString()));
						}
				 	}
				 }	
				 
				 	// if this is a newly introduced torrent trash the tracker cache. We do this to
				 	// prevent, say, someone publishing a torrent with a load of invalid cache entries
				 	// in it and a bad tracker URL. This could be used as a DOS attack
	
				 if ( new_torrent ){
				 	
				 	download_manager_state.setTrackerResponseCache( new HashMap());
				 	
				 		// also remove resume data incase someone's published a torrent with resume
				 		// data in it
				 	
				 	if ( open_for_seeding ){
				 		
				 		DiskManagerFactory.setTorrentResumeDataNearlyComplete(download_manager_state);
	
				 	}else{
				 		
				 		download_manager_state.clearResumeData();
				 	}
				 }
				 
		         
				 //trackerUrl = torrent.getAnnounceURL().toString();
	         
				torrent_comment = locale_decoder.decodeString(torrent.getComment());
	         
				if ( torrent_comment == null ){
					
				   torrent_comment	= "";
				}
				
				torrent_created_by = locale_decoder.decodeString(torrent.getCreatedBy());
	         
				if ( torrent_created_by == null ){
					
					torrent_created_by	= "";
				}
				 			 
				 	// only restore the tracker response cache for non-seeds

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -