📄 downloadmanagerstateimpl.java
字号:
/*
* 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.util.*;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.io.*;
import java.net.URL;
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.*;
import org.gudy.azureus2.core3.peer.PEPeerSource;
import org.gudy.azureus2.core3.category.*;
import org.gudy.azureus2.core3.config.COConfigurationManager;
import org.gudy.azureus2.core3.config.ParameterListener;
import org.gudy.azureus2.core3.torrent.TOTorrent;
import org.gudy.azureus2.core3.torrent.TOTorrentAnnounceURLGroup;
import org.gudy.azureus2.core3.torrent.TOTorrentException;
import org.gudy.azureus2.core3.torrent.TOTorrentFile;
import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncer;
import org.gudy.azureus2.core3.util.AEMonitor;
import org.gudy.azureus2.core3.util.AENetworkClassifier;
import org.gudy.azureus2.core3.util.BDecoder;
import org.gudy.azureus2.core3.util.BEncoder;
import org.gudy.azureus2.core3.util.ByteFormatter;
import org.gudy.azureus2.core3.util.Constants;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.FileUtil;
import org.gudy.azureus2.core3.util.HashWrapper;
import org.gudy.azureus2.core3.util.IndentWriter;
import org.gudy.azureus2.core3.util.TorrentUtils;
import com.aelitis.azureus.core.AzureusCoreFactory;
import com.aelitis.azureus.core.util.CaseSensitiveFileMap;
/**
* @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 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 File ACTIVE_DIR;
static{
ACTIVE_DIR = FileUtil.getUserFile( "active" );
if ( !ACTIVE_DIR.exists()){
FileUtil.mkdirs(ACTIVE_DIR);
}
}
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] );
}
}
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 List global_state_cache_wrappers = new ArrayList();
private DownloadManagerImpl download_manager;
private TOTorrent torrent;
private boolean write_required;
private Map tracker_response_cache;
private Category category;
private List listeners = new ArrayList();
private List will_be_read_list = new ArrayList();
private Map parameters;
private Map attributes;
private AEMonitor this_mon = new AEMonitor( "DownloadManagerState" );
private boolean firstPrimaryFileRead = true;
private static DownloadManagerState
getDownloadState(
DownloadManagerImpl download_manager,
TOTorrent original_torrent,
TOTorrent 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));
TOTorrent saved_state = null;
File saved_file = getStateFile( torrent_hash );
if ( saved_file.exists()){
try{
saved_state = TorrentUtils.readFromFile( saved_file, true );
}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.readFromFile( saved_file, true );
}
return( getDownloadState( null, original_torrent, saved_state ));
}
protected static DownloadManagerState
getDownloadState(
DownloadManagerImpl download_manager,
String torrent_file,
byte[] torrent_hash,
boolean force_piece_discard )
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;
TOTorrent 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, force_piece_discard );
global_state_cache_wrappers.add( wrapper );
saved_state = wrapper;
}else{
saved_state = TorrentUtils.readFromFile( saved_file, true, 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.readFromFile( new File(torrent_file), true, discard_pieces );
torrent_hash = original_torrent.getHash();
File saved_file = getStateFile( torrent_hash );
if ( saved_file.exists()){
try{
saved_state = TorrentUtils.readFromFile( saved_file, true, 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.readFromFile( saved_file, true, discard_pieces );
}
}
return( getDownloadState( download_manager, original_torrent, saved_state ));
}
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();
}
protected
DownloadManagerStateImpl(
DownloadManagerImpl _download_manager,
TOTorrent _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();
}
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( "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( "enable.seedingonly.maxuploads", this );
}
public DownloadManager
getDownloadManager()
{
return( download_manager );
}
protected void
setDownloadManager(
DownloadManagerImpl dm )
{
download_manager = dm;
}
public File
getStateFile(
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 );
}
}
public void
clearTrackerResponseCache()
{
setTrackerResponseCache( new HashMap());
}
public Map
getTrackerResponseCache()
{
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 void
setTrackerResponseCache(
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 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -