package edu.washington.cs.oneswarm.f2f.multisource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.gudy.azureus2.core3.disk.DiskManagerFileInfo;
import org.gudy.azureus2.core3.disk.DiskManagerPiece;
import org.gudy.azureus2.core3.disk.DiskManagerReadRequest;
import org.gudy.azureus2.core3.disk.DiskManagerReadRequestListener;
import org.gudy.azureus2.core3.download.DownloadManager;
import org.gudy.azureus2.core3.download.DownloadManagerListener;
import org.gudy.azureus2.core3.download.DownloadManagerPieceListener;
import org.gudy.azureus2.core3.peer.PEPeer;
import org.gudy.azureus2.core3.peer.PEPeerListener;
import org.gudy.azureus2.core3.peer.PEPeerManager;
import org.gudy.azureus2.core3.peer.PEPeerStats;
import org.gudy.azureus2.core3.peer.PEPiece;
import org.gudy.azureus2.core3.peer.impl.PEPeerControl;
import org.gudy.azureus2.core3.peer.impl.PEPeerTransport;
import org.gudy.azureus2.core3.util.AEMonitor;
import org.gudy.azureus2.core3.util.Average;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.DirectByteBuffer;
import org.gudy.azureus2.core3.util.IndentWriter;
import org.gudy.azureus2.core3.util.SystemTime;
import org.gudy.azureus2.plugins.network.Connection;
import com.aelitis.azureus.core.networkmanager.LimitedRateGroup;
import com.aelitis.azureus.core.peermanager.messaging.Message;
import com.aelitis.azureus.core.peermanager.peerdb.PeerItem;
import com.aelitis.azureus.core.peermanager.peerdb.PeerItemFactory;
import com.aelitis.azureus.core.peermanager.piecepicker.impl.PiecePickerImpl;
import com.aelitis.azureus.core.peermanager.piecepicker.util.BitFlags;
import edu.washington.cs.oneswarm.f2f.multisource.Sha1PieceRequestTranslator.PieceTranslationExcetion;
import edu.washington.cs.oneswarm.f2f.share.DownloadManagerStarter;
import edu.washington.cs.oneswarm.f2f.share.DownloadManagerStarter.DownloadManagerStartListener;
public class Sha1Peer
implements PEPeerTransport
{
private final static Logger logger = Logger.getLogger(Sha1Peer.class.getName());
private volatile int _lastPiece = -1; // last piece that was requested from
private boolean availabilityAdded = false;
private int consecutive_no_request_count;
// this peer (mostly to try to
// request from same one)
private long create_time = SystemTime.getCurrentTime();
// private Map data;
private int current_peer_state = PEPeer.CONNECTING;
private int current_connection_state = PEPeerTransport.CONNECTION_CONNECTING;
private Map<String, Object> data;
private final DownloadManager destinationDownloadManager;
private final AEMonitor general_mon = new AEMonitor(
"SHA1peer:data");
private final PEPeerControl manager;
private HashSet<PEPeerListener> peer_listeners = new HashSet<PEPeerListener>();
private List<PEPeerListener> peer_listeners_cow;
private final AEMonitor peer_listeners_mon = new AEMonitor(
"SHA1peer:PL");
private PEPeerStats peer_stats;
private int[] priorityOffsets = null;
private final ArrayList<DiskManagerReadRequest> requested = new ArrayList<DiskManagerReadRequest>();
private final AEMonitor requested_mon = new AEMonitor(
"SHA1peer:Req");
private Sha1PieceRequestTranslator requestTranslator;
private int reservedPiece = -1;
private final Sha1DownloadManager sha1DownloadManager;
private final DownloadManager sourceDownloadManager;
private boolean started = false;
private SourcePieceListener sourcePieceListener = new SourcePieceListener();
private boolean closed = false;
private BitFlags available = null;
private boolean interesting = false;
private long lastAvailableUpdate = 0;
private long MAX_MS_BETWEEN_AVAILABLE_UPDATE = 5 * 1000;
private Average sha1DownloadSpeedAverage;
private void updateAvailable() throws PieceTranslationExcetion {
lastAvailableUpdate = System.currentTimeMillis();
if (requestTranslator == null || current_peer_state != TRANSFERING) {
available = new BitFlags(
new boolean[destinationDownloadManager.getTorrent().getNumberOfPieces()]);
interesting = false;
return;
}
available = requestTranslator.getAvailable();
DiskManagerPiece[] pieces = destinationDownloadManager.getDiskManager().getPieces();
boolean intr = false;
for (int i = 0; i < pieces.length; i++) {
if (available.flags[i]
&& destinationDownloadManager.getDiskManager().isInteresting(i)) {
intr = true;
break;
}
}
interesting = intr;
}
public Sha1Peer(Sha1DownloadManager sha1DownloadManager,
DownloadManager sourceDownloadManager,
DownloadManager destinationDownloadManager)
throws PieceTranslationExcetion {
this.sha1DownloadManager = sha1DownloadManager;
this.sourceDownloadManager = sourceDownloadManager;
this.destinationDownloadManager = destinationDownloadManager;
this.manager = (PEPeerControl) destinationDownloadManager.getPeerManager();
this.updateAvailable();
sourceDownloadManager.addListener(new DownloadManagerListener() {
public void stateChanged(DownloadManager manager, int state) {
if (!closed) {
if (manager.getState() != DownloadManager.STATE_DOWNLOADING
&& manager.getState() != DownloadManager.STATE_SEEDING) {
closeConnection("source download manager not in downloading or seeding state");
Sha1Peer.this.sourceDownloadManager.removeListener(this);
}
}
}
public void positionChanged(DownloadManager download, int oldPosition,
int newPosition) {
}
public void filePriorityChanged(DownloadManager download,
DiskManagerFileInfo file) {
// TODO Auto-generated method stub
}
public void downloadComplete(DownloadManager manager) {
}
public void completionChanged(DownloadManager manager, boolean bCompleted) {
}
});
if (this.destinationDownloadManager.getData("sha1_rate") == null) {
this.sha1DownloadSpeedAverage = Average.getInstance(1000, 60);
this.destinationDownloadManager.setData("sha1_rate",
sha1DownloadSpeedAverage);
} else {
this.sha1DownloadSpeedAverage = (Average) this.destinationDownloadManager.getData("sha1_rate");
}
}
public DownloadManager getSourceDownloadManager() {
return sourceDownloadManager;
}
public DownloadManager getDestinationDownloadManager() {
return destinationDownloadManager;
}
private void addAvailability() {
if (!availabilityAdded && current_peer_state == PEPeerTransport.TRANSFERING) {
final List<PEPeerListener> peer_listeners_ref = peer_listeners_cow;
if (peer_listeners_ref != null) {
for (int i = 0; i < peer_listeners_ref.size(); i++) {
final PEPeerListener peerListener = peer_listeners_ref.get(i);
peerListener.addAvailability(this, getAvailable());
}
availabilityAdded = true;
}
}
}
public void addListener(final PEPeerListener listener) {
try {
peer_listeners_mon.enter();
if (peer_listeners_cow == null) {
peer_listeners_cow = new ArrayList<PEPeerListener>();
}
final List<PEPeerListener> new_listeners = new ArrayList<PEPeerListener>(
peer_listeners_cow);
new_listeners.add(listener);
peer_listeners_cow = new_listeners;
} finally {
peer_listeners_mon.exit();
}
}
public void addRateLimiter(LimitedRateGroup limiter, boolean upload) {
}
/**
* Nothing to do if called
*/
public void checkInterested() {
}
public void clearRequestHint() {
}
public void closeConnection(String reason) {
logger.fine("peer closed: " + reason);
if (!closed) {
closed = true;
removeAvailability();
stateChanged(DISCONNECTED, PEPeerTransport.DISCONNECTED);
sha1DownloadManager.peerClosed(this);
manager.peerConnectionClosed(this, false, false);
sourceDownloadManager.removePieceListener(sourcePieceListener);
}
}
public void doKeepAliveCheck() {
}
public void doPerformanceTuningCheck() {
}
// take this opportunity to refresh the disk pieces
long lastPieceRefresh = System.currentTimeMillis();
public boolean doTimeoutChecks() {
return false;
}
private void downloadManagerStarted() {
stateChanged(PEPeer.TRANSFERING,
PEPeerTransport.CONNECTION_FULLY_ESTABLISHED);
try {
peer_stats = manager.createPeerStats(this);
requestTranslator = new Sha1PieceRequestTranslator(this,
sourceDownloadManager, destinationDownloadManager);
updateAvailable();
sourceDownloadManager.addPieceListener(sourcePieceListener);
addAvailability();
} catch (Exception e) {
e.printStackTrace();
closeConnection("got exception when creating piece translator");
}
}
public boolean equals(Object o) {
if (!(o instanceof Sha1Peer)) {
return false;
}
Sha1Peer s = (Sha1Peer) o;
return s.sourceDownloadManager.equals(sourceDownloadManager)
&& s.destinationDownloadManager.equals(destinationDownloadManager);
}
public void generateEvidence(IndentWriter writer) {
writer.println("sha1peer: state=" + current_peer_state);
}
public BitFlags getAvailable() {
if (!closed
&& System.currentTimeMillis() - lastAvailableUpdate > MAX_MS_BETWEEN_AVAILABLE_UPDATE) {
try {
updateAvailable();
} catch (PieceTranslationExcetion e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return available;
}
public long getBytesRemaining() {
long torrentSize = destinationDownloadManager.getSize();
if (current_peer_state != TRANSFERING || closed) {
return torrentSize;
}
long downloaded = 0;
DiskManagerPiece[] pieces = destinationDownloadManager.getDiskManager().getPieces();
for (int i = 0; i < available.flags.length; i++) {
if (available.flags[i]) {
downloaded += pieces[i].getLength();
}
}
return torrentSize - downloaded;
}
public String getClient() {
return ("OneSwarm SHA1 virtual peer");
}
public String getClientNameFromExtensionHandshake() {
return null;
}
public String getClientNameFromPeerID() {
return null;
}
public int getConnectionState() {
return current_connection_state;
}
public int getConsecutiveNoRequestCount() {
return (consecutive_no_request_count);
}
public PEPeerControl getControl() {
return manager;
}
/** To retreive arbitrary objects against a peer. */
public Object getData(String key) {
if (data == null)
return null;
return data.get(key);
}
public int getDownloadRateLimitBytesPerSecond() {
// 1GB/s should be enough...
return 1024 * 1024 * 1024;
}
public String getEncryption() {
return ("Machine-local-only");
}
public List<DiskManagerReadRequest> getExpiredRequests() {
List<DiskManagerReadRequest> result = null;
// this is frequently called, hence we operate without a monitor and
// take the hit of possible exceptions due to concurrent list
// modification (only out-of-bounds can occur)
try {
for (int i = requested.size() - 1; i >= 0; i--) {
final DiskManagerReadRequest request = requested.get(i);
if (request.isExpired()) {
if (result == null) {
result = new ArrayList<DiskManagerReadRequest>();
}
result.add(request);
}
}
return (result);
} catch (Throwable e) {
return result;
}
}
public byte[] getHandshakeReservedBytes() {
return new byte[8];
}
public byte[] getId() {
byte[] id = new byte[20];
id[0] = (byte) 83;
id[1] = (byte) 72;
id[2] = (byte) 65;
id[3] = (byte) 49;
return id;
}
public int getIncomingRequestCount() {
return (0);
}
public int[] getIncomingRequestedPieceNumbers() {
return (new int[0]);
}
// PEPeer stuff
public String getIp() {
return "local: " + sourceDownloadManager.getDisplayName() + "->"
+ destinationDownloadManager.getDisplayName();
}
public String getIPHostName() {
return "local: " + sourceDownloadManager.getDisplayName() + "->"
+ destinationDownloadManager.getDisplayName();
}
public int getLastPiece() {
return _lastPiece;
}
public PEPeerManager getManager() {
return manager;
}
public int getMaxNbRequests() {
// each request is 16K
// requests are allocated 10x/sec
// allow up to 160MB of outstanding requests
// that is 10,000 block
return 10 * 1000;
}
public int getMessagingMode() {
return PEPeer.MESSAGING_BT_ONLY;
}
public int getNbRequests() {
return requested.size();
}
public int getOutboundDataQueueSize() {
return 0;
}
public int getOutgoingRequestCount() {
return getNbRequests();
}
public int[] getOutgoingRequestedPieceNumbers() {
try {
requested_mon.enter();
/**
* Cheap hack to reduce (but not remove all) the # of duplicate
* entries
*/
int iLastNumber = -1;
// allocate max size needed (we'll shrink it later)
final int[] pieceNumbers = new int[requested.size()];
int pos = 0;
for (int i = 0; i < requested.size(); i++) {
DiskManagerReadRequest request = null;
try {
request = requested.get(i);
} catch (Exception e) {
Debug.printStackTrace(e);
}
if (request != null && iLastNumber != request.getPieceNumber()) {
iLastNumber = request.getPieceNumber();
pieceNumbers[pos++] = iLastNumber;
}
}
final int[] trimmed = new int[pos];
System.arraycopy(pieceNumbers, 0, trimmed, 0, pos);
return trimmed;
} finally {
requested_mon.exit();
}
}
public PeerItem getPeerItemIdentity() {
return PeerItemFactory.createPeerItem(getIp(), getPort(),
PeerItemFactory.PEER_SOURCE_INCOMING,
PeerItemFactory.HANDSHAKE_TYPE_PLAIN, getUDPListenPort(),
PeerItemFactory.CRYPTO_LEVEL_1, 0);
}
public String getPeerSource() {
return "SHA1: " + sourceDownloadManager.getDisplayName();
}
public int getPeerState() {
return current_peer_state;
}
public int getPercentDoneInThousandNotation() {
long total = destinationDownloadManager.getSize();
return (int) ((1000 * (total - getBytesRemaining())) / total);
}
public int getPercentDoneOfCurrentIncomingRequest() {
return 0;
}
public int getPercentDoneOfCurrentOutgoingRequest() {
return 0;
}
public Connection getPluginConnection() {
return null;
}
public int getPort() {
return 0;
}
public int[] getPriorityOffsets() {
if (priorityOffsets == null) {
priorityOffsets = getPriorityOffsetsArray();
}
return priorityOffsets;
}
private int[] getPriorityOffsetsArray() {
// we want it to read in order to make the disk seeks less rough
int totalPieceNum = destinationDownloadManager.getNbPieces();
int[] piecePrio = new int[totalPieceNum];
for (int i = 0; i < piecePrio.length; i++) {
piecePrio[i] = (int) Math.round(PiecePickerImpl.PRIORITY_IN_ORDER_FILES
- (i * (PiecePickerImpl.PRIORITY_IN_ORDER_FILES / totalPieceNum)));
}
return piecePrio;
}
public int[] getRequestHint() {
return null;
}
public int getRequestIndex(DiskManagerReadRequest request) {
return (requested.indexOf(request));
}
public int getReservedPieceNumber() {
return (reservedPiece);
}
public long getSnubbedTime() {
return 0;
}
public PEPeerStats getStats() {
return peer_stats;
}
public Message[] getSupportedMessages() {
return null;
}
public int getTCPListenPort() {
return 0;
}
public long getTimeSinceConnectionEstablished() {
long now = SystemTime.getCurrentTime();
if (now > create_time) {
return (now - create_time);
}
return (0);
}
public long getTimeSinceGoodDataReceived() {
return (0);
}
public long getTimeSinceLastDataMessageReceived() {
return 0;
}
public long getTimeSinceLastDataMessageSent() {
return 0;
}
public int getUDPListenPort() {
return 0;
}
public int getUDPNonDataListenPort() {
return 0;
}
public int getUniqueAnnounce() {
return -1;
}
public int getUploadHint() {
return 0;
}
public int getUploadRateLimitBytesPerSecond() {
return 0;
}
public int hashCode() {
return (2 * sourceDownloadManager.hashCode())
^ destinationDownloadManager.hashCode();
}
public boolean hasReceivedBitField() {
return requestTranslator != null;
}
/**
* Apaprently nothing significant to do if called
*/
public boolean isAvailabilityAdded() {
return availabilityAdded;
}
public boolean isChokedByMe() {
return true;
}
public boolean isChokingMe() {
return false;
}
public boolean isDownloadPossible() {
return this.isInteresting();
}
public boolean isIncoming() {
return true;
}
public boolean isInterested() {
return false;
}
public boolean isInteresting() {
if (closed) {
return false;
}
return interesting;
}
public boolean isLANLocal() {
return true;
}
public boolean isOptimisticUnchoke() {
return false;
}
public boolean isPieceAvailable(int pieceNumber) {
if (closed) {
return false;
}
return available.flags[pieceNumber];
}
public boolean isSafeForReconnect() {
return false;
}
public boolean isSeed() {
if (closed) {
return false;
}
return available.nbSet == destinationDownloadManager.getNbPieces();
}
public boolean isSnubbed() {
return false;
}
public boolean isStalledPendingLoad() {
return (false);
}
public boolean isTCP() {
return (true);
}
public PEPeerTransport reconnect(boolean tryUDP) {
return null;
}
private void removeAvailability() {
if (availabilityAdded && requestTranslator != null) {
final List<PEPeerListener> peer_listeners_ref = peer_listeners_cow;
if (peer_listeners_ref != null) {
for (int i = 0; i < peer_listeners_ref.size(); i++) {
final PEPeerListener peerListener = peer_listeners_ref.get(i);
peerListener.removeAvailability(this, getAvailable());
}
}
availabilityAdded = false;
}
requestTranslator = null;
}
public void removeListener(PEPeerListener listener) {
try {
peer_listeners_mon.enter();
if (peer_listeners_cow != null) {
List<PEPeerListener> new_listeners = new ArrayList<PEPeerListener>(
peer_listeners_cow);
new_listeners.remove(listener);
if (new_listeners.isEmpty()) {
new_listeners = null;
}
peer_listeners_cow = new_listeners;
}
} finally {
peer_listeners_mon.exit();
}
}
public void removeRateLimiter(LimitedRateGroup limiter, boolean upload) {
}
private void removeRequest(DiskManagerReadRequest request) {
try {
requested_mon.enter();
requested.remove(request);
} finally {
requested_mon.exit();
}
}
/**
*
* @param pieceNumber
* @param pieceOffset
* @param pieceLength
* @return true is the piece is really requested
*/
long lastRequest = System.currentTimeMillis();
public DiskManagerReadRequest request(final int pieceNumber,
final int pieceOffset, final int pieceLength) {
DiskManagerReadRequest request = manager.createDiskManagerRequest(
pieceNumber, pieceOffset, pieceLength);
if (current_peer_state != TRANSFERING || requestTranslator == null) {
manager.requestCanceled(request);
return null;
}
boolean added = false;
try {
requested_mon.enter();
if (!requested.contains(request)) {
requested.add(request);
added = true;
}
} finally {
requested_mon.exit();
}
if (added) {
_lastPiece = pieceNumber;
final long requestStartTime = System.currentTimeMillis();
requestTranslator.request(request, new DiskManagerReadRequestListener() {
public int getPriority() {
return 0;
}
public void readCompleted(DiskManagerReadRequest _request,
DirectByteBuffer data) {
removeRequest(_request);
long time = System.currentTimeMillis() - requestStartTime;
logger.finest("completed request: piece=" + pieceNumber + " time="
+ time + " offset=" + pieceOffset + " len=" + pieceLength);
manager.writeBlock(pieceNumber, pieceOffset, data, Sha1Peer.this,
false);
int len = _request.getLength();
peer_stats.dataBytesReceived(len);
sha1DownloadSpeedAverage.addValue(len);
// manager.dataBytesReceived(Sha1Peer.this, len);
}
public void readFailed(DiskManagerReadRequest request, Throwable cause) {
closeConnection("read failed: " + cause.getMessage());
}
public void requestExecuted(long bytes) {
}
});
return request;
} else {
return null;
}
}
public void requestAllocationComplete() {
}
public boolean requestAllocationStarts(int[] base_priorities) {
return false;
}
public void sendBadPiece(int piece_number) {
}
public void sendCancel(DiskManagerReadRequest request) {
}
/**
* Should never be called
*/
public void sendChoke() {
}
/**
* Nothing to do if called
*/
public void sendHave(int piece) {
}
public boolean sendRequestHint(int piece_number, int offset, int length,
int life) {
return (false);
}
/**
* Should never be called
*/
public void sendUnChoke() {
}
public void setConsecutiveNoRequestCount(int num) {
consecutive_no_request_count = num;
}
/** To store arbitrary objects against a peer. */
public void setData(String key, Object value) {
try {
general_mon.enter();
if (data == null) {
data = new HashMap<String, Object>();
}
if (value == null) {
if (data.containsKey(key))
data.remove(key);
} else {
data.put(key, value);
}
} finally {
general_mon.exit();
}
}
public void setDownloadRateLimitBytesPerSecond(int bytes) {
};
public void setHaveAggregationEnabled(boolean enabled) {
}
public void setLastPiece(int pieceNumber) {
_lastPiece = pieceNumber;
}
public void setOptimisticUnchoke(boolean is_optimistic) {
}
public void setReservedPieceNumber(int pieceNumber) {
reservedPiece = pieceNumber;
}
public void setSnubbed(boolean b) {
}
public void setUniqueAnnounce(int uniquePieceNumber) {
}
public void setUploadHint(int timeToSpread) {
}
public void setUploadRateLimitBytesPerSecond(int bytes) {
}
public void start() {
logger.fine("peer started");
if (!started) {
started = true;
DownloadManagerStarter.startDownload(destinationDownloadManager,
new DownloadManagerStartListener() {
public void downloadStarted() {
DownloadManagerStarter.startDownload(sourceDownloadManager,
new DownloadManagerStartListener() {
public void downloadStarted() {
downloadManagerStarted();
}
});
}
});
}
}
private void stateChanged(int peer_state, int connection_state) {
this.current_peer_state = peer_state;
this.current_connection_state = connection_state;
for (PEPeerListener l : peer_listeners) {
l.stateChanged(this, current_peer_state);
}
}
protected void stop() {
// do nothing
}
public boolean supportsMessaging() {
return false;
}
public boolean transferAvailable() {
return (this.current_peer_state == TRANSFERING);
}
public void updatePeerExchange() {
}
private class SourcePieceListener
implements DownloadManagerPieceListener
{
public void pieceAdded(PEPiece piece) {
try {
if (getPeerState() == TRANSFERING) {
logger.finest("source piece added: " + piece.getPieceNumber());
BitFlags newAvailable = requestTranslator.getAvailable();
for (int i = 0; i < newAvailable.flags.length; i++) {
if (newAvailable.flags[i] == true && available.flags[i] == false) {
updateAvailable();
logger.finest("notifying manager of new piece: " + i);
int pieceSize = destinationDownloadManager.getDiskManager().getPiece(
i).getLength();
manager.havePiece(i, pieceSize, Sha1Peer.this);
peer_stats.hasNewPiece(pieceSize);
}
}
}
} catch (PieceTranslationExcetion e) {
closeConnection("problem when updating have fields: " + e.getMessage());
}
}
public void pieceRemoved(PEPiece piece) {
}
}
}