FileDocCategorySizeDatePackage
DownloadManagerStateImpl.javaAPI DocAzureus 3.0.3.463039Tue Sep 11 10:57:12 BST 2007org.gudy.azureus2.core3.download.impl

DownloadManagerStateImpl

public class DownloadManagerStateImpl extends Object implements org.gudy.azureus2.core3.config.ParameterListener, DownloadManagerState
author
parg Overall aim of this is to stop updating the torrent file itself and update something Azureus owns. To this end a file based on torrent hash is created in user-dir/active It is actually just a copy of the torrent file

Fields Summary
private static final LogIDs
LOGID
private static final String
RESUME_KEY
private static final String
TRACKER_CACHE_KEY
private static final String
ATTRIBUTE_KEY
private static final File
ACTIVE_DIR
private static final Map
default_parameters
private static final Map
default_attributes
private static AEMonitor
class_mon
private static Map
state_map
private static Map
global_state_cache
private static List
global_state_cache_wrappers
private DownloadManagerImpl
download_manager
private TorrentUtils.ExtendedTorrent
torrent
private boolean
write_required
private Map
tracker_response_cache
private org.gudy.azureus2.core3.category.Category
category
private List
listeners
private List
will_be_read_list
private Map
parameters
private Map
attributes
private AEMonitor
this_mon
private boolean
firstPrimaryFileRead
Constructors Summary
protected DownloadManagerStateImpl(DownloadManagerImpl _download_manager, TorrentUtils.ExtendedTorrent _torrent)

		download_manager	= _download_manager;
		torrent				= _torrent;
		
		attributes = torrent.getAdditionalMapProperty( ATTRIBUTE_KEY );
		
		if ( attributes == null ){
        	
			attributes	= new HashMap();
        }
		
        String cat_string = getStringAttribute( AT_CATEGORY );

        if ( cat_string != null ){
        	
        	Category cat = CategoryManager.getCategory( cat_string );
        	
        	if ( cat != null ){
        		
        		setCategory( cat );
        	}
        }
        
        parameters	= getMapAttribute( AT_PARAMETERS );
        
        if ( parameters == null ){
        	
        	parameters	= new HashMap();
        }
        

        addListeners();
	
Methods Summary
public voidaddListener(DownloadManagerStateListener l)

		listeners.add( l );
	
protected voidaddListeners()

		COConfigurationManager.addParameterListener( "Max.Peer.Connections.Per.Torrent.When.Seeding", this );
		COConfigurationManager.addParameterListener( "Max.Peer.Connections.Per.Torrent.When.Seeding.Enable", this );
		COConfigurationManager.addParameterListener( "Max.Peer.Connections.Per.Torrent", this );
		COConfigurationManager.addParameterListener( "Max Uploads", this );
		COConfigurationManager.addParameterListener( "Max Uploads Seeding", this );
		COConfigurationManager.addParameterListener( "Max Seeds Per Torrent", this );
		COConfigurationManager.addParameterListener( "enable.seedingonly.maxuploads", this );
	
public voidclearFileLinks()

		CaseSensitiveFileMap	links = getFileLinks();
		
		List	list = new ArrayList();
		
		Iterator	it = links.keySetIterator();
		
		boolean	changed = false;
		
		while( it.hasNext()){
			
			File	source = (File)it.next();
			File	target = (File)links.get(source);
			
			if ( target != null ){
				
				changed = true;
			}
			
			String	str = source + "\n";
			
			list.add( str );
		}
		
		if ( changed ){
	
			setListAttribute( AT_FILE_LINKS, list );
		}
	
public voidclearResumeData()

		setResumeData( null );
	
public voidclearTrackerResponseCache()

		setTrackerResponseCache( new HashMap());
	
public voiddelete()

		try{
			class_mon.enter();

			HashWrapper	wrapper = torrent.getHashWrapper();
			
			state_map.remove( wrapper );
			
	        TorrentUtils.delete( torrent );
	        
			File	dir = new File( ACTIVE_DIR, ByteFormatter.encodeString( wrapper.getBytes()));

			if ( dir.exists() && dir.isDirectory()){
				
				FileUtil.recursiveDelete( dir );
			}
			
			removeListeners();
			
		}catch( Throwable e ){
	    	
	    	Debug.printStackTrace( e );
	   
		}finally{
			
			class_mon.exit();
		}
	
public static voiddiscardGlobalStateCache()

		getGlobalStateFile().delete();
		
		for ( int i=0;i<global_state_cache_wrappers.size();i++){
			
			((CachedStateWrapper)global_state_cache_wrappers.get(i)).clearCache();
		}
		
		global_state_cache_wrappers.clear();
	
public voidgenerateEvidence(IndentWriter writer)

		writer.println( "DownloadManagerState" );
		
		try{
			writer.indent();
			
			writer.println( "parameters=" + parameters );
			
			writer.println("primary file=" + Debug.secretFileName(getPrimaryFile()));
			
		}finally{
			
			writer.exdent();
		}
	
public java.lang.StringgetAttribute(java.lang.String name)

		if ( name.equals( AT_CATEGORY )){
			
			Category	cat = getCategory();
			
			if ( cat == null ){
				
				return( null );
			}
			
			if ( cat == CategoryManager.getCategory( Category.TYPE_UNCATEGORIZED )){
				
				return( null );
			}
			
			return( cat.getName());
			
		}else{
			
			return( getStringAttribute( name ));
		}
	
public booleangetBooleanAttribute(java.lang.String name)

		return getLongAttribute(name) != 0;
	
public booleangetBooleanParameter(java.lang.String name)

		return( getLongParameter( name ) != 0 );
	
public org.gudy.azureus2.core3.category.CategorygetCategory()

	    return category;
	
public java.lang.StringgetDisplayName()

    	return this.getStringAttribute(AT_DISPLAY_NAME);
    
public DownloadManagergetDownloadManager()

		return( download_manager );
	
private static DownloadManagerStategetDownloadState(DownloadManagerImpl download_manager, TOTorrent original_torrent, TorrentUtils.ExtendedTorrent target_torrent)



	  
	
			
								
			 
	
		 
	
		byte[]	hash	= target_torrent.getHash();
		
		DownloadManagerStateImpl	res	= null;
		
		try{
			class_mon.enter();
		
			HashWrapper	hash_wrapper = new HashWrapper( hash );
			
			res = (DownloadManagerStateImpl)state_map.get(hash_wrapper); 
			
			if ( res == null ){
			
				res = new DownloadManagerStateImpl( download_manager, target_torrent );
									
				state_map.put( hash_wrapper, res );
				
			}else{
				
					// if original state was created without a download manager, 
					// bind it to this one
				
				if ( res.getDownloadManager() == null && download_manager != null ){
					
					res.setDownloadManager( download_manager );
				}
				
				if ( original_torrent != null ){
						
					res.mergeTorrentDetails( original_torrent );
				}
			}
		}finally{
			
			class_mon.exit();
		}
				
		return( res );
	
public static DownloadManagerStategetDownloadState(TOTorrent original_torrent)

		byte[]	torrent_hash = original_torrent.getHash();
		
		// System.out.println( "getDownloadState: hash = " + ByteFormatter.encodeString(torrent_hash));
		
		TorrentUtils.ExtendedTorrent saved_state	= null;
				
		File	saved_file = getStateFile( torrent_hash ); 
		
		if ( saved_file.exists()){
			
			try{
				saved_state = TorrentUtils.readDelegateFromFile( saved_file, false );
				
			}catch( Throwable e ){
				
				Debug.out( "Failed to load download state for " + saved_file, e );
			}
		}
		
			// if saved state not found then recreate from original torrent 
		
		if ( saved_state == null ){
		
			TorrentUtils.copyToFile( original_torrent, saved_file );
			
			saved_state = TorrentUtils.readDelegateFromFile( saved_file, false );
		}

		return( getDownloadState( null, original_torrent, saved_state ));
	
protected static DownloadManagerStategetDownloadState(DownloadManagerImpl download_manager, java.lang.String torrent_file, byte[] torrent_hash, boolean inactive)

		boolean	discard_pieces = state_map.size() > 32;
		
		// System.out.println( "getDownloadState: hash = " + (torrent_hash==null?"null":ByteFormatter.encodeString(torrent_hash) + ", file = " + torrent_file ));

		TOTorrent						original_torrent	= null;
		TorrentUtils.ExtendedTorrent 	saved_state			= null;
		
			// first, if we already have the hash then see if we can load the saved state
		
		if ( torrent_hash != null ){
			
			File	saved_file = getStateFile( torrent_hash ); 
		
			if ( saved_file.exists()){
				
				try{
					Map	cached_state = (Map)global_state_cache.remove( new HashWrapper( torrent_hash ));
					
					if ( cached_state != null ){
						
						CachedStateWrapper wrapper = new CachedStateWrapper( download_manager, torrent_file, torrent_hash, cached_state, inactive );
						
						global_state_cache_wrappers.add( wrapper );
						
						saved_state	= wrapper;
						
					}else{
						
						saved_state = TorrentUtils.readDelegateFromFile( saved_file, discard_pieces );
					}
					
				}catch( Throwable e ){
					
					Debug.out( "Failed to load download state for " + saved_file );
				}
			}
		}
		
			// if saved state not found then recreate from original torrent if required
		
		if ( saved_state == null ){
		
			original_torrent = TorrentUtils.readDelegateFromFile( new File(torrent_file), discard_pieces );
			
			torrent_hash = original_torrent.getHash();
			
			File	saved_file = getStateFile( torrent_hash ); 
			
			if ( saved_file.exists()){
				
				try{
					saved_state = TorrentUtils.readDelegateFromFile( saved_file, discard_pieces );
					
				}catch( Throwable e ){
					
					Debug.out( "Failed to load download state for " + saved_file );
				}
			}
			
			if ( saved_state == null ){
						
					// we must copy the torrent as we want one independent from the
					// original (someone might still have references to the original
					// and do stuff like write it somewhere else which would screw us
					// up)
				
				TorrentUtils.copyToFile( original_torrent, saved_file );
				
				saved_state = TorrentUtils.readDelegateFromFile( saved_file, discard_pieces );
			}
		}

		DownloadManagerState res = getDownloadState( download_manager, original_torrent, saved_state );
		
		if ( inactive ){
			
			res.setActive( false );
		}
		
		return( res );
	
public static DownloadManagerStategetDownloadState(DownloadManager dm)

		return( new nullState(dm));
	
public java.io.FilegetFileLink(java.io.File link_source)

		return((File)getFileLinks().get(link_source));
	
public com.aelitis.azureus.core.util.CaseSensitiveFileMapgetFileLinks()

		List	values = getListAttributeSupport( AT_FILE_LINKS );

		CaseSensitiveFileMap	res = new CaseSensitiveFileMap();
		
		for (int i=0;i<values.size();i++){
			
			String	entry = (String)values.get(i);
		
			int	sep = entry.indexOf( "\n" );
			
			if ( sep != -1 ){
				
				File target = (sep == entry.length()-1)?null:new File( entry.substring( sep+1 ));
				
				res.put( new File( entry.substring(0,sep)), target );
			}
		}
		
		return( res );
	
public booleangetFlag(long flag)

		long	value = getLongAttribute( AT_FLAGS );
	
		return(( value & flag ) != 0 );
	
protected static java.io.FilegetGlobalStateFile()

		return( new File( ACTIVE_DIR, "cache.dat" ));
	
public intgetIntAttribute(java.lang.String name)

		return (int)getLongAttribute(name);
	
public intgetIntParameter(java.lang.String name)

		return( (int)getLongParameter( name ));
	
public java.lang.String[]getListAttribute(java.lang.String attribute_name)

		if ( attribute_name == AT_NETWORKS ){
			
			return( getNetworks());
			
		}else if ( attribute_name == AT_PEER_SOURCES ){
		
			return( getPeerSources());
			
		}else{
			
			List	l = getListAttributeSupport( attribute_name );
			
			if ( l == null ){
				
				return( null );
			}
			
			String[]	res = new String[l.size()];
			
			for (int i=0;i<l.size();i++){
				
				Object	 o = l.get(i);
				
				if ( o instanceof String ){
					
					res[i] = (String)o;
					
				}else{
					
					Debug.out( "getListAttribute( " + attribute_name + ") - object isnt String - " + o );
					
					return( null );
				}
			}
			
			return( res );
		}
	
protected java.util.ListgetListAttributeSupport(java.lang.String attribute_name)

		informWillRead( attribute_name );
		
		try{
			this_mon.enter();
					
			List	res = new ArrayList();
	
			List	values = (List)attributes.get( attribute_name );
		
			if ( values != null ){
				
				for (int i=0;i<values.size();i++){
				
					Object	o = values.get(i);
					
					if ( o instanceof byte[] ){
						
						byte[]	bytes = (byte[])o;
						
						try{
							res.add( new String( bytes, Constants.DEFAULT_ENCODING ));
							
						}catch( UnsupportedEncodingException e ){
							
							Debug.printStackTrace(e);					
						}
					}else if ( o instanceof String ){
						
						res.add( o );
					}
				}
			}
		
			return( res );
			
		}finally{
			
			this_mon.exit();
		}
	
public longgetLongAttribute(java.lang.String attribute_name)

		informWillRead( attribute_name );
		
		try{
			this_mon.enter();
			
			Long	l = (Long)attributes.get( attribute_name );
			
			if ( l == null ){
				
				Object def = default_attributes.get( attribute_name );
				
				if ( def != null ){
					
					if ( def instanceof Long ){
						
						return(((Long)def).longValue());
						
					}else if ( def instanceof Integer ){
						
						return(((Integer)def).longValue());
						
					}else{
						
						Debug.out( "unknown default type " + def );
					}
				}
				
				return( 0 );
			}
			
			return( l.longValue());
			
		}finally{
			
			this_mon.exit();
		}
	
public longgetLongParameter(java.lang.String name)

		try{
			this_mon.enter();
		
			Object	value = parameters.get( name );
	
			if ( value == null ){
				
				value = default_parameters.get( name );
				
				if ( value == null ){
					
					Debug.out( "Unknown parameter '" + name + "' - must be defined in DownloadManagerState" );
				
					return( 0 );
					
				}else{
					
						// default overrides
					
						// **** note - if you add to these make sure you extend the parameter listeners
						// registered as well (see addParameterListeners)
					
					if ( name == PARAM_MAX_UPLOADS_WHEN_SEEDING_ENABLED ){
						
						if ( COConfigurationManager.getBooleanParameter( "enable.seedingonly.maxuploads" )){
							
							value = new Boolean( true );
						}
						
					}else if ( name == PARAM_MAX_UPLOADS_WHEN_SEEDING ){
						
						int	def = COConfigurationManager.getIntParameter( "Max Uploads Seeding" );
						
						value = new Integer( def );
											
					}else if ( name == PARAM_MAX_UPLOADS ){
						
						int	def = COConfigurationManager.getIntParameter("Max Uploads" );
						
						value = new Integer( def );
						
					}else if ( name == PARAM_MAX_PEERS ){
						
						int	def = COConfigurationManager.getIntParameter( "Max.Peer.Connections.Per.Torrent" );
						
						value = new Integer( def );
						
					}else if ( name == PARAM_MAX_PEERS_WHEN_SEEDING_ENABLED ){
						
						if ( COConfigurationManager.getBooleanParameter( "Max.Peer.Connections.Per.Torrent.When.Seeding.Enable" )){
								
							value = new Boolean( true );
						}

					}else if ( name == PARAM_MAX_PEERS_WHEN_SEEDING ){
						
						int	def = COConfigurationManager.getIntParameter( "Max.Peer.Connections.Per.Torrent.When.Seeding" );
						
						value = new Integer( def );
					}else if ( name == PARAM_MAX_SEEDS)
					{
						value = new Integer(COConfigurationManager.getIntParameter( "Max Seeds Per Torrent" ));
					}
				}
			}
			
			if ( value instanceof Boolean ){
				
				return(((Boolean)value).booleanValue()?1:0);
				
			}else if ( value instanceof Integer ){
				
				return( ((Integer)value).longValue());
				
			}else if ( value instanceof Long ){
				
				return( ((Long)value).longValue());
			}
			
			Debug.out( "Invalid parameter value for '" + name + "' - " + value );
			
			return( 0 );
			
		}finally{
			
			this_mon.exit();
		}
	
