/* * File : DownloadManagerImpl.java * Created : 06-Jan-2004 * By : parg * * Azureus - a Java Bittorrent client * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details ( see the LICENSE file ). * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.gudy.azureus2.pluginsimpl.local.download; /** * @author parg * */ import java.io.File; import java.io.IOException; import java.net.URL; import java.util.*; import org.gudy.azureus2.core3.config.COConfigurationManager; import org.gudy.azureus2.core3.disk.DiskManager; import org.gudy.azureus2.core3.download.DownloadManager; import org.gudy.azureus2.core3.download.DownloadManagerInitialisationAdapter; import org.gudy.azureus2.core3.download.DownloadManagerStateFactory; import org.gudy.azureus2.core3.download.impl.DownloadManagerDefaultPaths; import org.gudy.azureus2.core3.download.impl.DownloadManagerMoveHandler; import org.gudy.azureus2.core3.global.GlobalManager; import org.gudy.azureus2.core3.global.GlobalManagerDownloadRemovalVetoException; import org.gudy.azureus2.core3.global.GlobalManagerDownloadWillBeRemovedListener; import org.gudy.azureus2.core3.global.GlobalManagerListener; import org.gudy.azureus2.core3.torrent.TOTorrent; import org.gudy.azureus2.core3.torrent.TOTorrentException; import org.gudy.azureus2.core3.util.*; import org.gudy.azureus2.plugins.download.*; import org.gudy.azureus2.plugins.download.savelocation.DefaultSaveLocationManager; import org.gudy.azureus2.plugins.download.savelocation.SaveLocationManager; import org.gudy.azureus2.plugins.torrent.Torrent; import org.gudy.azureus2.plugins.torrent.TorrentException; import org.gudy.azureus2.plugins.ui.UIManagerEvent; import org.gudy.azureus2.pluginsimpl.local.PluginCoreUtils; import org.gudy.azureus2.pluginsimpl.local.torrent.TorrentImpl; import org.gudy.azureus2.pluginsimpl.local.ui.UIManagerImpl; import com.aelitis.azureus.core.AzureusCore; import com.aelitis.azureus.core.util.CopyOnWriteList; public class DownloadManagerImpl implements org.gudy.azureus2.plugins.download.DownloadManager, DownloadManagerInitialisationAdapter { protected static DownloadManagerImpl singleton; protected static AEMonitor class_mon = new AEMonitor( "DownloadManager:class"); public static DownloadManagerImpl getSingleton( AzureusCore azureus_core ) { try{ class_mon.enter(); if ( singleton == null ){ singleton = new DownloadManagerImpl( azureus_core ); } return( singleton ); }finally{ class_mon.exit(); } } //private AzureusCore azureus_core; private GlobalManager global_manager; private DownloadManagerStats stats; private DownloadEventNotifierImpl global_dl_notifier; private List<DownloadManagerListener> listeners = new ArrayList<DownloadManagerListener>(); private CopyOnWriteList<DownloadWillBeAddedListener> dwba_listeners = new CopyOnWriteList<DownloadWillBeAddedListener>(); private AEMonitor listeners_mon = new AEMonitor( "DownloadManager:L"); private List<Download> downloads = new ArrayList<Download>(); private Map<DownloadManager,DownloadImpl> pending_dls = new IdentityHashMap<DownloadManager,DownloadImpl>(); private Map<DownloadManager,DownloadImpl> download_map = new IdentityHashMap<DownloadManager,DownloadImpl>(); protected DownloadManagerImpl( AzureusCore _azureus_core ) { //azureus_core = _azureus_core; global_manager = _azureus_core.getGlobalManager(); stats = new DownloadManagerStatsImpl( global_manager ); global_dl_notifier = new DownloadEventNotifierImpl(this); readStubConfig(); global_manager.addListener( new GlobalManagerListener() { public void downloadManagerAdded( DownloadManager dm ) { addDownloadManager( dm ); } public void downloadManagerRemoved( DownloadManager dm ) { List<DownloadManagerListener> listeners_ref = null; DownloadImpl dl = null; try{ listeners_mon.enter(); dl = download_map.get( dm ); if ( dl == null ){ System.out.println( "DownloadManager:unknown manager removed"); }else{ downloads.remove( dl ); download_map.remove( dm ); pending_dls.remove( dm ); dl.destroy(); listeners_ref = listeners; } }finally{ listeners_mon.exit(); } if ( dl != null ){ for (int i=0;i<listeners_ref.size();i++){ try{ listeners_ref.get(i).downloadRemoved( dl ); }catch( Throwable e ){ Debug.out( e ); } } } } public void destroyInitiated() { } public void destroyed() { synchronized( download_stubs ){ if ( dirty_stubs ){ writeStubConfig(); } } } public void seedingStatusChanged( boolean seeding_only_mode, boolean b ){ //TODO } }); global_manager.addDownloadWillBeRemovedListener( new GlobalManagerDownloadWillBeRemovedListener() { public void downloadWillBeRemoved( DownloadManager dm, boolean remove_torrent, boolean remove_data ) throws GlobalManagerDownloadRemovalVetoException { DownloadImpl download = (DownloadImpl)download_map.get( dm ); if ( download != null ){ try{ download.isRemovable(); }catch( DownloadRemovalVetoException e ){ throw( new GlobalManagerDownloadRemovalVetoException( e.getMessage(),e.isSilent())); } } } }); } public void addDownload( final File fileName ) { UIManagerImpl.fireEvent( null, UIManagerEvent.ET_OPEN_TORRENT_VIA_FILE, fileName ); } public void addDownload( final URL url) { addDownload(url,null,true,null); } public void addDownload( URL url, boolean auto_download ) throws DownloadException { addDownload(url,null,auto_download,null); } public void addDownload( final URL url, final URL referrer) { addDownload(url,referrer,true,null); } public void addDownload( URL url, Map request_properties ) { addDownload(url,null,true,request_properties); } public void addDownload( final URL url, final URL referrer, boolean auto_download, Map request_properties ) { UIManagerImpl.fireEvent( null, UIManagerEvent.ET_OPEN_TORRENT_VIA_URL, new Object[]{ url, referrer, new Boolean( auto_download ), request_properties }); } protected void addDownloadManager( DownloadManager dm ) { List<DownloadManagerListener> listeners_ref = null; DownloadImpl dl = null; try{ listeners_mon.enter(); if ( download_map.get(dm) == null ){ dl = pending_dls.remove( dm ); if ( dl == null ){ dl = new DownloadImpl( this, dm); } downloads.add( dl ); download_map.put( dm, dl ); listeners_ref = listeners; } }finally{ listeners_mon.exit(); } if ( dl != null ){ for (int i=0;i<listeners_ref.size();i++){ try{ listeners_ref.get(i).downloadAdded( dl ); }catch( Throwable e ){ Debug.printStackTrace( e ); } } } } public Download addDownload( Torrent torrent ) throws DownloadException { return( addDownload( torrent, null, null )); } public Download addDownload( Torrent torrent, File torrent_file, File data_location ) throws DownloadException { return( addDownload( torrent, torrent_file, data_location, getInitialState())); } public Download addDownload( Torrent torrent, File torrent_file, File data_location, int initial_state ) throws DownloadException { if ( torrent_file == null ){ String torrent_dir = null; if( COConfigurationManager.getBooleanParameter("Save Torrent Files")){ try{ torrent_dir = COConfigurationManager.getDirectoryParameter("General_sDefaultTorrent_Directory"); }catch(Exception egnore){} } if ( torrent_dir == null || torrent_dir.length() == 0 ){ throw( new DownloadException("DownloadManager::addDownload: default torrent save directory must be configured" )); } torrent_file = new File( torrent_dir + File.separator + torrent.getName() + ".torrent" ); try{ torrent.writeToFile( torrent_file ); }catch( TorrentException e ){ throw( new DownloadException("DownloadManager::addDownload: failed to write torrent to '" + torrent_file.toString() + "'", e )); } } else { if (!torrent_file.exists()) { throw new DownloadException("DownloadManager::addDownload: torrent file does not exist - " + torrent_file.toString()); } else if (!torrent_file.isFile()) { throw new DownloadException("DownloadManager::addDownload: torrent filepath given is not a file - " + torrent_file.toString()); } } if ( data_location == null ){ String data_dir = COConfigurationManager.getStringParameter("Default save path"); if ( data_dir == null || data_dir.length() == 0 ){ throw( new DownloadException("DownloadManager::addDownload: default data save directory must be configured" )); } data_location = new File(data_dir); FileUtil.mkdirs(data_location); } byte[] hash = null; try { hash = torrent.getHash(); } catch (Exception e) { } boolean for_seeding = torrent.isComplete(); DownloadManager dm = global_manager.addDownloadManager( torrent_file.toString(), hash, data_location.toString(), initial_state, true, for_seeding, null ); if ( dm == null ){ throw( new DownloadException( "DownloadManager::addDownload - failed, download may already in the process of being added")); } addDownloadManager( dm ); return( getDownload( dm )); } public Download addDownloadStopped( Torrent torrent, File torrent_location, File data_location ) throws DownloadException { return( addDownload( torrent, torrent_location, data_location, DownloadManager.STATE_STOPPED )); } public Download addNonPersistentDownload( Torrent torrent, File torrent_file, File data_location ) throws DownloadException { byte[] hash = null; try { hash = torrent.getHash(); } catch (Exception e) { } DownloadManager dm = global_manager.addDownloadManager( torrent_file.toString(), hash, data_location.toString(), getInitialState(), false); if ( dm == null ){ throw( new DownloadException( "DownloadManager::addDownload - failed")); } addDownloadManager( dm ); return( getDownload( dm )); } public Download addNonPersistentDownloadStopped( Torrent torrent, File torrent_file, File data_location ) throws DownloadException { byte[] hash = null; try { hash = torrent.getHash(); } catch (Exception e) { } DownloadManager dm = global_manager.addDownloadManager( torrent_file.toString(), hash, data_location.toString(), DownloadManager.STATE_STOPPED, false); if ( dm == null ){ throw( new DownloadException( "DownloadManager::addDownload - failed")); } addDownloadManager( dm ); return( getDownload( dm )); } public void clearNonPersistentDownloadState( byte[] hash) { global_manager.clearNonPersistentDownloadState( hash ); } protected int getInitialState() { boolean default_start_stopped = COConfigurationManager.getBooleanParameter( "Default Start Torrents Stopped" ); return( default_start_stopped?DownloadManager.STATE_STOPPED:DownloadManager.STATE_WAITING); } protected DownloadImpl getDownload( DownloadManager dm ) throws DownloadException { DownloadImpl dl = download_map.get(dm); if ( dl == null ){ throw( new DownloadException("DownloadManager::getDownload: download not found")); } return( dl ); } public static DownloadImpl[] getDownloadStatic(DownloadManager[] dm) { ArrayList res = new ArrayList(dm.length); for (int i=0; i<dm.length; i++) { try {res.add(getDownloadStatic(dm[i]));} catch (DownloadException de) {} } return (DownloadImpl[])res.toArray(new DownloadImpl[res.size()]); } /** * Retrieve the plugin Downlaod object related to the DownloadManager * * @param dm DownloadManager to find * @return plugin object * @throws DownloadException */ public static DownloadImpl getDownloadStatic( DownloadManager dm ) throws DownloadException { if ( singleton != null ){ return( singleton.getDownload( dm )); } throw( new DownloadException( "DownloadManager not initialised")); } public static Download getDownloadStatic( DiskManager dm ) throws DownloadException { if ( singleton != null ){ return( singleton.getDownload( dm )); } throw( new DownloadException( "DownloadManager not initialised")); } public Download getDownload( DiskManager dm ) throws DownloadException { List<DownloadManager> dls = global_manager.getDownloadManagers(); for (int i=0;i<dls.size();i++){ DownloadManager man = dls.get(i); if ( man.getDiskManager() == dm ){ return( getDownload( man.getTorrent())); } } return( null ); } protected Download getDownload( TOTorrent torrent ) throws DownloadException { if ( torrent != null ){ for (int i=0;i<downloads.size();i++){ Download dl = (Download)downloads.get(i); TorrentImpl t = (TorrentImpl)dl.getTorrent(); // can be null if broken torrent if ( t == null ){ continue; } if ( t.getTorrent().hasSameHashAs( torrent )){ return( dl ); } } } throw( new DownloadException("DownloadManager::getDownload: download not found")); } public static Download getDownloadStatic( TOTorrent torrent ) throws DownloadException { if ( singleton != null ){ return( singleton.getDownload( torrent )); } throw( new DownloadException( "DownloadManager not initialised")); } public Download getDownload( Torrent _torrent ) { TorrentImpl torrent = (TorrentImpl)_torrent; try{ return( getDownload( torrent.getTorrent())); }catch( DownloadException e ){ } return( null ); } public Download getDownload( byte[] hash ) { DownloadManager manager = global_manager.getDownloadManager(new HashWrapper(hash)); if (manager != null) { try { return getDownload(manager); } catch (DownloadException e) { } } List dls = global_manager.getDownloadManagers(); for (int i=0;i<dls.size();i++){ DownloadManager man = (DownloadManager)dls.get(i); // torrent can be null if download manager torrent file read fails TOTorrent torrent = man.getTorrent(); if ( torrent != null ){ try{ if ( Arrays.equals( torrent.getHash(), hash )){ return( getDownload( torrent )); } }catch( DownloadException e ){ // not found }catch( TOTorrentException e ){ Debug.printStackTrace( e ); } } } return( null ); } public Download[] getDownloads() { // we have to use the global manager's ordering as it // hold this List<DownloadManager> dms = global_manager.getDownloadManagers(); Set<Download> res_l; try{ listeners_mon.enter(); res_l = new LinkedHashSet<Download>( downloads.size()); for (int i=0;i<dms.size();i++){ DownloadImpl dl = download_map.get( dms.get(i)); if ( dl != null ){ res_l.add( dl ); } } if ( res_l.size() < downloads.size()){ // now add in any external downloads for (int i=0;i<downloads.size();i++){ Download download = downloads.get(i); if ( !res_l.contains( download )){ res_l.add( download ); } } } }finally{ listeners_mon.exit(); } Download[] res = new Download[res_l.size()]; res_l.toArray( res ); return( res ); } public Download[] getDownloads(boolean bSorted) { if (bSorted){ return getDownloads(); } try{ listeners_mon.enter(); Download[] res = new Download[downloads.size()]; downloads.toArray( res ); return( res ); }finally{ listeners_mon.exit(); } } public void pauseDownloads() { global_manager.pauseDownloads(); } public boolean canPauseDownloads() { return global_manager.canPauseDownloads(); } public void resumeDownloads() { global_manager.resumeDownloads(); } public boolean canResumeDownloads() { return global_manager.canResumeDownloads(); } public void startAllDownloads() { global_manager.startAllDownloads(); } public void stopAllDownloads() { global_manager.stopAllDownloads(); } public DownloadManagerStats getStats() { return( stats ); } public boolean isSeedingOnly() { return( global_manager.isSeedingOnly()); } public void addListener(DownloadManagerListener l) {addListener(l, true);} public void addListener(DownloadManagerListener l, boolean notify_of_current_downloads) { List<Download> downloads_copy = null; try { listeners_mon.enter(); List<DownloadManagerListener> new_listeners = new ArrayList<DownloadManagerListener>(listeners); new_listeners.add(l); listeners = new_listeners; if (notify_of_current_downloads) { downloads_copy = new ArrayList<Download>(downloads); // randomize list so that plugins triggering dlm-state fixups don't lock each other by doing everything in the same order Collections.shuffle(downloads_copy); } } finally { listeners_mon.exit(); } if (downloads_copy != null) { for (int i = 0; i < downloads_copy.size(); i++) { try {l.downloadAdded( downloads_copy.get(i));} catch (Throwable e) {Debug.printStackTrace(e);} } } } public void removeListener(DownloadManagerListener l) {removeListener(l, false);} public void removeListener(DownloadManagerListener l, boolean notify_of_current_downloads) { List<Download> downloads_copy = null; try { listeners_mon.enter(); List<DownloadManagerListener> new_listeners = new ArrayList<DownloadManagerListener>(listeners); new_listeners.remove(l); listeners = new_listeners; if (notify_of_current_downloads) { downloads_copy = new ArrayList<Download>(downloads); } } finally { listeners_mon.exit(); } if (downloads_copy != null) { for (int i = 0; i < downloads_copy.size(); i++) { try {l.downloadRemoved( downloads_copy.get(i));} catch (Throwable e) {Debug.printStackTrace(e);} } } } public void initialised( DownloadManager manager, boolean for_seeding ) { DownloadImpl dl; try{ listeners_mon.enter(); dl = new DownloadImpl( this, manager ); pending_dls.put( manager, dl ); }finally{ listeners_mon.exit(); } Iterator<DownloadWillBeAddedListener> it = dwba_listeners.iterator(); while( it.hasNext()){ try{ it.next().initialised(dl); }catch( Throwable e ){ Debug.printStackTrace(e); } } } public int getActions() { // assumption is that plugin based download-will-be-added listeners might assign tags so // indicate this if ( dwba_listeners.size() > 0 ){ return( ACT_ASSIGNS_TAGS ); } return( ACT_NONE ); } public void addDownloadWillBeAddedListener( DownloadWillBeAddedListener listener ) { try{ listeners_mon.enter(); dwba_listeners.add( listener ); if ( dwba_listeners.size() == 1 ){ global_manager.addDownloadManagerInitialisationAdapter( this ); } }finally{ listeners_mon.exit(); } } public void removeDownloadWillBeAddedListener( DownloadWillBeAddedListener listener ) { try{ listeners_mon.enter(); dwba_listeners.remove( listener ); if ( dwba_listeners.size() == 0 ){ global_manager.removeDownloadManagerInitialisationAdapter( this ); } }finally{ listeners_mon.exit(); } } public void addExternalDownload( Download download ) { List<DownloadManagerListener> listeners_ref = null; try{ listeners_mon.enter(); if ( downloads.contains( download )){ return; } downloads.add( download ); listeners_ref = listeners; }finally{ listeners_mon.exit(); } for (int i=0;i<listeners_ref.size();i++){ try{ listeners_ref.get(i).downloadAdded( download ); }catch( Throwable e ){ Debug.printStackTrace( e ); } } } public void removeExternalDownload( Download download ) { List<DownloadManagerListener> listeners_ref = null; try{ listeners_mon.enter(); if ( !downloads.contains( download )){ return; } downloads.remove( download ); listeners_ref = listeners; }finally{ listeners_mon.exit(); } for (int i=0;i<listeners_ref.size();i++){ try{ listeners_ref.get(i).downloadRemoved( download ); }catch( Throwable e ){ Debug.printStackTrace( e ); } } } public DownloadEventNotifier getGlobalDownloadEventNotifier() { return this.global_dl_notifier; } public void setSaveLocationManager(SaveLocationManager manager) { if (manager == null) {manager = getDefaultSaveLocationManager();} DownloadManagerMoveHandler.CURRENT_HANDLER = manager; } public SaveLocationManager getSaveLocationManager() { return DownloadManagerMoveHandler.CURRENT_HANDLER; } public DefaultSaveLocationManager getDefaultSaveLocationManager() { return DownloadManagerDefaultPaths.DEFAULT_HANDLER; } // stubbin it private static final String STUB_CONFIG_FILE = "dlarchive.config"; private static final File ARCHIVE_DIR; static{ ARCHIVE_DIR = FileUtil.getUserFile( "dlarchive" ); if ( !ARCHIVE_DIR.exists()){ FileUtil.mkdirs(ARCHIVE_DIR); } } private List<DownloadStubImpl> download_stubs = new ArrayList<DownloadStubImpl>(); private CopyOnWriteList<DownloadStubListener> download_stub_listeners = new CopyOnWriteList<DownloadStubListener>(); private FrequencyLimitedDispatcher dirty_stub_dispatcher = new FrequencyLimitedDispatcher( new AERunnable() { public void runSupport() { synchronized( download_stubs ){ writeStubConfig(); } } }, 10*1000 ); private boolean dirty_stubs = false; private void readStubConfig() { if ( FileUtil.resilientConfigFileExists( STUB_CONFIG_FILE )){ Map map = FileUtil.readResilientConfigFile( STUB_CONFIG_FILE ); List<Map> list = (List<Map>)map.get( "stubs" ); if ( list != null ){ for ( Map m: list ){ download_stubs.add( new DownloadStubImpl( this, m )); } } } } private void writeStubConfig() { if ( download_stubs.size() == 0 ){ FileUtil.deleteResilientConfigFile( STUB_CONFIG_FILE ); }else{ Map map = new HashMap(); List list = new ArrayList( download_stubs.size()); map.put( "stubs", list ); for ( DownloadStubImpl stub: download_stubs ){ list.add( stub.exportToMap()); } FileUtil.writeResilientConfigFile( STUB_CONFIG_FILE, map ); } dirty_stubs = false; } public boolean canStubbify( DownloadImpl download ) { if ( download.getState() != Download.ST_STOPPED ){ return( false ); } if ( !download.isPersistent()){ return( false ); } if ( download.getTorrent() == null ){ return( false ); } if ( download.getFlag( Download.FLAG_LOW_NOISE ) || download.getFlag( Download.FLAG_METADATA_DOWNLOAD )){ return( false ); } if ( !download.isComplete( false )){ return( false ); } return( true ); } protected DownloadStub stubbify( DownloadImpl download ) throws DownloadException, DownloadRemovalVetoException { if ( !canStubbify( download )){ throw( new DownloadException( "Download not in stubbifiable state" )); } DownloadManager core_dm = PluginCoreUtils.unwrap( download ); Map gm_data = global_manager.exportDownloadStateToMap( core_dm ); // meh, gm assumes this map is always serialised + deserialised and doesn't expect // String values try{ gm_data = BDecoder.decode( BEncoder.encode( gm_data )); }catch( IOException e ){ Debug.out( e ); } DownloadStubImpl stub = new DownloadStubImpl( this, download, gm_data ); try{ informAdded( stub, true ); }finally{ stub.setStubbified(); } boolean added = false; try{ core_dm.getDownloadState().exportState( ARCHIVE_DIR ); download.remove( false, false ); synchronized( download_stubs ){ download_stubs.add( stub ); writeStubConfig(); } added = true; informAdded( stub, false ); }finally{ if ( !added ){ // inform that the 'will be added' failed informRemoved( stub, true ); } } return( stub ); } protected Download destubbify( DownloadStubImpl stub ) throws DownloadException { boolean removed = false; informRemoved( stub, true ); try{ byte[] torrent_hash = stub.getTorrentHash(); try{ DownloadManagerStateFactory.importDownloadState( ARCHIVE_DIR, torrent_hash ); }catch( Throwable e ){ throw( new DownloadException( "Failed to import download state", e )); } DownloadManager core_dm = global_manager.importDownloadStateFromMap( stub.getGMMap()); if ( core_dm == null ){ try{ DownloadManagerStateFactory.deleteDownloadState( torrent_hash ); }catch( Throwable e ){ Debug.out( e ); } throw( new DownloadException( "Failed to add download" )); }else{ try{ DownloadManagerStateFactory.deleteDownloadState( ARCHIVE_DIR, torrent_hash ); }catch( Throwable e ){ Debug.out( e ); } synchronized( download_stubs ){ download_stubs.remove( stub ); writeStubConfig(); } removed = true; informRemoved( stub, false ); return( PluginCoreUtils.wrap( core_dm )); } }finally{ if ( !removed ){ // inform that the 'will be removed' failed informAdded( stub, true ); } } } protected void remove( DownloadStubImpl stub ) { boolean removed = false; informRemoved( stub, true ); try{ try{ DownloadManagerStateFactory.deleteDownloadState( ARCHIVE_DIR, stub.getTorrentHash()); }catch( Throwable e ){ Debug.out( e ); } synchronized( download_stubs ){ download_stubs.remove( stub ); writeStubConfig(); } removed = true; informRemoved( stub, false ); }finally{ if ( !removed ){ informAdded( stub, true ); } } } protected void updated( DownloadStubImpl stub ) { synchronized( download_stubs ){ dirty_stubs = true; } dirty_stub_dispatcher.dispatch(); } public DownloadStub[] getDownloadStubs() { synchronized( download_stubs ){ return( download_stubs.toArray( new DownloadStub[download_stubs.size()])); } } private void informAdded( DownloadStub stub, final boolean preparing ) { final List<DownloadStub> list = new ArrayList<DownloadStub>(); list.add( stub ); for ( DownloadStubListener l: download_stub_listeners ){ try{ l.downloadStubEventOccurred( new DownloadStubEvent() { public int getEventType() { return( preparing?DownloadStubEvent.DSE_STUB_WILL_BE_ADDED:DownloadStubEvent.DSE_STUB_ADDED ); } public List<DownloadStub> getDownloadStubs() { return( list ); } }); }catch( Throwable e ){ Debug.out( e ); } } } private void informRemoved( DownloadStub stub, final boolean preparing ) { final List<DownloadStub> list = new ArrayList<DownloadStub>(); list.add( stub ); for ( DownloadStubListener l: download_stub_listeners ){ try{ l.downloadStubEventOccurred( new DownloadStubEvent() { public int getEventType() { return( preparing?DownloadStubEvent.DSE_STUB_WILL_BE_REMOVED:DownloadStubEvent.DSE_STUB_REMOVED ); } public List<DownloadStub> getDownloadStubs() { return( list ); } }); }catch( Throwable e ){ Debug.out( e ); } } } public void addDownloadStubListener( DownloadStubListener l, boolean inform_of_current ) { download_stub_listeners.add( l ); if ( inform_of_current ){ final List<DownloadStub> existing; synchronized( download_stubs ){ existing = new ArrayList<DownloadStub>( download_stubs ); } try{ l.downloadStubEventOccurred( new DownloadStubEvent() { public int getEventType() { return( DownloadStubEvent.DSE_STUB_ADDED ); } public List<DownloadStub> getDownloadStubs() { return( existing ); } }); }catch( Throwable e ){ Debug.out( e ); } } } public void removeDownloadStubListener( DownloadStubListener l ) { download_stub_listeners.remove( l ); } }