/*
* Created on 15-Nov-2004
* Created by Paul Gardner
* Copyright (C) 2004, 2005, 2006 Aelitis, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* AELITIS, SAS au capital de 46,603.30 euros
* 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
*
*/
package org.gudy.azureus2.core3.download.impl;
import java.io.*;
import java.net.URL;
import java.util.*;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
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.ParameterListener;
import org.gudy.azureus2.core3.disk.DiskManagerFactory;
import org.gudy.azureus2.core3.disk.DiskManagerFileInfo;
import org.gudy.azureus2.core3.download.*;
import org.gudy.azureus2.core3.logging.LogEvent;
import org.gudy.azureus2.core3.logging.LogIDs;
import org.gudy.azureus2.core3.logging.LogRelation;
import org.gudy.azureus2.core3.logging.Logger;
import org.gudy.azureus2.core3.peer.PEPeerSource;
import org.gudy.azureus2.core3.torrent.*;
import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncer;
import org.gudy.azureus2.core3.util.*;
import com.aelitis.azureus.core.AzureusCoreFactory;
import com.aelitis.azureus.core.util.CaseSensitiveFileMap;
import com.aelitis.azureus.core.util.CopyOnWriteList;
import com.aelitis.azureus.core.util.CopyOnWriteMap;
/**
* @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
*/
public class
DownloadManagerStateImpl
implements DownloadManagerState, ParameterListener
{
private static final int VER_INCOMING_PEER_SOURCE = 1;
private static final int VER_CURRENT = VER_INCOMING_PEER_SOURCE;
private static final LogIDs LOGID = LogIDs.DISK;
private static final String RESUME_KEY = "resume";
private static final String TRACKER_CACHE_KEY = "tracker_cache";
private static final String ATTRIBUTE_KEY = "attributes";
private static final String AZUREUS_PROPERTIES_KEY = "azureus_properties";
private static final File ACTIVE_DIR;
public static boolean SUPPRESS_FIXUP_ERRORS = false;
static{
ACTIVE_DIR = FileUtil.getUserFile( "active" );
if ( !ACTIVE_DIR.exists()){
FileUtil.mkdirs(ACTIVE_DIR);
}
}
private static Random random = RandomUtils.SECURE_RANDOM;
private static final Map default_parameters;
private static final Map default_attributes;
static{
default_parameters = new HashMap();
for (int i=0;i<PARAMETERS.length;i++){
default_parameters.put( PARAMETERS[i][0], PARAMETERS[i][1] );
}
default_attributes = new HashMap();
for (int i=0;i<ATTRIBUTE_DEFAULTS.length;i++){
default_attributes.put( ATTRIBUTE_DEFAULTS[i][0], ATTRIBUTE_DEFAULTS[i][1] );
}
// only add keys that will point to Map objects here!
TorrentUtils.registerMapFluff( new String[] {TRACKER_CACHE_KEY,RESUME_KEY} );
}
private static AEMonitor class_mon = new AEMonitor( "DownloadManagerState:class" );
private static Map state_map = new HashMap();
private static Map global_state_cache = new HashMap();
private static ArrayList global_state_cache_wrappers = new ArrayList();
private DownloadManagerImpl download_manager;
private final TorrentUtils.ExtendedTorrent torrent;
private boolean write_required;
private Category category;
private CopyOnWriteList listeners_cow = new CopyOnWriteList();
private CopyOnWriteMap listeners_read_map_cow = new CopyOnWriteMap();
private CopyOnWriteMap listeners_write_map_cow = new CopyOnWriteMap();
private Map parameters;
private Map attributes;
private AEMonitor this_mon = new AEMonitor( "DownloadManagerState" );
private boolean firstPrimaryFileRead = true;
private int supressWrites = 0;
private static ThreadLocal tls_wbr =
new ThreadLocal()
{
public Object
initialValue()
{
return( new ArrayList(1));
}
};
private static DownloadManagerState
getDownloadState(
DownloadManagerImpl download_manager,
TOTorrent original_torrent,
TorrentUtils.ExtendedTorrent target_torrent )
throws TOTorrentException
{
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 DownloadManagerState
getDownloadState(
TOTorrent original_torrent )
throws TOTorrentException
{
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 DownloadManagerState
getDownloadState(
DownloadManagerImpl download_manager,
String torrent_file,
byte[] torrent_hash,
boolean inactive )
throws TOTorrentException
{
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 );
}
protected static File
getStateFile(
byte[] torrent_hash )
{
return( new File( ACTIVE_DIR, ByteFormatter.encodeString( torrent_hash ) + ".dat" ));
}
protected static File
getGlobalStateFile()
{
return( new File( ACTIVE_DIR, "cache.dat" ));
}
public static void
loadGlobalStateCache()
{
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 );
}
}
public static void
saveGlobalStateCache()
{
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 static void
discardGlobalStateCache()
{
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();
global_state_cache_wrappers.trimToSize();
}
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();
}
// note that version will be -1 for the first time through this code
int version = getIntAttribute( AT_VERSION );
if ( version < VER_INCOMING_PEER_SOURCE ){
// migrate by adding incoming as enabled - only needed if we have any specified as other
// code takes care of the case where we have none
if ( getPeerSources().length > 0 ){
if ( PEPeerSource.isPeerSourceEnabledByDefault( PEPeerSource.PS_INCOMING )){
setPeerSourceEnabled( PEPeerSource.PS_INCOMING, true );
}
}else{
// set default for newly added torrent
setPeerSources( PEPeerSource.getDefaultEnabledPeerSources());
}
}
if ( version < VER_CURRENT ){
setIntAttribute( AT_VERSION, VER_CURRENT );
}
addListeners();
}
public void
parameterChanged(
String parameterName)
{
// get any listeners to pick up new values as their defaults are based on core params
informWritten( AT_PARAMETERS );
}
protected void
addListeners()
{
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 );
}
protected void
removeListeners()
{
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 DownloadManager
getDownloadManager()
{
return( download_manager );
}
protected void
setDownloadManager(
DownloadManagerImpl dm )
{
download_manager = dm;
}
public File
getStateFile( )
{
try{
File parent = new File( ACTIVE_DIR, ByteFormatter.encodeString( torrent.getHash()) + File.separatorChar);
return( StringInterner.internFile(parent));
}catch( Throwable e ){
Debug.printStackTrace(e);
return( null );
}
}
public void
clearTrackerResponseCache()
{
setTrackerResponseCache( new HashMap());
}
public Map getTrackerResponseCache() {
Map 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 void
setTrackerResponseCache(
Map value )
{
try{
this_mon.enter();
// System.out.println( "setting download state/tracker cache for '" + new String(torrent.getName()));
boolean changed = !BEncoder.mapsAreIdentical( value, getTrackerResponseCache() );
if ( changed ){
write_required = true;
torrent.setAdditionalMapProperty( TRACKER_CACHE_KEY, value );
}
}finally{
this_mon.exit();
}
}
public Map
getResumeData()
{
try{
this_mon.enter();
return( torrent.getAdditionalMapProperty(RESUME_KEY));
}finally{
this_mon.exit();
}
}
public void
clearResumeData()
{
setResumeData( null );
}
public void
setResumeData(
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();
}
public boolean
isResumeDataComplete()
{
// 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 TOTorrent
getTorrent()
{
return( torrent );
}
public void
setActive(
boolean active )
{
torrent.setDiscardFluff( !active );
}
public void discardFluff()
{
torrent.setDiscardFluff(true);
}
public void suppressStateSave(boolean suppress) {
if(suppress)
supressWrites++;
else if(supressWrites > 0)
supressWrites--;
}
public void
save()
{
if(supressWrites > 0)
return;
boolean do_write;
try {
this_mon.enter();
do_write = write_required;
if(write_required != false)
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 void
delete()
{
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();
}
}
protected void
mergeTorrentDetails(
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 void
setFlag(
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 boolean
getFlag(
long flag )
{
long value = getLongAttribute( AT_FLAGS );
return(( value & flag ) != 0 );
}
public long
getFlags()
{
return( getLongAttribute( AT_FLAGS ));
}
public boolean parameterExists(String name) {
return parameters.containsKey(name);
}
public void
setParameterDefault(
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 LightHashMap(parameters);
parameters.remove( name );
}finally{
this_mon.exit();
}
setMapAttribute( AT_PARAMETERS, parameters );
}
public long
getLongParameter(
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" ));
}else if ( name == PARAM_RANDOM_SEED ){
long rand = random.nextLong();
setLongParameter( name, rand );
value = new Long( rand );
}
}
}
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 void
setLongParameter(
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 LightHashMap(parameters);
parameters.put( name, new Long(value));
setMapAttribute( AT_PARAMETERS, parameters );
}finally{
this_mon.exit();
}
}
public int
getIntParameter(
String name )
{
return( (int)getLongParameter( name ));
}
public void
setIntParameter(
String name,
int value )
{
setLongParameter( name, value );
}
public boolean
getBooleanParameter(
String name )
{
return( getLongParameter( name ) != 0 );
}
public void
setBooleanParameter(
String name,
boolean value )
{
setLongParameter( name, value?1:0 );
}
public void
setAttribute(
String name,
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 String
getAttribute(
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
Category
getCategory()
{
return category;
}
public void
setCategory(
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 String
getTrackerClientExtensions()
{
return( getStringAttribute( AT_TRACKER_CLIENT_EXTENSIONS ));
}
public void
setTrackerClientExtensions(
String value )
{
setStringAttribute( AT_TRACKER_CLIENT_EXTENSIONS, value );
}
public String getDisplayName() {
return this.getStringAttribute(AT_DISPLAY_NAME);
}
public void setDisplayName(String value) {
this.setStringAttribute(AT_DISPLAY_NAME, value);
}
public String getUserComment() {
return this.getStringAttribute(AT_USER_COMMENT);
}
public void setUserComment(String value) {
this.setStringAttribute(AT_USER_COMMENT, value);
}
public String getRelativeSavePath() {
return this.getStringAttribute(AT_RELATIVE_SAVE_PATH);
}
public String getPrimaryFile() {
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;
int numChecked = 0;
for (int i = 0; i < fileInfo.length && numChecked < 10; i++) {
if (!fileInfo[i].isSkipped()) {
numChecked++;
if (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;
}
/**
* @param primary
*/
public void setPrimaryFile(String fileFullPath) {
this.setStringAttribute(AT_PRIMARY_FILE, fileFullPath);
}
public 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 boolean isNetworkEnabled(
String network) {
List values = getListAttributeSupport( AT_NETWORKS );
return values.contains(network);
}
public void
setNetworks(
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 void
setNetworkEnabled(
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 );
}
}
// peer sources
public 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 boolean
isPeerSourceEnabled(
String peerSource )
{
List values = getListAttributeSupport( AT_PEER_SOURCES );
return values.contains(peerSource);
}
public boolean
isPeerSourcePermitted(
String peerSource )
{
// no DHT for private torrents or explicitly prevented
if ( peerSource.equals( PEPeerSource.PS_DHT )){
if ( TorrentUtils.getPrivate( torrent ) ||
!TorrentUtils.getDHTBackupEnabled( torrent )){
return( false );
}
}
// no PEX for private torrents
if ( peerSource.equals( PEPeerSource.PS_OTHER_PEER )){
if ( TorrentUtils.getPrivate( torrent )){
return( false );
}
}
List values = getListAttributeSupport( AT_PEER_SOURCES_DENIED );
if ( values != null ){
if ( values.contains( peerSource )){
return( false );
}
}
return( true );
}
public void
setPeerSourcePermitted(
String peerSource,
boolean enabled )
{
if ( !getFlag( FLAG_ALLOW_PERMITTED_PEER_SOURCE_CHANGES )){
Logger.log(new LogEvent(torrent, LOGID, "Attempt to modify permitted peer sources denied as disabled '"
+ TorrentUtils.getLocalisedName(torrent) + "'"));
return;
}
if ( !enabled ){
setPeerSourceEnabled( peerSource, false );
}
List values = getListAttributeSupport( AT_PEER_SOURCES_DENIED );
if ( values == null ){
if ( !enabled ){
values = new ArrayList();
values.add( peerSource );
setListAttribute( AT_PEER_SOURCES_DENIED, values );
}
}else{
if ( enabled ){
values.remove( peerSource );
}else{
if ( !values.contains( peerSource )){
values.add( peerSource );
}
}
setListAttribute( AT_PEER_SOURCES_DENIED, values );
}
}
public void
setPeerSources(
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 void
setPeerSourceEnabled(
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 );
}
}
// links stuff
public void
setFileLink(
File link_source,
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 void
clearFileLinks()
{
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 File
getFileLink(
File link_source )
{
return((File)getFileLinks().get(link_source));
}
public CaseSensitiveFileMap
getFileLinks()
{
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 boolean isOurContent() {
// HACK!
Map mapAttr = getMapAttribute("Plugin.azdirector.ContentMap");
return mapAttr != null
&& mapAttr.containsKey("DIRECTOR PUBLISH");
}
// general stuff
protected String
getStringAttribute(
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();
}
}
protected void
setStringAttribute(
final String attribute_name,
final 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 long
getLongAttribute(
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 void
setLongAttribute(
final String attribute_name,
final 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 void
setListAttribute(
String name,
String[] values )
{
List list = values==null?null:Arrays.asList((Object[]) values.clone());
/*
if ( list != null ){
for (int i=0;i<values.length;i++){
list.add( values[i]);
}
}*/
setListAttribute( name, list );
}
public String getListAttribute(String name, int idx) {
if (name.equals(AT_NETWORKS) || name.equals(AT_PEER_SOURCES))
throw new UnsupportedOperationException("not supported right now, implement it yourself :P");
informWillRead(name);
try {
this_mon.enter();
List values = (List) attributes.get(name);
if(values == null || idx >= values.size() || idx < 0)
return null;
Object o = values.get(idx);
if (o instanceof byte[]) {
byte[] bytes = (byte[]) o;
String s = null;
try {
s = StringInterner.intern(new String(bytes, Constants.DEFAULT_ENCODING));
} catch (UnsupportedEncodingException e) {
Debug.printStackTrace(e);
}
if (s != null)
values.set(idx, s);
return s;
} else if (o instanceof String) {
return (String) o;
}
} finally {
this_mon.exit();
}
return null;
}
public String[]
getListAttribute(
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()];
try {
res = (String[])l.toArray(res);
} catch (ArrayStoreException e)
{
Debug.out( "getListAttribute( " + attribute_name + ") - object isnt String - " + e );
return( null );
}
return( res );
}
}
protected List
getListAttributeSupport(
String attribute_name )
{
informWillRead( attribute_name );
try{
this_mon.enter();
List values = (List)attributes.get( attribute_name );
List res = new ArrayList(values != null ? values.size() : 0);
if ( values != null ){
for (int i=0;i<values.size();i++){
Object o = values.get(i);
if ( o instanceof byte[] ){
byte[] bytes = (byte[])o;
String s = null;
try{
s = StringInterner.intern(new String( bytes, Constants.DEFAULT_ENCODING ));
}catch( UnsupportedEncodingException e ){
Debug.printStackTrace(e);
}
if(s != null)
{
res.add(s);
values.set(i, s);
}
}else if ( o instanceof String ){
res.add( o );
}
}
}
return( res );
}finally{
this_mon.exit();
}
}
protected void
setListAttribute(
final String attribute_name,
final 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 Map
getMapAttribute(
String attribute_name )
{
informWillRead( attribute_name );
try{
this_mon.enter();
Map value = (Map)attributes.get( attribute_name );
return( value );
}finally{
this_mon.exit();
}
}
public void
setMapAttribute(
final String attribute_name,
final Map attribute_value )
{
setMapAttribute( attribute_name, attribute_value, false );
}
protected void
setMapAttribute(
final String attribute_name,
final 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 boolean
hasAttribute(
String name )
{
try{
this_mon.enter();
if ( attributes == null) {return false;}
return attributes.containsKey(name);
}finally{
this_mon.exit();
}
}
// These methods just use long attributes to store data into.
public void
setIntAttribute(
String name,
int value)
{
setLongAttribute(name, value);
}
public int
getIntAttribute(
String name )
{
return (int)getLongAttribute(name);
}
public void
setBooleanAttribute(
String name,
boolean value )
{
setLongAttribute(name, (value ? 1 : 0));
}
public boolean
getBooleanAttribute(
String name )
{
return getLongAttribute(name) != 0;
}
public static DownloadManagerState
getDownloadState(
DownloadManager dm )
{
return( new nullState(dm));
}
protected void
informWritten(
final String attribute_name )
{
List listeners_ref = listeners_cow.getList();
for (int i=0;i<listeners_ref.size();i++){
try{
((DownloadManagerStateListener)listeners_ref.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);
}
}
listeners_ref = null;
CopyOnWriteList write_listeners = (CopyOnWriteList)listeners_write_map_cow.get(attribute_name);
if (write_listeners != null) {listeners_ref = write_listeners.getList();}
if (listeners_ref != null) {
for (int i=0;i<listeners_ref.size();i++) {
try {((DownloadManagerStateAttributeListener)listeners_ref.get(i)).attributeEventOccurred(download_manager, attribute_name, DownloadManagerStateAttributeListener.WRITTEN);}
catch (Throwable t) {Debug.printStackTrace(t);}
}
}
}
protected void
informWillRead(
final String attribute_name )
{
// avoid potential recursion will a will-be-read causing a write that then
// causes a further will-be-read...
List will_be_read_list = (List)tls_wbr.get();
if ( !will_be_read_list.contains( attribute_name )){
will_be_read_list.add( attribute_name );
try{
List listeners_ref = listeners_cow.getList();
for (int i=0;i<listeners_ref.size();i++){
try{
((DownloadManagerStateListener)listeners_ref.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);
}
}
listeners_ref = null;
CopyOnWriteList read_listeners = null;
read_listeners = (CopyOnWriteList)listeners_read_map_cow.get(attribute_name);
if (read_listeners != null) {listeners_ref = read_listeners.getList();}
if (listeners_ref != null) {
for (int i=0;i<listeners_ref.size();i++) {
try {((DownloadManagerStateAttributeListener)listeners_ref.get(i)).attributeEventOccurred(download_manager, attribute_name, DownloadManagerStateAttributeListener.WILL_BE_READ);}
catch (Throwable t) {Debug.printStackTrace(t);}
}
}
}finally{
will_be_read_list.remove( attribute_name );
}
}
}
public void
addListener(
DownloadManagerStateListener l )
{
listeners_cow.add( l );
}
public void
removeListener(
DownloadManagerStateListener l )
{
listeners_cow.remove(l);
}
public void addListener(DownloadManagerStateAttributeListener l, String attribute, int event_type) {
CopyOnWriteMap map_to_use = (event_type == DownloadManagerStateAttributeListener.WILL_BE_READ) ? this.listeners_read_map_cow : this.listeners_write_map_cow;
CopyOnWriteList lst = (CopyOnWriteList)map_to_use.get(attribute);
if (lst == null) {
lst = new CopyOnWriteList();
map_to_use.put(attribute, lst);
}
lst.add(l);
}
public void removeListener(DownloadManagerStateAttributeListener l, String attribute, int event_type) {
CopyOnWriteMap map_to_use = (event_type == DownloadManagerStateAttributeListener.WILL_BE_READ) ? this.listeners_read_map_cow : this.listeners_write_map_cow;
CopyOnWriteList lst = (CopyOnWriteList)map_to_use.get(attribute);
if (lst != null) {lst.remove(l);}
}
public void
generateEvidence(
IndentWriter writer)
{
writer.println( "DownloadManagerState" );
try{
writer.indent();
writer.println( "parameters=" + parameters );
writer.println("primary file=" + Debug.secretFileName(getPrimaryFile()));
}finally{
writer.exdent();
}
}
protected static class
nullState
implements DownloadManagerState
{
protected DownloadManager download_manager;
protected
nullState(
DownloadManager _dm )
{
download_manager = _dm;
}
public TOTorrent
getTorrent()
{
return( null );
}
public File
getStateFile( )
{
return( null );
}
public DownloadManager
getDownloadManager()
{
return( download_manager );
}
public void
clearResumeData()
{
}
public Map
getResumeData()
{
return( new HashMap());
}
public void
setResumeData(
Map data )
{
}
public boolean
isResumeDataComplete()
{
return( false );
}
public void
clearTrackerResponseCache()
{
}
public Map
getTrackerResponseCache()
{
return( new HashMap());
}
public void
setTrackerResponseCache(
Map value )
{
}
public void
setFlag(
long flag,
boolean set )
{
}
public boolean
getFlag(
long flag )
{
return( false );
}
public long
getFlags()
{
return 0;
}
public void
setParameterDefault(
String name )
{
}
public long
getLongParameter(
String name )
{
return( 0 );
}
public void
setLongParameter(
String name,
long value )
{
}
public int
getIntParameter(
String name )
{
return( 0 );
}
public void
setIntParameter(
String name,
int value )
{
}
public boolean
getBooleanParameter(
String name )
{
return( false );
}
public void
setBooleanParameter(
String name,
boolean value )
{
}
public void
setAttribute(
String name,
String value )
{
}
public String
getAttribute(
String name )
{
return( null );
}
public String
getTrackerClientExtensions()
{
return( null );
}
public void
setTrackerClientExtensions(
String value )
{
}
public void
setListAttribute(
String name,
String[] values )
{
}
public String getListAttribute(String name, int idx) {
return null;
}
public String[]
getListAttribute(
String name )
{
return( null );
}
public void
setMapAttribute(
String name,
Map value )
{
}
public Map
getMapAttribute(
String name )
{
return( null );
}
public boolean hasAttribute(String name) {return false;}
public int getIntAttribute(String name) {return 0;}
public long getLongAttribute(String name) {return 0L;}
public boolean getBooleanAttribute(String name) {return false;}
public void setIntAttribute(String name, int value) {}
public void setLongAttribute(String name, long value) {}
public void setBooleanAttribute(String name, boolean value) {}
public Category
getCategory()
{
return( null );
}
public void
setCategory(
Category cat )
{
}
public String[]
getNetworks()
{
return( new String[0] );
}
public boolean isNetworkEnabled(String network) {
return false;
}
public void
setNetworks(
String[] networks )
{
}
public void setNetworkEnabled(
String network,
boolean enabled) {
}
public String[]
getPeerSources()
{
return( new String[0] );
}
public boolean
isPeerSourcePermitted(
String peerSource )
{
return( false );
}
public void setPeerSourcePermitted(String peerSource, boolean permitted) {
}
public boolean
isPeerSourceEnabled(
String peerSource) {
return false;
}
public void suppressStateSave(boolean suppress) {}
public void
setPeerSources(
String[] networks )
{
}
public void
setPeerSourceEnabled(
String source,
boolean enabled) {
}
public void
setFileLink(
File link_source,
File link_destination )
{
}
public void
clearFileLinks()
{
}
public File
getFileLink(
File link_source )
{
return( null );
}
public CaseSensitiveFileMap
getFileLinks()
{
return( new CaseSensitiveFileMap());
}
public void
setActive(boolean active )
{
}
public void discardFluff() {}
public void
save()
{
}
public void
delete()
{
}
public void
addListener(
DownloadManagerStateListener l )
{}
public void
removeListener(
DownloadManagerStateListener l )
{}
public void addListener(DownloadManagerStateAttributeListener l, String attribute, int event_type) {}
public void removeListener(DownloadManagerStateAttributeListener l, String attribute, int event_type) {}
public void setDisplayName(String name) {}
public String getDisplayName() {return null;}
public void setUserComment(String name) {}
public String getUserComment() {return null;}
public void setRelativeSavePath(String name) {}
public String getRelativeSavePath() {return null;}
public boolean parameterExists(String name) {
// TODO Auto-generated method stub
return false;
}
public void
generateEvidence(
IndentWriter writer)
{
writer.println( "DownloadManagerState: broken torrent" );
}
public boolean isOurContent() {
// TODO Auto-generated method stub
return false;
}
// @see org.gudy.azureus2.core3.download.DownloadManagerState#getPrimaryFile()
public String getPrimaryFile() {
// TODO Auto-generated method stub
return null;
}
// @see org.gudy.azureus2.core3.download.DownloadManagerState#setPrimaryFile(java.lang.String)
public void setPrimaryFile(String relativeFile) {
// TODO Auto-generated method stub
}
}
protected static class
CachedStateWrapper
extends LogRelation
implements TorrentUtils.ExtendedTorrent
{
private DownloadManagerImpl download_manager;
private String torrent_file;
private HashWrapper torrent_hash_wrapper;
private Map cache;
private Map cache_attributes;
private Map cache_azp;
private volatile TorrentUtils.ExtendedTorrent delegate;
private TOTorrentException fixup_failure;
private boolean discard_pieces;
private boolean logged_failure;
private Boolean simple_torrent;
private long size;
private URL announce_url;
private cacheGroup announce_group;
private volatile boolean discard_fluff;
protected
CachedStateWrapper(
DownloadManagerImpl _download_manager,
String _torrent_file,
byte[] _torrent_hash,
Map _cache,
boolean _force_piece_discard )
{
download_manager = _download_manager;
torrent_file = _torrent_file;
torrent_hash_wrapper = new HashWrapper( _torrent_hash );
cache = _cache;
cache_attributes = (Map)cache.get( "attributes" );
cache_azp = (Map)cache.get( "azp" );
if ( _force_piece_discard ){
discard_pieces = true;
}else{
Long l_fp = (Long)cache.get( "dp" );
if ( l_fp != null ){
discard_pieces = l_fp.longValue() == 1;
}
}
Long st = (Long)cache.get( "simple" );
if ( st != null ){
simple_torrent = new Boolean( st.longValue()==1 );
}
Long l_size = (Long)cache.get( "size" );
if ( l_size != null ){
size = l_size.longValue();
}
byte[] au = (byte[])cache.get( "au" );
if ( au != null ){
try{
announce_url = StringInterner.internURL(new URL((new String( au, "UTF-8" ))));
}catch( Throwable e ){
}
}
List ag = (List)cache.get( "ag" );
if ( ag != null ){
try{
announce_group = importGroup( ag );
}catch( Throwable e ){
}
}
}
protected static Map
export(
DownloadManagerState dms )
throws TOTorrentException
{
Map cache = new HashMap();
TOTorrent state = dms.getTorrent();
cache.put( "hash", state.getHash());
cache.put( "name", state.getName());
cache.put( "utf8name", state.getUTF8Name() == null ? "" : state.getUTF8Name());
cache.put( "comment", state.getComment());
cache.put( "createdby", state.getCreatedBy());
cache.put( "size", new Long( state.getSize()));
cache.put( "encoding", state.getAdditionalStringProperty( "encoding" ));
cache.put( "torrent filename", state.getAdditionalStringProperty( "torrent filename" ));
cache.put( "attributes", state.getAdditionalMapProperty( ATTRIBUTE_KEY ));
cache.put( "azp", state.getAdditionalMapProperty( AZUREUS_PROPERTIES_KEY ));
try{
cache.put( "au", state.getAnnounceURL().toExternalForm());
cache.put( "ag", exportGroup(state.getAnnounceURLGroup()));
}catch( Throwable e ){
}
boolean discard_pieces = dms.isResumeDataComplete();
TOTorrent t = dms.getTorrent();
if ( t instanceof CachedStateWrapper ){
CachedStateWrapper csw = (CachedStateWrapper)t;
if ( !discard_pieces ){
// discard pieces if they are currently discarded
discard_pieces = csw.peekPieces() == null;
}
Boolean simple_torrent = csw.simple_torrent;
if ( simple_torrent != null ){
cache.put( "simple", new Long(simple_torrent.booleanValue()?1:0 ));
}
}else{
// Debug.out( "Hmm, torrent isn't cache-state-wrapper, it is " + t );
}
cache.put( "dp", new Long( discard_pieces?1:0 ));
return( cache );
}
protected static List
exportGroup(
TOTorrentAnnounceURLGroup group )
{
TOTorrentAnnounceURLSet[] sets = group.getAnnounceURLSets();
List result = new ArrayList();
for (int i=0;i<sets.length;i++){
TOTorrentAnnounceURLSet set = sets[i];
URL[] urls = set.getAnnounceURLs();
if ( urls.length > 0 ){
List s = new ArrayList( urls.length );
for (int j=0;j<urls.length;j++){
s.add( urls[j].toExternalForm());
}
result.add( s );
}
}
return( result );
}
protected cacheGroup
importGroup(
List l )
throws Exception
{
return( new cacheGroup( l ));
}
protected class
cacheGroup
implements TOTorrentAnnounceURLGroup
{
private TOTorrentAnnounceURLSet[] sets;
protected
cacheGroup(
List group )
throws Exception
{
sets = new TOTorrentAnnounceURLSet[ group.size() ];
for (int i = 0; i < sets.length; i++){
List set = (List) group.get(i);
URL[] urls = new URL[set.size()];
for (int j = 0; j < urls.length; j++){
urls[j] = StringInterner.internURL(new URL(new String((byte[]) set.get(j), "UTF-8")));
}
sets[i] = new cacheSet(urls);
}
}
public TOTorrentAnnounceURLSet[]
getAnnounceURLSets()
{
if ( announce_group == null && fixup()){
return delegate.getAnnounceURLGroup().getAnnounceURLSets();
}
return( sets );
}
void fixGroup()
{
TOTorrentAnnounceURLSet[] realSets = delegate.getAnnounceURLGroup().getAnnounceURLSets();
if ( realSets.length != sets.length ){
Debug.out("Cached announce group state does not match real state");
}else{
for (int i=0;i<realSets.length;i++){
if ( sets[i] instanceof cacheSet ){
((cacheSet)sets[i]).delegateSet = realSets[i];
}
}
sets = null;
}
}
public void
setAnnounceURLSets(
TOTorrentAnnounceURLSet[] toSet )
{
if ( fixup()){
TOTorrentAnnounceURLSet[] modToSet = new TOTorrentAnnounceURLSet[toSet.length];
for (int i = 0; i < toSet.length; i++){
TOTorrentAnnounceURLSet set = toSet[i];
if ( set instanceof cacheSet ){
modToSet[i] = ((cacheSet) set).delegateSet;
}
if ( modToSet[i] == null ){
modToSet[i] = set;
}
}
delegate.getAnnounceURLGroup().setAnnounceURLSets( modToSet );
}
}
public TOTorrentAnnounceURLSet
createAnnounceURLSet(
URL[] urls )
{
if ( fixup()){
return( delegate.getAnnounceURLGroup().createAnnounceURLSet( urls ));
}
return( null );
}
protected class
cacheSet
implements TOTorrentAnnounceURLSet
{
private URL[] urls;
private TOTorrentAnnounceURLSet delegateSet;
public cacheSet(URL[] urls)
{
this.urls = urls;
}
public URL[]
getAnnounceURLs()
{
if ( announce_group == null && fixup() && delegateSet != null ){
return delegateSet.getAnnounceURLs();
}
return( urls );
}
public void
setAnnounceURLs(
URL[] toSet )
{
if ( fixup() && delegateSet != null ){
delegateSet.setAnnounceURLs( toSet );
}else{
urls = toSet;
}
}
}
}
protected void
clearCache()
{
cache = null;
}
protected boolean
fixup()
{
try{
if ( delegate == null ){
synchronized( this ){
if ( delegate == null ){
// System.out.println( "Fixing up " + this );
if ( fixup_failure != null ){
throw( fixup_failure );
}
delegate = loadRealState();
if ( discard_fluff ){
delegate.setDiscardFluff( discard_fluff );
}
if ( cache != null ){
Debug.out( "Cache miss forced fixup" );
}
cache = null;
// join cache view back up with real state to save memory as the one
// we've just read is irrelevant due to the cache values being
// used
if ( cache_attributes != null ){
delegate.setAdditionalMapProperty( ATTRIBUTE_KEY, cache_attributes );
cache_attributes = null;
}
if ( cache_azp != null ){
delegate.setAdditionalMapProperty( AZUREUS_PROPERTIES_KEY, cache_azp );
cache_azp = null;
}
announce_url = null;
if ( announce_group != null ){
announce_group.fixGroup();
announce_group = null;
}
}
}
}
return( true );
}catch( TOTorrentException e ){
fixup_failure = e;
if ( download_manager != null ){
download_manager.setTorrentInvalid( Debug.getNestedExceptionMessage( e ));
}else{
if ( !logged_failure ){
logged_failure = true;
Debug.out( "Torrent can't be loaded: " + Debug.getNestedExceptionMessage( e ));
}
}
}
return( false );
}
protected TorrentUtils.ExtendedTorrent
loadRealState()
throws TOTorrentException
{
// System.out.println("loadReal: " + torrent_file + " dp=" + discard_pieces + ": " + Debug.getCompressedStackTrace().substring(114));
if ( !SUPPRESS_FIXUP_ERRORS && Constants.isCVSVersion() ){
if ( Thread.currentThread().isDaemon()){
// Debug.outNoStack( "Fixup on thread " + Thread.currentThread().getName() + ": " + Debug.getCompressedStackTrace());
}else{
Debug.out( "Premature fixup?" );
}
}
File saved_file = getStateFile( torrent_hash_wrapper.getBytes() );
if ( saved_file.exists()){
try{
return( TorrentUtils.readDelegateFromFile( saved_file, discard_pieces ));
}catch( Throwable e ){
Debug.out( "Failed to load download state for " + saved_file );
}
}
// try reading from original
TOTorrent original_torrent = TorrentUtils.readFromFile( new File(torrent_file), true );
torrent_hash_wrapper = original_torrent.getHashWrapper();
saved_file = getStateFile( torrent_hash_wrapper.getBytes());
if ( saved_file.exists()){
try{
return( TorrentUtils.readDelegateFromFile( saved_file, discard_pieces ));
}catch( Throwable e ){
Debug.out( "Failed to load download state for " + saved_file );
}
}
// 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 );
return( TorrentUtils.readDelegateFromFile( saved_file, discard_pieces ));
}
public byte[]
getName()
{
Map c = cache;
if ( c != null ){
byte[] name = (byte[])c.get( "name" );
if (name != null) {
return name;
}
}
if ( fixup()){
return( delegate.getName());
}
// Does grabbing the nested exception message always give us something useful?
// My experience is that we just get an empty string here...
return(("Error - " + Debug.getNestedExceptionMessage( fixup_failure )).getBytes());
}
public String getUTF8Name() {
Map c = cache;
if ( c != null ){
byte[] name = (byte[])c.get( "utf8name" );
if (name != null) {
String utf8name;
try {
utf8name = new String(name, "utf8");
} catch (UnsupportedEncodingException e) {
return null;
}
if (utf8name.length() == 0) {
return null;
}
return utf8name;
}
}
if (fixup()) {
return delegate.getUTF8Name();
}
return null;
}
public boolean
isSimpleTorrent()
{
if ( simple_torrent != null ){
return( simple_torrent.booleanValue());
}
if ( fixup()){
boolean st = delegate.isSimpleTorrent();
simple_torrent = new Boolean( st );
return( st );
}
return( false );
}
public byte[]
getComment()
{
Map c = cache;
if ( c != null ){
return((byte[])c.get( "comment" ));
}
if ( fixup()){
return( delegate.getComment());
}
return( null );
}
public void
setComment(
String comment )
{
if ( fixup()){
delegate.setComment( comment );
}
}
public long
getCreationDate()
{
if ( fixup()){
return( delegate.getCreationDate());
}
return( 0 );
}
public void
setCreationDate(
long date )
{
if ( fixup()){
delegate.setCreationDate( date );
}
}
public byte[]
getCreatedBy()
{
Map c = cache;
if ( c != null ){
return((byte[])c.get( "createdby" ));
}
if ( fixup()){
return( delegate.getCreatedBy());
}
return( null );
}
public void
setCreatedBy(
byte[] cb )
{
if ( fixup()){
delegate.setCreatedBy( cb );
}
}
public boolean
isCreated()
{
if ( fixup()){
return( delegate.isCreated());
}
return( false );
}
public URL
getAnnounceURL()
{
if ( announce_url != null ){
return( announce_url );
}
if ( fixup()){
return( delegate.getAnnounceURL());
}
return( null );
}
public boolean
setAnnounceURL(
URL url )
{
if ( fixup()){
return( delegate.setAnnounceURL( url ));
} else
announce_url = url;
return( false );
}
public TOTorrentAnnounceURLGroup
getAnnounceURLGroup()
{
if ( announce_group != null ){
return( announce_group );
}
if ( fixup()){
return( delegate.getAnnounceURLGroup());
}
return( null );
}
public byte[][]
getPieces()
throws TOTorrentException
{
if ( fixup()){
return( delegate.getPieces());
}
throw( fixup_failure );
}
public void
setPieces(
byte[][] pieces )
throws TOTorrentException
{
if ( fixup()){
delegate.setPieces( pieces );
return;
}
throw( fixup_failure );
}
public byte[][]
peekPieces()
throws TOTorrentException
{
if ( fixup()){
return( delegate.peekPieces());
}
throw( fixup_failure );
}
public void
setDiscardFluff(
boolean discard )
{
discard_fluff = discard;
if ( delegate != null ){
delegate.setDiscardFluff( discard_fluff );
}
}
public long
getPieceLength()
{
if ( fixup()){
return( delegate.getPieceLength());
}
return( 0 );
}
public int
getNumberOfPieces()
{
if ( fixup()){
return( delegate.getNumberOfPieces());
}
return( 0 );
}
public long
getSize()
{
if ( size > 0 ){
return( size );
}
if ( fixup()){
size = delegate.getSize();
return( size );
}
return( 0 );
}
public TOTorrentFile[]
getFiles()
{
if ( fixup()){
return( delegate.getFiles());
}
return( new TOTorrentFile[0] );
}
public byte[]
getHash()
throws TOTorrentException
{
// optimise this
return( torrent_hash_wrapper.getBytes());
}
public HashWrapper
getHashWrapper()
throws TOTorrentException
{
return( torrent_hash_wrapper );
}
public void
setHashOverride(
byte[] hash )
throws TOTorrentException
{
throw( new TOTorrentException( "Not supported", TOTorrentException.RT_HASH_FAILS ));
}
public boolean
hasSameHashAs(
TOTorrent other )
{
try{
byte[] other_hash = other.getHash();
return( Arrays.equals( getHash(), other_hash ));
}catch( TOTorrentException e ){
Debug.printStackTrace( e );
return( false );
}
}
public boolean
getPrivate()
{
if ( fixup()){
return( delegate.getPrivate());
}
return( false );
}
public void
setPrivate(
boolean _private )
throws TOTorrentException
{
if ( fixup()){
delegate.setPrivate( _private );
}
}
public void
setAdditionalStringProperty(
String name,
String value )
{
if ( fixup()){
delegate.setAdditionalStringProperty( name, value );
}
}
public String
getAdditionalStringProperty(
String name )
{
Map c = cache;
if ( c != null && ( name.equals( "encoding") || name.equals( "torrent filename" ))){
byte[] res = (byte[])c.get( name );
if ( res == null ){
return( null );
}
try{
return( new String( res, "UTF8" ));
}catch( Throwable e ){
Debug.printStackTrace( e );
return( null );
}
}
if ( fixup()){
return( delegate.getAdditionalStringProperty( name ));
}
return( null );
}
public void
setAdditionalByteArrayProperty(
String name,
byte[] value )
{
if ( fixup()){
delegate.setAdditionalByteArrayProperty( name, value );
}
}
public byte[]
getAdditionalByteArrayProperty(
String name )
{
if ( fixup()){
return( delegate.getAdditionalByteArrayProperty( name ));
}
return( null );
}
public void
setAdditionalLongProperty(
String name,
Long value )
{
if ( fixup()){
delegate.setAdditionalLongProperty( name, value );
}
}
public Long
getAdditionalLongProperty(
String name )
{
if ( fixup()){
return( delegate.getAdditionalLongProperty( name ));
}
return( null );
}
public void
setAdditionalListProperty(
String name,
List value )
{
if ( fixup()){
delegate.setAdditionalListProperty( name, value );
}
}
public List
getAdditionalListProperty(
String name )
{
if ( fixup()){
return( delegate.getAdditionalListProperty( name ));
}
return( null );
}
public void
setAdditionalMapProperty(
String name,
Map value )
{
if ( fixup()){
delegate.setAdditionalMapProperty( name, value );
}
}
public Map
getAdditionalMapProperty(
String name )
{
Map c = cache_attributes;
if ( c != null && name.equals( "attributes" )){
return( c );
}
c = cache_azp;
if ( c != null && name.equals( "azureus_properties" )){
return( c );
}
if ( fixup()){
return( delegate.getAdditionalMapProperty( name ));
}
return( null );
}
public Object
getAdditionalProperty(
String name )
{
if ( fixup()){
return( delegate.getAdditionalProperty( name ));
}
return( null );
}
public void
setAdditionalProperty(
String name,
Object value )
{
if ( fixup()){
delegate.setAdditionalProperty( name, value );
}
}
public void
removeAdditionalProperty(
String name )
{
if ( fixup()){
delegate.removeAdditionalProperty( name );
}
}
public void
removeAdditionalProperties()
{
if ( fixup()){
delegate.removeAdditionalProperties();
}
}
public void
serialiseToBEncodedFile(
File file )
throws TOTorrentException
{
if ( fixup()){
delegate.serialiseToBEncodedFile( file );
return;
}
throw( fixup_failure );
}
public Map
serialiseToMap()
throws TOTorrentException
{
if ( fixup()){
return( delegate.serialiseToMap());
}
throw( fixup_failure );
}
public void
serialiseToXMLFile(
File file )
throws TOTorrentException
{
if ( fixup()){
delegate.serialiseToXMLFile( file );
return;
}
throw( fixup_failure );
}
public void
addListener(
TOTorrentListener l )
{
if ( fixup()){
delegate.addListener( l );
}
}
public void
removeListener(
TOTorrentListener l )
{
if ( fixup()){
delegate.removeListener( l );
}
}
public AEMonitor
getMonitor()
{
if ( fixup()){
return( delegate.getMonitor());
}
return( null );
}
public void
print()
{
if ( fixup()){
delegate.print();
}
}
/* (non-Javadoc)
* @see org.gudy.azureus2.core3.logging.LogRelation#getLogRelationText()
*/
public String getRelationText() {
return "Torrent: '" + new String(getName()) + "'";
}
/* (non-Javadoc)
* @see org.gudy.azureus2.core3.logging.LogRelation#queryForClass(java.lang.Class)
*/
public Object[] getQueryableInterfaces() {
// yuck
try {
return new Object[] { AzureusCoreFactory.getSingleton()
.getGlobalManager().getDownloadManager(this) };
} catch (Exception e) {
}
return null;
}
}
}