/* * Created on 15-Dec-2005 * Created by Paul Gardner * Copyright (C) 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 com.aelitis.azureus.plugins.extseed; import java.io.PrintWriter; import java.io.StringWriter; import java.util.*; import org.gudy.azureus2.core3.download.DownloadManager; import org.gudy.azureus2.core3.download.DownloadManagerState; import org.gudy.azureus2.core3.util.AENetworkClassifier; import org.gudy.azureus2.core3.util.AERunnable; import org.gudy.azureus2.core3.util.AEThread2; import org.gudy.azureus2.core3.util.Debug; import org.gudy.azureus2.core3.util.DelayedEvent; import org.gudy.azureus2.core3.util.HashWrapper; import org.gudy.azureus2.core3.util.TorrentUtils; import org.gudy.azureus2.plugins.Plugin; import org.gudy.azureus2.plugins.PluginInterface; import org.gudy.azureus2.plugins.download.Download; import org.gudy.azureus2.plugins.download.DownloadListener; import org.gudy.azureus2.plugins.download.DownloadManagerListener; import org.gudy.azureus2.plugins.download.DownloadManagerStats; import org.gudy.azureus2.plugins.download.DownloadPeerListener; import org.gudy.azureus2.plugins.logging.LoggerChannel; import org.gudy.azureus2.plugins.logging.LoggerChannelListener; import org.gudy.azureus2.plugins.peers.PeerManager; import org.gudy.azureus2.plugins.torrent.Torrent; import org.gudy.azureus2.plugins.ui.model.BasicPluginViewModel; import org.gudy.azureus2.plugins.utils.*; import com.aelitis.azureus.plugins.extseed.impl.getright.ExternalSeedReaderFactoryGetRight; import com.aelitis.azureus.plugins.extseed.impl.webseed.ExternalSeedReaderFactoryWebSeed; public class ExternalSeedPlugin implements Plugin, DownloadManagerListener, DownloadListener { private static ExternalSeedReaderFactory[] factories = { new ExternalSeedReaderFactoryGetRight(), new ExternalSeedReaderFactoryWebSeed(), }; private PluginInterface plugin_interface; private DownloadManagerStats dm_stats; private LoggerChannel log; private Random random = new Random(); private Map download_map = new HashMap(); private Monitor download_mon; public void initialize( PluginInterface _plugin_interface ) { plugin_interface = _plugin_interface; dm_stats = plugin_interface.getDownloadManager().getStats(); plugin_interface.getPluginProperties().setProperty( "plugin.version", "1.0" ); plugin_interface.getPluginProperties().setProperty( "plugin.name", "External Seed" ); log = plugin_interface.getLogger().getTimeStampedChannel( "External Seeds" ); final BasicPluginViewModel view_model = plugin_interface.getUIManager().createBasicPluginViewModel( "Plugin.extseed.name" ); view_model.getActivity().setVisible( false ); view_model.getProgress().setVisible( false ); log.addListener( new LoggerChannelListener() { public void messageLogged( int type, String content ) { view_model.getLogArea().appendText( content + "\n" ); } public void messageLogged( String str, Throwable error ) { if ( str.length() > 0 ){ view_model.getLogArea().appendText( str + "\n" ); } StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter( sw ); error.printStackTrace( pw ); pw.flush(); view_model.getLogArea().appendText( sw.toString() + "\n" ); } }); download_mon = plugin_interface.getUtilities().getMonitor(); Utilities utilities = plugin_interface.getUtilities(); // XXX Would be better if we fired this off after (any) UI is complete, // instead of a timer new DelayedEvent( "ExternalSeedInitialise", 15000, new AERunnable() { public void runSupport() { AEThread2 t = new AEThread2( "ExternalSeedInitialise", true ) { public void run() { plugin_interface.getDownloadManager().addListener( ExternalSeedPlugin.this); } }; t.setPriority( Thread.MIN_PRIORITY ); t.start(); } }); UTTimer timer = utilities.createTimer("ExternalPeerScheduler", true); timer.addPeriodicEvent( 5000, new UTTimerEventPerformer() { public void perform( UTTimerEvent event ) { try{ Iterator it = download_map.values().iterator(); while( it.hasNext()){ List peers = randomiseList((List)it.next()); for (int i=0;i<peers.size();i++){ // bail out early if the state changed for this peer // so one peer at a time gets a chance to activate if (((ExternalSeedPeer)peers.get(i)).checkConnection()){ break; } } } }catch( Throwable e ){ // we do this without holding the monitor as doing so causes potential // deadlock between download_mon and the connection's connection_mon // so ignore possible errors here that may be caused by concurrent // modification to the download_map ans associated lists. We are only // reading the data so errors will only be transient } } }); } public void downloadAdded( Download download ) { Torrent torrent = download.getTorrent(); download.addListener(this); if ( torrent == null ){ return; } addExtPeers(download); } private void addExtPeers(Download download) { if(!networkEnabled(download)){ return; } List peers = new ArrayList(); for (int i=0;i<factories.length;i++){ ExternalSeedReader[] x = factories[i].getSeedReaders( this, download ); for (int j=0;j<x.length;j++){ ExternalSeedReader reader = x[j]; ExternalSeedPeer peer = new ExternalSeedPeer( this, download, reader ); peers.add( peer ); } } log.log(LoggerChannel.LT_INFORMATION,"adding peers: " + peers.size()); addPeers( download, peers ); } // ********************************** // edit, by isdal // require http seeds to have http seed network enabled // ********************************** private boolean networkEnabled(Download download) { log.log(LoggerChannel.LT_INFORMATION, "checking networks: " + download.getName()); byte[] infohash = download.getTorrent().getHash(); if (infohash == null) { Debug.out("infohash=null"); return false; } DownloadManager dm = TorrentUtils.getDownloadManager(new HashWrapper( infohash)); if (dm == null) { Debug.out("downloadmanager=null"); return false; } DownloadManagerState state = dm.getDownloadState(); if (state == null) { return false; } boolean networkEnabled = false; for (String network : state.getNetworks()) { if (network.equals(AENetworkClassifier.AT_WEBSEED)) { networkEnabled = true; break; } } if (!networkEnabled) { log.log(LoggerChannel.LT_INFORMATION, "http seed network not enabled"); return false; } return true; } //********************************** public void addSeed( Download download, Map config ) { Torrent torrent = download.getTorrent(); if ( torrent == null ){ return; } addExtPeers(download); } protected void addPeers( final Download download, List _peers ) { //*************************************** if(!networkEnabled(download)){ return; } //************************************* final List peers = new ArrayList(); peers.addAll( _peers ); if ( peers.size() > 0 ){ boolean add_listener = false; try{ download_mon.enter(); List existing_peers = (List)download_map.get( download ); if ( existing_peers == null ){ add_listener = true; existing_peers = new ArrayList(); download_map.put( download, existing_peers ); } Iterator it = peers.iterator(); while( it.hasNext()){ ExternalSeedPeer peer = (ExternalSeedPeer)it.next(); boolean skip = false; for (int j=0;j<existing_peers.size();j++){ ExternalSeedPeer existing_peer = (ExternalSeedPeer)existing_peers.get(j); if ( existing_peer.sameAs( peer )){ skip = true; break; } } if ( skip ){ it.remove(); }else{ log( download.getName() + " found seed " + peer.getName()); existing_peers.add( peer ); } } }finally{ download_mon.exit(); } if ( add_listener ){ download.addPeerListener( new DownloadPeerListener() { public void peerManagerAdded( Download download, PeerManager peer_manager ) { List existing_peers = getPeers(); if ( existing_peers== null ){ return; } for (int i=0;i<existing_peers.size();i++){ ExternalSeedPeer peer = (ExternalSeedPeer)existing_peers.get(i); peer.setManager( peer_manager ); } } public void peerManagerRemoved( Download download, PeerManager peer_manager ) { List existing_peers = getPeers(); if ( existing_peers== null ){ return; } for (int i=0;i<existing_peers.size();i++){ ExternalSeedPeer peer = (ExternalSeedPeer)existing_peers.get(i); peer.setManager( null ); } } protected List getPeers() { List existing_peers = null; try{ download_mon.enter(); List temp = (List)download_map.get( download ); if ( temp != null ){ existing_peers = new ArrayList( temp.size()); existing_peers.addAll( temp ); } }finally{ download_mon.exit(); } return( existing_peers ); } }); }else{ // fix up newly added peers to current peer manager PeerManager existing_pm = download.getPeerManager(); if ( existing_pm != null ){ for (int i=0;i<peers.size();i++){ ExternalSeedPeer peer = (ExternalSeedPeer)peers.get(i); if ( peer.getManager() == null ){ peer.setManager( existing_pm ); } } } } } } protected void removePeer( ExternalSeedPeer peer ) { Download download = peer.getDownload(); try{ download_mon.enter(); List existing_peers = (List)download_map.get( download ); if ( existing_peers != null ){ if ( existing_peers.remove( peer )){ log( download.getName() + " removed seed " + peer.getName()); } } }finally{ download_mon.exit(); } } public void downloadRemoved( Download download ) { try{ download_mon.enter(); download_map.remove( download ); }finally{ download_mon.exit(); } } public ExternalSeedManualPeer[] getManualWebSeeds( Download download ) { try{ download_mon.enter(); List peers = (List)download_map.get( download ); ExternalSeedManualPeer[] result = new ExternalSeedManualPeer[peers.size()]; for (int i=0;i<peers.size();i++){ result[i] = new ExternalSeedManualPeer((ExternalSeedPeer)peers.get(i)); } return( result ); }finally{ download_mon.exit(); } } public int getGlobalDownloadRateBytesPerSec() { return( dm_stats.getDataAndProtocolReceiveRate()); } public void log( String str ) { log.log( str ); } public void log( String str, Throwable e ) { log.log( str, e ); } public PluginInterface getPluginInterface() { return( plugin_interface ); } protected List randomiseList( List l ) { if ( l.size() < 2 ){ return(l); } List new_list = new ArrayList(); for (int i=0;i<l.size();i++){ new_list.add( random.nextInt(new_list.size()+1), l.get(i)); } return( new_list ); } public void positionChanged(Download download, int oldPosition, int newPosition) { } public void stateChanged(Download download, int old_state, int new_state) { if(new_state == Download.ST_DOWNLOADING){ log.log(LoggerChannel.LT_INFORMATION,"download started: "+download.getName()); this.addExtPeers(download); } } }