public java.util.MapgetMapAttribute(java.lang.String attribute_name)

		informWillRead( attribute_name );
		
		try{
			this_mon.enter();
		
			Map	value = (Map)attributes.get( attribute_name );
			
			return( value );
			
		}finally{
			
			this_mon.exit();
		}
	
public java.lang.String[]getNetworks()

		List	values = getListAttributeSupport( AT_NETWORKS );
		
		List	res = new ArrayList();
		
			// map back to the constants to allow == comparisons
		
		for (int i=0;i<values.size();i++){
			
			String	nw = (String)values.get(i);
			
			for (int j=0;j<AENetworkClassifier.AT_NETWORKS.length;j++){
			
				String	nn = AENetworkClassifier.AT_NETWORKS[j];
		
				if ( nn.equals( nw )){
					
					res.add( nn );
				}
			}
		}
		
		String[]	x = new String[res.size()];
		
		res.toArray(x);
		
		return( x );
	
public java.lang.String[]getPeerSources()

		List	values = getListAttributeSupport( AT_PEER_SOURCES );
		
		List	res = new ArrayList();
		
			// map back to the constants to allow == comparisons
		
		for (int i=0;i<values.size();i++){
			
			String	ps = (String)values.get(i);
			
			for (int j=0;j<PEPeerSource.PS_SOURCES.length;j++){
			
				String	x = PEPeerSource.PS_SOURCES[j];
		
				if ( x.equals( ps )){
					
					res.add( x );
				}
			}
		}
		
		String[]	x = new String[res.size()];
		
		res.toArray(x);
		
		return( x );
	
public java.lang.StringgetPrimaryFile()

		String sPrimary = this.getStringAttribute(AT_PRIMARY_FILE);
		// Only recheck when file doesn't exists if this is the first check
		// of the session, because the file may never exist and we don't want
		// to continuously go through the fileinfos
		if (sPrimary == null
				|| sPrimary.length() == 0
				|| (firstPrimaryFileRead && !new File(sPrimary).exists() 
						&& download_manager.getStats().getDownloadCompleted(true) != 0)) {
			DiskManagerFileInfo[] fileInfo = download_manager.getDiskManagerFileInfo();
			if (fileInfo.length > 0) {
				int idxBiggest = -1;
				long lBiggest = -1;
				for (int i = 0; i < fileInfo.length && i < 10; i++) {
					if (!fileInfo[i].isSkipped() && fileInfo[i].getLength() > lBiggest) {
						lBiggest = fileInfo[i].getLength();
						idxBiggest = i;
					}
				}
				if (idxBiggest >= 0) {
					sPrimary = fileInfo[idxBiggest].getFile(true).getPath();
				}
			}
			// System.out.println("calc getPrimaryFile " + sPrimary + ": " + download_manager.getDisplayName());
		}

		if (sPrimary == null) {
			sPrimary = "";
		}
		
		if (firstPrimaryFileRead) {
			firstPrimaryFileRead = false;
		}
		setPrimaryFile(sPrimary);
		return sPrimary;
	
public java.lang.StringgetRelativeSavePath()

    	return this.getStringAttribute(AT_RELATIVE_SAVE_PATH);
    
public java.util.MapgetResumeData()

		try{
			this_mon.enter();
		
			return( torrent.getAdditionalMapProperty(RESUME_KEY));
		
		}finally{
			
			this_mon.exit();
		}
	
public java.io.FilegetStateFile(java.lang.String name)

		try{
			File	parent = new File( ACTIVE_DIR, ByteFormatter.encodeString( torrent.getHash()));
		
			return( new File( parent, name ));

		}catch( Throwable e ){

			Debug.printStackTrace(e);
			
			return( null );
		}
	
protected static java.io.FilegetStateFile(byte[] torrent_hash)

		return( new File( ACTIVE_DIR, ByteFormatter.encodeString( torrent_hash ) + ".dat" ));
	
protected java.lang.StringgetStringAttribute(java.lang.String attribute_name)

		informWillRead( attribute_name );
		
		try{
			this_mon.enter();
		
			if ( !(attributes.get( attribute_name) instanceof byte[] )){
				
				return( null );
			}
			
			byte[]	bytes = (byte[])attributes.get( attribute_name );
			
			if ( bytes == null ){
				
				return( null );
			}
			
			try{
				return( new String( bytes, Constants.DEFAULT_ENCODING ));
				
			}catch( UnsupportedEncodingException e ){
				
				Debug.printStackTrace(e);
				
				return( null );
			}
		}finally{
			
			this_mon.exit();
		}
	
public TOTorrentgetTorrent()

		return( torrent );
	
public java.lang.StringgetTrackerClientExtensions()

		return( getStringAttribute( AT_TRACKER_CLIENT_EXTENSIONS ));
	
public java.util.MapgetTrackerResponseCache()

		if ( tracker_response_cache == null ){
		
			tracker_response_cache	= torrent.getAdditionalMapProperty( TRACKER_CACHE_KEY );
			
			if ( tracker_response_cache == null ){
			
				tracker_response_cache	= new HashMap();
			}
		}
		
		return( tracker_response_cache );
	
public java.lang.StringgetUserComment()

    	return this.getStringAttribute(AT_USER_COMMENT);
    
public booleanhasAttribute(java.lang.String name)

		try{
	
			this_mon.enter();
			
			if ( attributes == null) {return false;}
			
			return attributes.containsKey(name);
	
		}finally{
			
			this_mon.exit();
		}
	
protected voidinformWillRead(java.lang.String attribute_name)

			// avoid potential recursion will a will-be-read causing a write that then
			// causes a further will-be-read...
		
		boolean	do_it = false;
	
		try{
			
			try{
				this_mon.enter();
				
				if ( !will_be_read_list.contains( attribute_name )){
					
					do_it	= true;
					
					will_be_read_list.add( attribute_name );
				}
			}finally{
				
				this_mon.exit();
				
			}
		
			if ( do_it ){
				
				for (int i=0;i<listeners.size();i++){
					
					try{
						((DownloadManagerStateListener)listeners.get(i)).stateChanged(
							this,
							new DownloadManagerStateEvent()
							{
								public int
								getType()
								{
									return( DownloadManagerStateEvent.ET_ATTRIBUTE_WILL_BE_READ );
								}
								
								public Object
								getData()
								{
									return( attribute_name );
								}
							});
					}catch( Throwable e ){
						
						Debug.printStackTrace(e);
					}
				}
			}
		}finally{
			
			if ( do_it ){
				
				try{
					this_mon.enter();
					
					will_be_read_list.remove( attribute_name );
					
				}finally{
					
					this_mon.exit();
				}
			}
		}
	
protected voidinformWritten(java.lang.String attribute_name)

		for (int i=0;i<listeners.size();i++){
			
			try{
				((DownloadManagerStateListener)listeners.get(i)).stateChanged(
					this,
					new DownloadManagerStateEvent()
					{
						public int
						getType()
						{
							return( DownloadManagerStateEvent.ET_ATTRIBUTE_WRITTEN );
						}
						
						public Object
						getData()
						{
							return( attribute_name );
						}
					});
			}catch( Throwable e ){
				
				Debug.printStackTrace(e);
			}
		}
	
public booleanisNetworkEnabled(java.lang.String network)

	    List	values = getListAttributeSupport( AT_NETWORKS );
	    return values.contains(network);
	  
public booleanisOurContent()

		// HACK!
		Map mapAttr = getMapAttribute("Plugin.azdirector.ContentMap");

		return mapAttr != null
				&& mapAttr.containsKey("DIRECTOR PUBLISH");
	
public booleanisPeerSourceEnabled(java.lang.String peerSource)

		List	values = getListAttributeSupport( AT_PEER_SOURCES );
		
		return values.contains(peerSource);
	
public booleanisPeerSourcePermitted(java.lang.String peerSource)

		if ( peerSource == PEPeerSource.PS_DHT ){
			
			if ( !TorrentUtils.getDHTTrackerEnabled()){
				
				return( false );
			}
		}
		
		if ( TorrentUtils.getPrivate( torrent )){
			
			if ( 	peerSource == PEPeerSource.PS_DHT ||
					peerSource == PEPeerSource.PS_OTHER_PEER ){
				
				return( false );
			}
			
		}else if ( !TorrentUtils.getDHTBackupEnabled( torrent )){
			
			if ( peerSource == PEPeerSource.PS_DHT ){
				
				return( false );
			}
		}
		
		return( true );
	
public booleanisResumeDataComplete()

			// this is a cache of resume state to speed up startup
		
		long	state = getLongAttribute( AT_RESUME_STATE );
		
		if ( state == 0 ){
			
			// don't know
			
			boolean complete = DiskManagerFactory.isTorrentResumeDataComplete( this );
			
			setLongAttribute( AT_RESUME_STATE, complete?2:1 );
			
			return( complete );
			
		}else{
			
			return( state == 2 );
		}
	
public static voidloadGlobalStateCache()

		File file = getGlobalStateFile();
		
		if ( !file.canRead()){
			
			return;
		}
		
		try{
			
			BufferedInputStream is = new BufferedInputStream( new GZIPInputStream( new FileInputStream( file )));
			
			try{
				
				Map	map = BDecoder.decode( is );
				
				List	cache = (List)map.get( "state" );
				
				if ( cache != null ){
					
					for (int i=0;i<cache.size();i++){
						
						Map	entry = (Map)cache.get(i);
						
						byte[]	hash = (byte[])entry.get( "hash" );
						
						if ( hash != null ){
							
							global_state_cache.put( new HashWrapper( hash ), entry );
						}
					}
				}
				
				is.close();
				
			}catch( IOException e){
				
				Debug.printStackTrace( e );
			}finally{
				
				try{
					is.close();
					
				}catch( Throwable e ){
				}
			}
		}catch( Throwable e ){
			
			Debug.printStackTrace( e );
		}
	
protected voidmergeTorrentDetails(TOTorrent other_torrent)

		try{		
			boolean	write = TorrentUtils.mergeAnnounceURLs( other_torrent, torrent );
					
			// System.out.println( "DownloadManagerState:mergeTorrentDetails -> " + write );
			
			if ( write ){
				
				save();
				
				if ( download_manager != null ){
					
					TRTrackerAnnouncer	client = download_manager.getTrackerClient();

					if ( client != null ){
										
						// pick up any URL changes
					
						client.resetTrackerUrl( false );
					}
				}
			}
		}catch( Throwable e ){
				
			Debug.printStackTrace( e );
		}
	
public voidparameterChanged(java.lang.String parameterName)

			// get any listeners to pick up new values as their defaults are based on core params
		
		informWritten( AT_PARAMETERS );
	
public booleanparameterExists(java.lang.String name)

		return parameters.containsKey(name);
	
public voidremoveListener(DownloadManagerStateListener l)

		listeners.remove(l);
	
protected voidremoveListeners()

		COConfigurationManager.removeParameterListener( "Max.Peer.Connections.Per.Torrent.When.Seeding", this );
		COConfigurationManager.removeParameterListener( "Max.Peer.Connections.Per.Torrent.When.Seeding.Enable", this );
		COConfigurationManager.removeParameterListener( "Max.Peer.Connections.Per.Torrent", this );
		COConfigurationManager.removeParameterListener( "Max Uploads", this );
		COConfigurationManager.removeParameterListener( "Max Uploads Seeding", this );
		COConfigurationManager.removeParameterListener( "Max Seeds Per Torrent", this );
		COConfigurationManager.removeParameterListener( "enable.seedingonly.maxuploads", this );
	
public voidsave()

 		boolean do_write;

		try {
			this_mon.enter();

			do_write = write_required;

			write_required = false;

		} finally {

			this_mon.exit();
		}

		if ( do_write ){

			try {
				// System.out.println( "writing download state for '" + new String(torrent.getName()));

				if (Logger.isEnabled())
					Logger.log(new LogEvent(torrent, LOGID, "Saving state for download '"
							+ TorrentUtils.getLocalisedName(torrent) + "'"));

				torrent.setAdditionalMapProperty( ATTRIBUTE_KEY, attributes );
				
				TorrentUtils.writeToFile(torrent, true);

			} catch (Throwable e) {
				Logger.log(new LogEvent(torrent, LOGID, "Saving state", e));
			}
		} else {

			// System.out.println( "not writing download state for '" + new String(torrent.getName()));
		}
	
public static voidsaveGlobalStateCache()

		try{
			class_mon.enter();

			Map	map = new HashMap();
			
			List	cache = new ArrayList();
			
			map.put( "state", cache );

			Iterator	it = state_map.values().iterator();
			
			while( it.hasNext()){
				
				DownloadManagerState dms = (DownloadManagerState)it.next();
				
				DownloadManager dm = dms.getDownloadManager();
				
				if ( dm != null && dm.isPersistent()){
					
					try{
						Map	state = CachedStateWrapper.export( dms );
					
						cache.add( state );
						
					}catch( Throwable e ){
						
						Debug.printStackTrace( e );
					}
				}
			}
			
			GZIPOutputStream	os = new GZIPOutputStream( new FileOutputStream( getGlobalStateFile()));
			
			try{
				
				os.write( BEncoder.encode( map ));
				
				os.close();
				
			}catch( IOException e ){
				
				Debug.printStackTrace( e );
			
				try{
					os.close();
					
				}catch( IOException f ){
					
				}
			}
		}catch( Throwable e ){
			
			Debug.printStackTrace( e );
			
		}finally{
			
			class_mon.exit();

		}
	
public voidsetActive(boolean active)

		torrent.setDiscardFluff( !active );
	
public voidsetAttribute(java.lang.String name, java.lang.String value)

	
		if ( name.equals( AT_CATEGORY )){
			
			if ( value == null ){
				
				setCategory( null );
				
			}else{
				Category	cat = CategoryManager.getCategory( value );
			
				if ( cat == null ){
				
					cat = CategoryManager.createCategory( value );
					
				}
								
				setCategory( cat );
			}
			return;
		}
		
		if (name.equals(AT_RELATIVE_SAVE_PATH)) {
			if (value.length() > 0) {
				File relative_path_file = new File(value);
				relative_path_file = DownloadManagerDefaultPaths.normaliseRelativePath(relative_path_file);
				value = (relative_path_file == null) ? "" : relative_path_file.getPath();
			}
		}
		
		setStringAttribute( name, value );
	
public voidsetBooleanAttribute(java.lang.String name, boolean value)

		setLongAttribute(name, (value ? 1 : 0));
	
public voidsetBooleanParameter(java.lang.String name, boolean value)

		setLongParameter( name, value?1:0 );
	
public voidsetCategory(org.gudy.azureus2.core3.category.Category cat)

		if ( cat == CategoryManager.getCategory(Category.TYPE_UNCATEGORIZED)){
			
			cat	= null;
		}
		
		if ( cat == category ){
			
			return;
		}
	  
		if (cat != null && cat.getType() != Category.TYPE_USER){
	    
			cat = null;
		}
		
		Category oldCategory = (category == null)?CategoryManager.getCategory(Category.TYPE_UNCATEGORIZED):category;
				
		category = cat;
	  
		if (oldCategory != null ){
			
			oldCategory.removeManager( this );
  		}
		
		if (category != null ){
			
			category.addManager( this );
		}
  	
		if ( category != null && category.getType() == Category.TYPE_USER ){
			
			setStringAttribute( AT_CATEGORY, category.getName());
			
		}else{
			
			setStringAttribute( AT_CATEGORY, null );
		}
	
public voidsetDisplayName(java.lang.String value)

    	this.setStringAttribute(AT_DISPLAY_NAME, value);
    
protected voidsetDownloadManager(DownloadManagerImpl dm)

		download_manager	= dm;
	
