/* * File : PeerManagerImpl.java * Created : 28-Dec-2003 * 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.peers; /** * @author parg * */ import java.util.*; import org.gudy.azureus2.core3.disk.DiskManagerPiece; import org.gudy.azureus2.core3.disk.DiskManagerReadRequest; import org.gudy.azureus2.core3.peer.*; import org.gudy.azureus2.core3.util.AEMonitor; import org.gudy.azureus2.core3.util.Debug; import org.gudy.azureus2.plugins.disk.DiskManager; import org.gudy.azureus2.plugins.download.*; import org.gudy.azureus2.plugins.peers.*; import org.gudy.azureus2.plugins.torrent.Torrent; import org.gudy.azureus2.plugins.utils.PooledByteBuffer; import org.gudy.azureus2.pluginsimpl.local.disk.DiskManagerImpl; import org.gudy.azureus2.pluginsimpl.local.download.DownloadManagerImpl; import org.gudy.azureus2.pluginsimpl.local.utils.PooledByteBufferImpl; import com.aelitis.azureus.core.networkmanager.NetworkManager; public class PeerManagerImpl implements PeerManager { private static final String PEPEER_DATA_KEY = PeerManagerImpl.class.getName(); protected PEPeerManager manager; protected static AEMonitor pm_map_mon = new AEMonitor( "PeerManager:Map" ); public static PeerManagerImpl getPeerManager( PEPeerManager _manager ) { try{ pm_map_mon.enter(); PeerManagerImpl res = (PeerManagerImpl)_manager.getData( "PluginPeerManager" ); if ( res == null ){ res = new PeerManagerImpl( _manager ); _manager.setData( "PluginPeerManager", res ); } return( res ); }finally{ pm_map_mon.exit(); } } private Map foreign_map = new HashMap(); private Map listener_map = new HashMap(); protected AEMonitor this_mon = new AEMonitor( "PeerManager" ); private final DiskManagerPiece[] dm_pieces; private final PEPiece[] pe_pieces; private pieceFacade[] piece_facades; private boolean destroyed; protected PeerManagerImpl( PEPeerManager _manager ) { manager = _manager; dm_pieces = _manager.getDiskManager().getPieces(); pe_pieces = _manager.getPieces(); manager.addListener( new PEPeerManagerListener() { public void peerAdded( PEPeerManager manager, PEPeer peer ) { } public void peerRemoved( PEPeerManager manager, PEPeer peer ) { PeerImpl dele = getPeerForPEPeer( peer ); if ( dele != null ){ dele.closed(); } } public void destroyed() { synchronized( foreign_map ){ destroyed = true; Iterator it = foreign_map.values().iterator(); while( it.hasNext()){ try{ ((PeerForeignDelegate)it.next()).stop(); }catch( Throwable e ){ Debug.printStackTrace( e ); } } } } }); } public PEPeerManager getDelegate() { return( manager ); } public DiskManager getDiskManager() { return( new DiskManagerImpl( manager.getDiskManager())); } public PeerManagerStats getStats() { return(new PeerManagerStatsImpl( manager)); } public boolean isSeeding() { // this is the wrong thing to check for seeding.. return( manager.getDiskManager().getRemainingExcludingDND() == 0 ); //yuck } public boolean isSuperSeeding() { return( manager.isSuperSeedMode()); } public Download getDownload() throws DownloadException { return( DownloadManagerImpl.getDownloadStatic( manager.getDiskManager().getTorrent())); } public Piece[] getPieces() { if ( piece_facades == null ){ pieceFacade[] pf = new pieceFacade[manager.getDiskManager().getNbPieces()]; for (int i=0;i<pf.length;i++){ pf[i] = new pieceFacade(i); } piece_facades = pf; } return( piece_facades ); } public PeerStats createPeerStats( Peer peer ) { PEPeer delegate = mapForeignPeer( peer ); return( new PeerStatsImpl( this, peer, manager.createPeerStats( delegate ))); } public void requestComplete( PeerReadRequest request, PooledByteBuffer data, Peer sender) { manager.writeBlock( request.getPieceNumber(), request.getOffset(), ((PooledByteBufferImpl)data).getBuffer(), mapForeignPeer( sender ), false); PeerForeignDelegate delegate = lookupForeignPeer( sender ); if ( delegate != null ){ delegate.dataReceived(); } } public void requestCancelled( PeerReadRequest request, Peer sender ) { manager.requestCanceled((DiskManagerReadRequest)request ); } // these are foreign peers public void addPeer( Peer peer ) { // no private check here, we come through here for webseeds for example manager.addPeer(mapForeignPeer( peer )); } public void removePeer( Peer peer ) { manager.removePeer(mapForeignPeer( peer )); } protected void removePeer( Peer peer, String reason ) { manager.removePeer(mapForeignPeer( peer ), reason ); } public void addPeer( String ip_address, int tcp_port ) { checkIfPrivate(); manager.addPeer( ip_address, tcp_port, 0, NetworkManager.getCryptoRequired( NetworkManager.CRYPTO_OVERRIDE_NONE )); } public void addPeer( String ip_address, int tcp_port, boolean use_crypto ) { checkIfPrivate(); if ( pluginPeerSourceEnabled()){ manager.addPeer( ip_address, tcp_port, 0, use_crypto ); } } public void addPeer( String ip_address, int tcp_port, int udp_port, boolean use_crypto ) { checkIfPrivate(); manager.addPeer( ip_address, tcp_port, udp_port, use_crypto ); } protected boolean pluginPeerSourceEnabled() { if ( manager.isPeerSourceEnabled( PEPeerSource.PS_PLUGIN )){ return( true ); }else{ Debug.out( "Plugin peer source disabled for " + manager.getDisplayName()); return( false ); } } protected void checkIfPrivate() { Download dl; try{ dl = getDownload(); }catch( Throwable e ){ // if this didn't work then nothing much else will so just fall through return; } Torrent t = dl.getTorrent(); if ( t != null ){ if ( t.isPrivate()){ throw( new RuntimeException( "Torrent is private, peer addition not permitted" )); } } } public Peer[] getPeers() { List l = manager.getPeers(); Peer[] res= new Peer[l.size()]; // this is all a bit shagged as we should maintain the PEPeer -> Peer link rather // than continually creating new PeerImpls... for (int i=0;i<res.length;i++){ res[i] = getPeerForPEPeer((PEPeer)l.get(i)); } return( res ); } public Peer[] getPeers( String address ) { List l = manager.getPeers( address ); Peer[] res= new Peer[l.size()]; // this is all a bit shagged as we should maintain the PEPeer -> Peer link rather // than continually creating new PeerImpls... for (int i=0;i<res.length;i++){ res[i] = getPeerForPEPeer((PEPeer)l.get(i)); } return( res ); } public PeerDescriptor[] getPendingPeers( String address ) { return( manager.getPendingPeers( address )); } public long getTimeSinceConnectionEstablished( Peer peer ) { if ( peer instanceof PeerImpl ){ return(((PeerImpl)peer).getDelegate().getTimeSinceConnectionEstablished()); }else{ PeerForeignDelegate delegate = lookupForeignPeer( peer ); if ( delegate != null ){ return( delegate.getTimeSinceConnectionEstablished()); }else{ return( 0 ); } } } public PEPeer mapForeignPeer( Peer _foreign ) { if ( _foreign instanceof PeerImpl ){ return(((PeerImpl)_foreign).getDelegate()); } synchronized( foreign_map ){ PEPeer local = (PEPeer)foreign_map.get( _foreign ); if( local == null ){ if ( destroyed ){ Debug.out( "Peer added to destroyed peer manager" ); return( null ); } local = new PeerForeignDelegate( this, _foreign ); _foreign.setUserData( PeerManagerImpl.class, local ); foreign_map.put( _foreign, local ); } return( local ); } } protected PeerForeignDelegate lookupForeignPeer( Peer _foreign ) { return((PeerForeignDelegate)_foreign.getUserData( PeerManagerImpl.class )); } public List mapForeignPeers( Peer[] _foreigns ) { List res = new ArrayList(); for (int i=0;i<_foreigns.length;i++){ PEPeer local = mapForeignPeer( _foreigns[i]); // could already be there if torrent contains two identical seeds (for whatever reason) if ( !res.contains( local )){ res.add( local ); } } return( res ); } public static PeerImpl getPeerForPEPeer( PEPeer pe_peer ) { PeerImpl peer = (PeerImpl)pe_peer.getData( PEPEER_DATA_KEY ); if ( peer == null ){ peer = new PeerImpl( pe_peer ); pe_peer.setData( PEPEER_DATA_KEY, peer ); } return( peer ); } public int getUploadRateLimitBytesPerSecond() { return( manager.getUploadRateLimitBytesPerSecond()); } public int getDownloadRateLimitBytesPerSecond() { return( manager.getDownloadRateLimitBytesPerSecond()); } public void addListener( final PeerManagerListener l ) { try{ this_mon.enter(); final Map peer_map = new HashMap(); PEPeerManagerListener core_listener = new PEPeerManagerListener() { public void peerAdded( PEPeerManager manager, PEPeer peer ) { PeerImpl pi = getPeerForPEPeer( peer ); peer_map.put( peer, pi ); l.peerAdded( PeerManagerImpl.this, pi ); } public void peerRemoved( PEPeerManager manager, PEPeer peer ) { PeerImpl pi = (PeerImpl)peer_map.remove( peer ); if ( pi == null ){ // somewhat inconsistently we get told here about the removal of // peers that never connected (and weren't added) // Debug.out( "PeerManager: peer not found"); } else{ l.peerRemoved( PeerManagerImpl.this, pi ); } } public void destroyed() { } }; listener_map.put( l, core_listener ); manager.addListener( core_listener ); }finally{ this_mon.exit(); } } public void removeListener( PeerManagerListener l ) { try{ this_mon.enter(); PEPeerManagerListener core_listener = (PEPeerManagerListener)listener_map.remove( l ); if ( core_listener != null ){ manager.removeListener( core_listener ); } }finally{ this_mon.exit(); } } protected class pieceFacade implements Piece { private final int index; protected pieceFacade( int _index ) { index = _index; } public boolean isDone() { return( dm_pieces[index].isDone()); } public boolean isNeeded() { return( dm_pieces[index].isNeeded()); } public boolean isDownloading() { return( pe_pieces[index] != null ); } public boolean isFullyAllocatable() { if ( pe_pieces[index] != null ){ return( false ); } return( dm_pieces[index].isInteresting()); } public int getAllocatableRequestCount() { PEPiece pe_piece = pe_pieces[index]; if ( pe_piece != null ){ return( pe_piece.getNbUnrequested()); } if ( dm_pieces[index].isInteresting() ){ return( dm_pieces[index].getNbBlocks()); } return( 0 ); } } }