📄 externalseedreaderimpl.java
字号:
/*
* 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.impl;
import java.util.*;
import org.gudy.azureus2.core3.util.AESemaphore;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.plugins.PluginInterface;
import org.gudy.azureus2.plugins.clientid.ClientIDGenerator;
import org.gudy.azureus2.plugins.download.Download;
import org.gudy.azureus2.plugins.peers.Peer;
import org.gudy.azureus2.plugins.peers.PeerManager;
import org.gudy.azureus2.plugins.peers.PeerReadRequest;
import org.gudy.azureus2.plugins.peers.Piece;
import org.gudy.azureus2.plugins.torrent.Torrent;
import org.gudy.azureus2.plugins.utils.Monitor;
import org.gudy.azureus2.plugins.utils.PooledByteBuffer;
import org.gudy.azureus2.plugins.utils.Semaphore;
import com.aelitis.azureus.plugins.extseed.ExternalSeedException;
import com.aelitis.azureus.plugins.extseed.ExternalSeedPlugin;
import com.aelitis.azureus.plugins.extseed.ExternalSeedReader;
import com.aelitis.azureus.plugins.extseed.ExternalSeedReaderListener;
public abstract class
ExternalSeedReaderImpl
implements ExternalSeedReader
{
protected static final int RECONNECT_DEFAULT = 30*1000;
private ExternalSeedPlugin plugin;
private Torrent torrent;
private String status;
private boolean active;
private boolean permanent_fail;
private long last_failed_read;
private int consec_failures;
private String user_agent;
private long peer_manager_change_time;
private volatile PeerManager current_manager;
private List requests = new LinkedList();
private Thread request_thread;
private Semaphore request_sem;
private Monitor requests_mon;
private ExternalSeedReaderRequest active_read_request;
private int[] priority_offsets;
private int min_availability;
private int min_speed;
private long valid_until;
private boolean transient_seed;
private int reconnect_delay = RECONNECT_DEFAULT;
private volatile ExternalSeedReaderRequest current_request;
private List listeners = new ArrayList();
private AESemaphore rate_sem = new AESemaphore( "ExternalSeedReaderRequest" );
private int rate_bytes_read;
private int rate_bytes_permitted;
protected
ExternalSeedReaderImpl(
ExternalSeedPlugin _plugin,
Torrent _torrent,
Map _params )
{
plugin = _plugin;
torrent = _torrent;
min_availability = getIntParam( _params, "min_avail", 1 ); // default is avail based
min_speed = getIntParam( _params, "min_speed", 0 );
valid_until = getIntParam( _params, "valid_ms", 0 );
if ( valid_until > 0 ){
valid_until += getSystemTime();
}
transient_seed = getBooleanParam( _params, "transient", false );
requests_mon = plugin.getPluginInterface().getUtilities().getMonitor();
request_sem = plugin.getPluginInterface().getUtilities().getSemaphore();
PluginInterface pi = plugin.getPluginInterface();
user_agent = pi.getAzureusName();
try{
Properties props = new Properties();
pi.getClientIDManager().getGenerator().generateHTTPProperties( props );
String ua = props.getProperty( ClientIDGenerator.PR_USER_AGENT );
if ( ua != null ){
user_agent = ua;
}
}catch( Throwable e ){
}
setActive( false );
}
public Torrent
getTorrent()
{
return( torrent );
}
public String
getStatus()
{
return( status );
}
public boolean
isTransient()
{
return( transient_seed );
}
protected void
log(
String str )
{
plugin.log( str );
}
protected String
getUserAgent()
{
return( user_agent );
}
protected long
getSystemTime()
{
return( plugin.getPluginInterface().getUtilities().getCurrentSystemTime());
}
protected int
getFailureCount()
{
return( consec_failures );
}
protected long
getLastFailTime()
{
return( last_failed_read );
}
public boolean
isPermanentlyUnavailable()
{
return( permanent_fail );
}
protected void
setReconnectDelay(
int delay )
{
reconnect_delay = delay;
}
protected boolean
readyToActivate(
PeerManager peer_manager,
Peer peer,
long time_since_start )
{
boolean early_days = time_since_start < 30000;
try{
// first respect failure count
int fail_count = getFailureCount();
if ( fail_count > 0 ){
int delay = reconnect_delay;
for (int i=1;i<fail_count;i++){
delay += delay;
if ( delay > 30*60*1000 ){
break;
}
}
long now = getSystemTime();
long last_fail = getLastFailTime();
if ( last_fail < now && now - last_fail < delay ){
return( false );
}
}
// next obvious things like validity and the fact that we're complete
if ( valid_until > 0 && getSystemTime() > valid_until ){
return( false );
}
if ( peer_manager.getDownload().getState() == Download.ST_SEEDING ){
return( false );
}
// now the more interesting stuff
if ( transient_seed ){
if ( peer_manager.getPeers( getIP()).length == 0 ){
// check to see if we have pending connections to the same address
if ( peer_manager.getPendingPeers( getIP()).length == 0 ){
log( getName() + ": activating as transient seed and nothing blocking it" );
return( true );
}
}
}
// availability and speed based stuff needs a little time before being applied
if ( !early_days ){
if ( min_availability > 0 ){
float availability = peer_manager.getDownload().getStats().getAvailability();
if ( availability < min_availability){
log( getName() + ": activating as availability is poor" );
return( true );
}
}
if ( min_speed > 0 ){
if ( peer_manager.getStats().getDownloadAverage() < min_speed ){
log( getName() + ": activating as speed is slow" );
return( true );
}
}
}
}catch( Throwable e ){
Debug.printStackTrace(e);
}
return( false );
}
protected boolean
readyToDeactivate(
PeerManager peer_manager,
Peer peer )
{
try{
// obvious stuff first
if ( valid_until > 0 && getSystemTime() > valid_until ){
return( true );
}
if ( peer_manager.getDownload().getState() == Download.ST_SEEDING ){
return( true );
}
// more interesting stuff
if ( transient_seed ){
return( false );
}
if ( min_availability > 0 ){
float availability = peer_manager.getDownload().getStats().getAvailability();
if ( availability >= min_availability + 1 ){
log( getName() + ": deactivating as availability is good" );
return( true );
}
}
if ( min_speed > 0 ){
long my_speed = peer.getStats().getDownloadAverage();
long overall_speed = peer_manager.getStats().getDownloadAverage();
if ( overall_speed - my_speed > 2 * min_speed ){
log( getName() + ": deactivating as speed is good" );
return( true );
}
}
}catch( Throwable e ){
Debug.printStackTrace(e);
}
return( false );
}
public boolean
checkActivation(
PeerManager peer_manager,
Peer peer )
{
long now = getSystemTime();
if ( peer_manager == current_manager ){
if ( peer_manager_change_time > now ){
peer_manager_change_time = now;
}
long time_since_started = now - peer_manager_change_time;
if ( peer_manager != null ){
if ( active ){
if ( now - peer_manager_change_time > 30000 && readyToDeactivate( peer_manager, peer )){
setActive( false );
}
}else{
if ( !isPermanentlyUnavailable()){
if ( readyToActivate( peer_manager, peer, time_since_started )){
setActive( true );
}
}
}
}
}else{
// if the peer manager's changed then we always go inactive for a period to wait for
// download status to stabilise a bit
peer_manager_change_time = now;
current_manager = peer_manager;
setActive( false );
}
return( active );
}
public void
deactivate(
String reason )
{
plugin.log( getName() + ": deactivating (" + reason + ")" );
checkActivation( null, null );
}
protected void
setActive(
boolean _active )
{
try{
requests_mon.enter();
active = _active;
status = active?"Active":"Idle";
}finally{
requests_mon.exit();
}
}
public boolean
isActive()
{
return( active );
}
protected void
processRequests()
{
try{
requests_mon.enter();
if ( request_thread != null ){
return;
}
request_thread = Thread.currentThread();
}finally{
requests_mon.exit();
}
while( true ){
try{
if ( !request_sem.reserve(30000)){
try{
requests_mon.enter();
if ( requests.size() == 0 ){
request_thread = null;
break;
}
}finally{
requests_mon.exit();
}
}else{
List selected_requests = new ArrayList();
PeerReadRequest cancelled_request = null;
try{
requests_mon.enter();
// get an advisory set to process together
int count = selectRequests( requests );
if ( count <= 0 || count > requests.size()){
Debug.out( "invalid count" );
count = 1;
}
for (int i=0;i<count;i++){
PeerReadRequest request = (PeerReadRequest)requests.remove(0);
if ( request.isCancelled()){
// if this is the first request then process it, otherwise leave
// for the next-round
if ( i == 0 ){
cancelled_request = request;
}else{
requests.add( 0, request );
}
break;
}else{
selected_requests.add( request );
if ( i > 0 ){
// we've only got the sem for the first request, catch up for subsequent
request_sem.reserve();
}
}
}
}finally{
requests_mon.exit();
}
if ( cancelled_request != null ){
informCancelled( cancelled_request );
}else{
processRequests( selected_requests );
}
}
}catch( Throwable e ){
e.printStackTrace();
}
}
}
/**
* Rate handling
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -