/*
* 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.util.AEThread2;
import org.gudy.azureus2.core3.util.SystemTime;
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.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.torrent.TorrentAttribute;
import org.gudy.azureus2.plugins.ui.components.UITextField;
import org.gudy.azureus2.plugins.ui.model.BasicPluginViewModel;
import org.gudy.azureus2.plugins.utils.*;
import org.gudy.azureus2.pluginsimpl.local.PluginInitializer;
import com.aelitis.azureus.core.tracker.TrackerPeerSource;
import com.aelitis.azureus.core.tracker.TrackerPeerSourceAdapter;
import com.aelitis.azureus.plugins.extseed.impl.getright.ExternalSeedReaderFactoryGetRight;
import com.aelitis.azureus.plugins.extseed.impl.webseed.ExternalSeedReaderFactoryWebSeed;
public class
ExternalSeedPlugin
implements Plugin, DownloadManagerListener
{
private static ExternalSeedReaderFactory[] factories = {
new ExternalSeedReaderFactoryGetRight(),
new ExternalSeedReaderFactoryWebSeed(),
};
private PluginInterface plugin_interface;
private DownloadManagerStats dm_stats;
private UITextField status_field;
private LoggerChannel log;
private Random random = new Random();
private Map download_map = new HashMap();
private Monitor download_mon;
public static void
load(
PluginInterface plugin_interface )
{
plugin_interface.getPluginProperties().setProperty( "plugin.version", "1.0" );
plugin_interface.getPluginProperties().setProperty( "plugin.name", "External Seed" );
}
public void
initialize(
PluginInterface _plugin_interface )
{
plugin_interface = _plugin_interface;
dm_stats = plugin_interface.getDownloadManager().getStats();
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" );
}
});
status_field = view_model.getStatus();
setStatus( "Initialising" );
download_mon = plugin_interface.getUtilities().getMonitor();
Utilities utilities = plugin_interface.getUtilities();
final DelayedTask dt = plugin_interface.getUtilities().createDelayedTask(new Runnable()
{
public void
run()
{
AEThread2 t =
new AEThread2( "ExternalSeedInitialise", true )
{
public void
run()
{
setStatus( "Running" );
plugin_interface.getDownloadManager().addListener( ExternalSeedPlugin.this);
}
};
t.setPriority( Thread.MIN_PRIORITY );
t.start();
}
});
dt.queue();
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();
if ( torrent == null ){
return;
}
List peers = new ArrayList();
for (int i=0;i<factories.length;i++){
String attributeID = "no-ext-seeds-" + factories[i].getClass().getSimpleName();
TorrentAttribute attribute = plugin_interface.getTorrentManager().getPluginAttribute( attributeID );
boolean noExternalSeeds = download.getBooleanAttribute(attribute);
if (noExternalSeeds) {
continue;
}
ExternalSeedReader[] x = factories[i].getSeedReaders( this, download );
if (x.length == 0) {
download.setBooleanAttribute(attribute, true);
} else {
for (int j=0;j<x.length;j++){
ExternalSeedReader reader = x[j];
ExternalSeedPeer peer = new ExternalSeedPeer( this, download, reader );
peers.add( peer );
}
}
}
addPeers( download, peers );
}
public void
downloadChanged(
Download download )
{
downloadRemoved( download );
downloadAdded( download );
}
public List<ExternalSeedPeer>
addSeed(
Download download,
Map config )
{
Torrent torrent = download.getTorrent();
List<ExternalSeedPeer> peers = new ArrayList<ExternalSeedPeer>();
if ( torrent != null ){
for (int i=0;i<factories.length;i++){
String attributeID = "no-ext-seeds-" + factories[i].getClass().getSimpleName();
TorrentAttribute attribute = plugin_interface.getTorrentManager().getPluginAttribute( attributeID );
ExternalSeedReader[] x = factories[i].getSeedReaders( this, download, config );
download.setBooleanAttribute(attribute, x.length == 0);
for (int j=0;j<x.length;j++){
ExternalSeedReader reader = x[j];
ExternalSeedPeer peer = new ExternalSeedPeer( this, download, reader );
peers.add( peer );
}
}
addPeers( download, peers );
}
return( peers );
}
protected void
addPeers(
final Download download,
List _peers )
{
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 );
}
}
setStatus( "Running: Downloads with external seeds = " + download_map.size());
}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 );
setStatus( "Running: Downloads with external seeds = " + download_map.size());
}finally{
download_mon.exit();
}
}
public ExternalSeedManualPeer[]
getManualWebSeeds(
Download download )
{
try{
download_mon.enter();
List peers = (List)download_map.get( download );
if ( peers == null ){
return( new ExternalSeedManualPeer[0] );
}
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 TrackerPeerSource
getTrackerPeerSource(
final Download download )
{
return(
new TrackerPeerSourceAdapter()
{
private long fixup_time;
private ExternalSeedManualPeer[] peers;
private boolean running;
public int
getType()
{
return( TP_HTTP_SEED );
}
public int
getStatus()
{
fixup();
if ( running ){
return( peers.length==0?ST_UNAVAILABLE:ST_AVAILABLE );
}else{
return( ST_STOPPED );
}
}
public String
getName()
{
fixup();
if ( peers.length == 0 ){
return( "" );
}
StringBuffer sb = new StringBuffer();
for ( ExternalSeedManualPeer peer: peers ){
if ( sb.length() > 0 ){
sb.append( ", " );
}
String str = peer.getDelegate().getURL().toExternalForm();
int pos = str.indexOf( '?' );
if ( pos != -1 ){
str = str.substring( 0, pos );
}
sb.append( str );
}
return( sb.toString());
}
public int
getPeers()
{
fixup();
if ( running ){
return( peers.length );
}else{
return( -1 );
}
}
protected void
fixup()
{
long now = SystemTime.getMonotonousTime();
if ( now - fixup_time > 10*1000 ){
fixup_time = now;
peers = getManualWebSeeds(download);
int state = download.getState();
running = state == Download.ST_DOWNLOADING || state == Download.ST_SEEDING;
}
}
});
}
public int
getGlobalDownloadRateBytesPerSec()
{
return( dm_stats.getDataAndProtocolReceiveRate());
}
protected void
setStatus(
String str )
{
status_field.setText( str );
}
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 );
}
}