public voidsetFileLink(java.io.File link_source, java.io.File link_destination)

		CaseSensitiveFileMap	links = getFileLinks();
		
		File	existing = (File)links.get(link_source);
		
		if ( link_destination == null ){
			
			if ( existing == null ){
				
				return;
			}
		}else if ( existing != null && existing.equals( link_destination )){
			
			return;
		}
		
		links.put( link_source, link_destination );
		
		List	list = new ArrayList();
		
		Iterator	it = links.keySetIterator();
		
		while( it.hasNext()){
			
			File	source = (File)it.next();
			File	target = (File)links.get(source);
			
			String	str = source + "\n" + (target==null?"":target.toString());
			
			list.add( str );
		}
		
		setListAttribute( AT_FILE_LINKS, list );
	
public voidsetFlag(long flag, boolean set)

		long	old_value = getLongAttribute( AT_FLAGS );
	
		long	new_value;
		
		if ( set ){
			
			new_value = old_value | flag;
			
		}else{
			
			new_value = old_value & ~flag;
		}
		
		if ( old_value != new_value ){
			
			setLongAttribute( AT_FLAGS, new_value );
		}
	
public voidsetIntAttribute(java.lang.String name, int value)

		setLongAttribute(name, value);
	
public voidsetIntParameter(java.lang.String name, int value)

		setLongParameter( name, value );
	
public voidsetListAttribute(java.lang.String name, java.lang.String[] values)

		List	list = values==null?null:new ArrayList();
		
		if ( list != null ){
			
			for (int i=0;i<values.length;i++){
				
				list.add( values[i]);
			}
		}
		
		setListAttribute( name, list );
	
protected voidsetListAttribute(java.lang.String attribute_name, java.util.List attribute_value)

		boolean	changed	= false;

		try{
			this_mon.enter();
						
			if ( attribute_value == null ){
				
				if ( attributes.containsKey( attribute_name )){
				
					attributes.remove( attribute_name );
				
					changed	= true;
				}
			}else{
			
				List old_value = getListAttributeSupport( attribute_name );
									
				if ( old_value == null || old_value.size() != attribute_value.size()){
					
					attributes.put( attribute_name, attribute_value );
						
					changed	= true;
					
				}else{
					
					if ( old_value == attribute_value ){
						
						Debug.out( "setListAttribute: should clone?" );
					}
					
					changed = !BEncoder.listsAreIdentical( old_value, attribute_value ); 
					
					if ( changed ){
						
						attributes.put( attribute_name, attribute_value );
					}
				}
			}
		}finally{
			
			this_mon.exit();
		}
		
		if ( changed ){
			
			write_required	= true;
			
			informWritten( attribute_name );
		}
	
public voidsetLongAttribute(java.lang.String attribute_name, long attribute_value)

		boolean	changed	= false;
		
		try{
			this_mon.enter();
		
			Long	existing_value = (Long)attributes.get( attribute_name );
					
			if ( 	existing_value == null ||
					existing_value.longValue() != attribute_value ){
					
				attributes.put( attribute_name, new Long( attribute_value) );
									
				changed	= true;
			}
		}finally{
			
			this_mon.exit();
		}
		
		if ( changed ){
			
			write_required	= true;
			
			informWritten( attribute_name );
		}
	
public voidsetLongParameter(java.lang.String name, long value)

		Object	default_value = default_parameters.get( name );

		if ( default_value == null ){
			
			Debug.out( "Unknown parameter '" + name + "' - must be defined in DownloadManagerState" );
		}
			
		try{
			this_mon.enter();
		
				// gotta clone here otherwise we update the underlying  map and the setMapAttribute code
				// doesn't think it has changed
			
			parameters	= new HashMap( parameters );
			
			parameters.put( name, new Long(value));
			
			setMapAttribute( AT_PARAMETERS, parameters );
			
		}finally{
			
			this_mon.exit();
		}
	
public voidsetMapAttribute(java.lang.String attribute_name, java.util.Map attribute_value)

		setMapAttribute( attribute_name, attribute_value, false );
	
protected voidsetMapAttribute(java.lang.String attribute_name, java.util.Map attribute_value, boolean disable_change_notification)

		boolean	changed	= false;

		try{
			this_mon.enter();
				
			if ( attribute_value == null ){
				
				if ( attributes.containsKey( attribute_name )){
				
					attributes.remove( attribute_name );
				
					changed	= true;
				}
			}else{
			
				Map old_value = getMapAttribute( attribute_name );
									
				if ( old_value == null || old_value.size() != attribute_value.size()){
					
					attributes.put( attribute_name, attribute_value );
						
					changed	= true;
					
				}else{
					
					if ( old_value == attribute_value ){
						
						Debug.out( "setMapAttribute: should clone?" );
					}
					
					changed = !BEncoder.mapsAreIdentical( old_value, attribute_value ); 
					
					if ( changed ){
						
						attributes.put( attribute_name, attribute_value );
					}
				}
			}
		}finally{
			
			this_mon.exit();
		}
		
		if ( changed && !disable_change_notification ){
			
			write_required	= true;
			
			informWritten( attribute_name );
		}
	
public voidsetNetworkEnabled(java.lang.String network, boolean enabled)

	    List	values = getListAttributeSupport( AT_NETWORKS );
	    boolean alreadyEnabled = values.contains(network);
	    List	l = new ArrayList();
	    	  
	    if(enabled && !alreadyEnabled) {	      	
	      for (int i=0;i<values.size();i++){
	        l.add(values.get(i));
	      }	
	      l.add(network);
	      setListAttribute( AT_NETWORKS, l );
	    }
	    if(!enabled && alreadyEnabled) {
	      for (int i=0;i<values.size();i++){
	        l.add(values.get(i));
	      }	
	      l.remove(network);
	      setListAttribute( AT_NETWORKS, l );
	    }
	  
