/*
* Created on Jul 16, 2008
* Created by Paul Gardner
*
* Copyright 2008 Vuze, Inc. 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; version 2 of the License only.
*
* 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.
*/
package com.aelitis.azureus.core.lws;
import java.io.File;
import java.net.InetSocketAddress;
import java.net.URL;
import org.gudy.azureus2.core3.disk.DiskManager;
import org.gudy.azureus2.core3.disk.DiskManagerReadRequest;
import org.gudy.azureus2.core3.disk.DiskManagerReadRequestListener;
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.PEPeer;
import org.gudy.azureus2.core3.peer.PEPeerManager;
import org.gudy.azureus2.core3.peer.PEPeerManagerFactory;
import org.gudy.azureus2.core3.peer.PEPeerManagerListener;
import org.gudy.azureus2.core3.peer.PEPiece;
import org.gudy.azureus2.core3.peer.util.PeerUtils;
import org.gudy.azureus2.core3.torrent.TOTorrent;
import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncer;
import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncerDataProvider;
import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncerException;
import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncerFactory;
import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncerListener;
import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncerResponse;
import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncerResponsePeer;
import org.gudy.azureus2.core3.util.ByteFormatter;
import org.gudy.azureus2.core3.util.HashWrapper;
import org.gudy.azureus2.core3.util.SystemTime;
import org.gudy.azureus2.plugins.torrent.Torrent;
import org.gudy.azureus2.pluginsimpl.local.torrent.TorrentImpl;
import com.aelitis.azureus.core.networkmanager.NetworkConnection;
import com.aelitis.azureus.core.networkmanager.NetworkManager;
import com.aelitis.azureus.core.peermanager.PeerManager;
import com.aelitis.azureus.core.peermanager.PeerManagerRegistration;
import com.aelitis.azureus.core.peermanager.PeerManagerRegistrationAdapter;
import com.aelitis.azureus.core.peermanager.peerdb.PeerItem;
public class
LightWeightSeed
extends LogRelation
implements PeerManagerRegistrationAdapter
{
private static final byte ACT_NONE = 0;
private static final byte ACT_HAS_PEERS = 2;
private static final byte ACT_HAS_POTENTIAL_PEERS = 3;
private static final byte ACT_INCOMING = 4;
private static final byte ACT_NO_PM = 5;
private static final byte ACT_TIMING_OUT = 6;
private static final byte ACT_TRACKER_ANNOUNCE = 7;
private static final byte ACT_TRACKER_SCRAPE = 8;
private static final int DEACTIVATION_TIMEOUT = 5*60*1000;
private static final int DEACTIVATION_WITH_POTENTIAL_TIMEOUT = 15*60*1000;
private LightWeightSeedManager manager;
private LightWeightSeedAdapter adapter;
private String name;
private HashWrapper hash;
private URL announce_url;
private File data_location;
private PeerManagerRegistration peer_manager_registration;
private PEPeerManager peer_manager;
private LWSDiskManager disk_manager;
private LWSDownload pseudo_download;
private LWSTorrent torrent_facade;
private TRTrackerAnnouncer announcer;
private TOTorrent actual_torrent;
private boolean is_running;
private long last_activity_time;
private int activation_state = ACT_NONE;
protected
LightWeightSeed(
LightWeightSeedManager _manager,
String _name,
HashWrapper _hash,
URL _announce_url,
File _data_location,
LightWeightSeedAdapter _adapter )
{
manager = _manager;
name = _name;
hash = _hash;
announce_url = _announce_url;
data_location = _data_location;
adapter = _adapter;
}
protected String
getName()
{
return( name + "/" + ByteFormatter.encodeString( hash.getBytes()));
}
protected Torrent
getTorrent()
{
return( new TorrentImpl( getTOTorrent( false )));
}
protected TOTorrent
getTOTorrent(
boolean actual )
{
if ( actual ){
synchronized( this ){
if ( actual_torrent == null ){
try{
actual_torrent = adapter.getTorrent( hash.getBytes(), announce_url, data_location );
}catch( Throwable e ){
log( "Failed to get torrent", e );
}
if ( actual_torrent == null ){
throw( new RuntimeException( "Torrent not available" ));
}
}
return( actual_torrent );
}
}else{
return( torrent_facade );
}
}
public HashWrapper
getHash()
{
return( hash );
}
public URL
getAnnounceURL()
{
return( announce_url );
}
public File
getDataLocation()
{
return( data_location );
}
protected long
getSize()
{
return( data_location.length());
}
public boolean
isPeerSourceEnabled(
String peer_source )
{
return( true );
}
public boolean
manualRoute(
NetworkConnection connection )
{
return false;
}
public byte[][]
getSecrets()
{
return( new byte[][]{ hash.getBytes()});
}
public boolean
activateRequest(
InetSocketAddress remote_address )
{
ensureActive( "Incoming[" + remote_address.getAddress().getHostAddress() + "]", ACT_INCOMING );
return( true );
}
public void
deactivateRequest(
InetSocketAddress remote_address )
{
}
public String
getDescription()
{
return( "LWS: " + getName());
}
protected synchronized void
start()
{
log( "Start" );
if ( is_running ){
log( "Start of '" + getString() + "' failed - already running" );
return;
}
if ( peer_manager_registration != null ){
log( "Start of '" + getString() + "' failed - router already registered" );
return;
}
if ( pseudo_download != null ){
log( "Start of '" + getString() + "' failed - pseudo download already registered" );
return;
}
if ( disk_manager != null ){
log( "Start of '" + getString() + "' failed - disk manager already started" );
return;
}
if ( peer_manager != null ){
log( "Start of '" + getString() + "' failed - peer manager already started" );
return;
}
try{
if ( torrent_facade == null ){
torrent_facade = new LWSTorrent( this );
}
peer_manager_registration = PeerManager.getSingleton().registerLegacyManager( hash, this );
announcer = createAnnouncer();
pseudo_download = new LWSDownload( this, announcer );
manager.addToDHTTracker( pseudo_download );
is_running = true;
last_activity_time = SystemTime.getMonotonousTime();
}catch( Throwable e ){
log( "Start of '" + getString() + "' failed", e );
}finally{
if ( is_running ){
log( "Started " + getString());
}else{
stop();
}
}
}
protected synchronized void
stop()
{
log( "Stop" );
try{
if ( disk_manager != null ){
disk_manager.stop( false );
disk_manager = null;
}
if ( peer_manager != null ){
peer_manager.stopAll();
peer_manager = null;
}
if ( pseudo_download != null ){
manager.removeFromDHTTracker( pseudo_download );
pseudo_download = null;
}
if ( announcer != null ){
announcer.stop( false );
announcer.destroy();
announcer = null;
}
if ( peer_manager_registration != null ){
peer_manager_registration.unregister();
peer_manager_registration = null;
}
}finally{
is_running = false;
activation_state = ACT_NONE;
log( "Stopped " + getString());
}
}
protected synchronized void
activate(
String reason_str,
byte activation_reason )
{
log( "Activate: " + activation_reason + "/" + reason_str );
if ( activation_state != ACT_NONE ){
return;
}
try{
disk_manager = new LWSDiskManager( this, data_location );
disk_manager.start();
if ( disk_manager.getState() != DiskManager.READY ){
log( "Start of '" + getString() + "' failed, disk manager failed = " + disk_manager.getErrorMessage());
}else{
peer_manager =
PEPeerManagerFactory.create(
announcer.getPeerId(),
new LWSPeerManagerAdapter(
this,
peer_manager_registration ),
disk_manager );
peer_manager.addListener(
new PEPeerManagerListener()
{
public void
peerAdded(
final PEPeerManager manager,
final PEPeer peer )
{
last_activity_time = SystemTime.getMonotonousTime();
}
public void
peerRemoved(
PEPeerManager manager,
PEPeer peer )
{
last_activity_time = SystemTime.getMonotonousTime();
}
public void
peerDiscovered(
PEPeerManager manager,
PeerItem peer,
PEPeer finder)
{
}
public void
pieceAdded(
PEPeerManager manager,
PEPiece peice,
PEPeer for_peer )
{
}
public void
pieceRemoved(
PEPeerManager manager,
PEPiece peice )
{
}
public void
peerSentBadData(
PEPeerManager manager,
PEPeer peer,
int pieceNumber)
{
}
public void
destroyed()
{
}
});
peer_manager.start();
announcer.update( true );
activation_state = activation_reason;
last_activity_time = SystemTime.getMonotonousTime();
}
}catch( Throwable e ){
log( "Activation of '" + getString() + "' failed", e );
}finally{
if ( activation_state != ACT_NONE ){
}else{
deactivate();
}
}
}
protected synchronized void
deactivate()
{
log( "Deactivate" );
try{
if ( disk_manager != null ){
disk_manager.stop( false );
disk_manager = null;
}
if ( peer_manager != null ){
peer_manager.stopAll();
peer_manager = null;
}
}finally{
activation_state = ACT_NONE;
}
}
protected synchronized TRTrackerAnnouncer
createAnnouncer()
throws TRTrackerAnnouncerException
{
// use a facade here to delay loading the actual torrent until the
// download is activated
TRTrackerAnnouncer result = TRTrackerAnnouncerFactory.create( torrent_facade, true );
result.addListener(
new TRTrackerAnnouncerListener()
{
public void
receivedTrackerResponse(
TRTrackerAnnouncerResponse response )
{
TRTrackerAnnouncerResponsePeer[] peers = response.getPeers();
// tracker shouldn't return seeds to seeds to we can assume
// that if peers returned this means we have someone to talk to
if ( peers != null && peers.length > 0 ){
ensureActive( "Tracker[" + peers[0].getAddress()+ "]", ACT_TRACKER_ANNOUNCE );
}else if ( response.getScrapeIncompleteCount() > 0 ){
ensureActive( "Tracker[scrape]", ACT_TRACKER_SCRAPE );
}
PEPeerManager pm = peer_manager;
if ( pm != null ){
pm.processTrackerResponse( response );
}
}
public void
urlChanged(
TRTrackerAnnouncer announcer,
URL old_url,
URL new_url,
boolean explicit )
{
}
public void
urlRefresh()
{
}
});
result.setAnnounceDataProvider(
new TRTrackerAnnouncerDataProvider()
{
public String
getName()
{
return( LightWeightSeed.this.getName());
}
public long
getTotalSent()
{
return( 0 );
}
public long
getTotalReceived()
{
return( 0 );
}
public long
getFailedHashCheck()
{
return( 0 );
}
public long
getRemaining()
{
return( 0 );
}
public String
getExtensions()
{
return( null );
}
public int
getMaxNewConnectionsAllowed()
{
PEPeerManager pm = peer_manager;
if ( pm == null ){
// got to ask for at least one to trigger activation!
return( 8 );
}
return( PeerUtils.numNewConnectionsAllowed( pm.getPeerIdentityDataID(),0));
}
public int
getPendingConnectionCount()
{
PEPeerManager pm = peer_manager;
if ( pm == null ){
return( 0 );
}
return( pm.getPendingPeerCount());
}
public int
getConnectedConnectionCount()
{
PEPeerManager pm = peer_manager;
if ( pm == null ){
return( 0 );
}
return( pm.getNbPeers() + pm.getNbSeeds());
}
public int
getUploadSpeedKBSec(
boolean estimate)
{
return 0;
}
public int
getCryptoLevel()
{
return( NetworkManager.CRYPTO_OVERRIDE_NONE );
}
public boolean
isPeerSourceEnabled(
String peer_source )
{
return( true );
}
public void
setPeerSources(
String[] sources )
{
}
});
return( result );
}
protected synchronized void
ensureActive(
String reason,
byte a_reason )
{
if ( is_running && activation_state == ACT_NONE ){
activate( reason, a_reason );
}
}
protected synchronized void
checkDeactivation()
{
if ( activation_state == ACT_NONE ){
return;
}
if ( peer_manager == null ){
activation_state = ACT_NO_PM;
return;
}
if ( peer_manager.getNbPeers() > 0 ){
activation_state = ACT_HAS_PEERS;
return;
}
long now = SystemTime.getMonotonousTime();
long millis_since_last_act = now - last_activity_time;
if ( peer_manager.hasPotentialConnections()){
if ( millis_since_last_act < DEACTIVATION_WITH_POTENTIAL_TIMEOUT ){
activation_state = ACT_HAS_POTENTIAL_PEERS;
return;
}
}
if ( millis_since_last_act >= DEACTIVATION_TIMEOUT ){
deactivate();
}else{
activation_state = ACT_TIMING_OUT;
}
}
public void
enqueueReadRequest(
PEPeer peer,
DiskManagerReadRequest request,
DiskManagerReadRequestListener listener )
{
LWSDiskManager dm = disk_manager;
if ( dm == null ){
listener.readFailed( request, new Throwable( "download is stopped" ));
}else{
dm.enqueueReadRequest( request, listener );
}
}
public void
remove()
{
manager.remove( this );
}
public String
getRelationText()
{
return "LWS: '" + getName() + "'";
}
public Object[]
getQueryableInterfaces()
{
return new Object[]{};
}
public LogRelation
getRelation()
{
return( this );
}
protected String
getString()
{
return( getName());
}
protected void
log(
String str )
{
Logger.log(new LogEvent(this, LogIDs.CORE, str ));
}
protected void
log(
String str,
Throwable e )
{
Logger.log(new LogEvent(this, LogIDs.CORE, str, e ));
}
}