public voidsetNetworks(java.lang.String[] networks)

		if ( networks == null ){
			
			networks = new String[0];
		}
		
		List	l = new ArrayList();
		
		for (int i=0;i<networks.length;i++){
			
			l.add( networks[i]);
		}
		
		setListAttribute( AT_NETWORKS, l );
	
public voidsetParameterDefault(java.lang.String name)

		try{
			this_mon.enter();
		
			Object	value = parameters.get( name );
			
			if ( value == null ){
		
				return;
			}
				
				// gotta clone here otherwise we update the underlying  map and the setMapAttribute code
				// doesn't think it has changed
		
			parameters	= new HashMap( parameters );
			
			parameters.remove( name );
			
		}finally{
			
			this_mon.exit();
		}

		setMapAttribute( AT_PARAMETERS, parameters );
	
public voidsetPeerSourceEnabled(java.lang.String source, boolean enabled)

		  if ( enabled && !isPeerSourcePermitted( source )){
			  
			  return;
		  }
		  
		  List	values = getListAttributeSupport( AT_PEER_SOURCES );
		  
		  boolean alreadyEnabled = values.contains(source);
		  
		  List	l = new ArrayList();
  	  
		  if(enabled && !alreadyEnabled) {	      	
		    for (int i=0;i<values.size();i++){
		      l.add(values.get(i));
		    }	
		    l.add(source);
		    setListAttribute( AT_PEER_SOURCES, l );
		  }
		  if(!enabled && alreadyEnabled) {
		    for (int i=0;i<values.size();i++){
		      l.add(values.get(i));
		    }	
		    l.remove(source);
		    setListAttribute( AT_PEER_SOURCES, l );
		  }
	  
public voidsetPeerSources(java.lang.String[] ps)

		if ( ps == null ){
			
			ps = new String[0];
		}
		
		List	l = new ArrayList();
		
		for (int i=0;i<ps.length;i++){
			
			String	p = ps[i];
			
			if ( isPeerSourcePermitted(p)){
				
				l.add( ps[i]);
			}
		}
		
		setListAttribute( AT_PEER_SOURCES, l );
	
public voidsetPrimaryFile(java.lang.String fileFullPath)

param
primary

		this.setStringAttribute(AT_PRIMARY_FILE, fileFullPath);
	
public voidsetRelativeSavePath(java.lang.String path)

		this.setStringAttribute(AT_RELATIVE_SAVE_PATH, path);
	
public voidsetResumeData(java.util.Map data)

		try{
			this_mon.enter();
		
			// System.out.println( "setting download state/resume data for '" + new String(torrent.getName()));

			if ( data == null ){
				
				setLongAttribute( AT_RESUME_STATE, 1 );
				
				torrent.removeAdditionalProperty( RESUME_KEY );
				
			}else{
				
				torrent.setAdditionalMapProperty( RESUME_KEY, data );
				
				boolean complete = DiskManagerFactory.isTorrentResumeDataComplete( this );
				
				setLongAttribute( AT_RESUME_STATE, complete?2:1 );
			}
			
			write_required	= true;
			
		}finally{
			
			this_mon.exit();
		}
		
			// we need to ensure this is persisted now as it has implications regarding crash restarts etc
	
		save();
	
protected voidsetStringAttribute(java.lang.String attribute_name, java.lang.String attribute_value)

		boolean	changed	= false;

		try{
			this_mon.enter();		
			
			if ( attribute_value == null ){
				
				if ( attributes.containsKey( attribute_name )){
				
					attributes.remove( attribute_name );
				
					changed	= true;
				}
			}else{
			
				try{
					byte[]	existing_bytes = (byte[])attributes.get( attribute_name );
					
					byte[]	new_bytes = attribute_value.getBytes( Constants.DEFAULT_ENCODING );
					
					if ( 	existing_bytes == null || 
							!Arrays.equals( existing_bytes, new_bytes )){
					
						attributes.put( attribute_name, new_bytes );
						
						changed	= true;
					}
					
				}catch( UnsupportedEncodingException e ){
					
					Debug.printStackTrace(e);
				}
			}
		}finally{
			
			this_mon.exit();
		}
		
		if ( changed ){
			
			write_required	= true;
			
			informWritten( attribute_name );
		}
	
public voidsetTrackerClientExtensions(java.lang.String value)

		setStringAttribute( AT_TRACKER_CLIENT_EXTENSIONS, value );
	
public voidsetTrackerResponseCache(java.util.Map value)

			// ensure initial value read
		
		getTrackerResponseCache();
		
		try{
			this_mon.enter();
		
			// System.out.println( "setting download state/tracker cache for '" + new String(torrent.getName()));

			boolean	changed = !BEncoder.mapsAreIdentical( value, tracker_response_cache );
		
			if ( changed ){
				
				write_required	= true;
				
				tracker_response_cache	 = value;
		
				torrent.setAdditionalMapProperty( TRACKER_CACHE_KEY, tracker_response_cache );
			}	
			
		}finally{
			
			this_mon.exit();
		}
	
public voidsetUserComment(java.lang.String value)

    	this.setStringAttribute(AT_USER_COMMENT, value);