package org.torrent.internal.transfer; import java.util.Collection; import java.util.LinkedHashSet; import java.util.concurrent.CopyOnWriteArrayList; import java.util.logging.Logger; import org.merapi.helper.handlers.BarUpdateRequestHandler; import org.merapi.helper.messages.BarUpdateRespondMessage; import org.torrent.internal.data.BTPart; import org.torrent.internal.event.DownloadEvent; import org.torrent.internal.event.DownloadEventListener; import org.torrent.internal.peer.connection.BTConnection; import org.torrent.internal.protocol.message.BTMessageVisitorAdapter; import org.torrent.internal.protocol.message.BitField; import org.torrent.internal.protocol.message.BittorrentMessage; import org.torrent.internal.protocol.message.Choke; import org.torrent.internal.protocol.message.Have; import org.torrent.internal.protocol.message.Piece; import org.torrent.internal.protocol.message.Request; import org.torrent.internal.protocol.message.UnChoke; import org.torrent.internal.service.ContentStateListener; import org.torrent.internal.service.event.ContentStateEvent; import org.torrent.internal.util.BTUtil; import org.torrent.internal.util.Time; import org.torrent.internal.util.Validator; public class Download extends TrafficWatcher implements BTDownload { //BTSessionListenerAdapter { private static final Logger LOG = Logger .getLogger(Download.class.getName()); private final BTSession session; private RequestProvider reqProv; private Collection<org.torrent.internal.data.TorrentMetaInfo.Piece> tasks = new LinkedHashSet<org.torrent.internal.data.TorrentMetaInfo.Piece>(); private Collection<DownloadEventListener> listeners = new CopyOnWriteArrayList<DownloadEventListener>(); public Download(BTSession session, RequestProvider requestProvider, ContentWatcher contentWatcher) { Validator.nonNull(session, requestProvider); this.session = session; reqProv = requestProvider; contentWatcher.addContentStateListener(new ContentStateListener() { @Override public void requiresPiece(ContentStateEvent evt) { update(); } @Override public void verifiedPiece(ContentStateEvent evt) { update(); //out to test message overflow // BarUpdateRequestHandler.sendUpdateBarData(BarUpdateRespondMessage.UPDATE_BAR_DATA, evt.getPiece().getContentInfo().getInfoHash().asHexString(), evt.getPiece().getIndex(), 1); } @Override public void receivedPiece(ContentStateEvent evt) { synchronized (tasks) { if (tasks.contains(evt.getPiece())) { firePieceDownloaded(evt.getPiece()); } } // BarUpdateRequestHandler.getInstance().sendUpdateBarData(BarUpdateRespondMessage.UPDATE_BAR_DATA, evt.getPiece().getContentInfo().getInfoHash().asHexString(), evt.getPiece().getIndex(), 1); } @Override public void stateChanged(ContentStateEvent evt) { // TODO Auto-generated method stub // BarUpdateRequestHandler.getInstance().sendUpdateBarData(BarUpdateRespondMessage.UPDATE_BAR_DATA, evt.getPiece().getContentInfo().getInfoHash().asHexString(), evt.getPiece().getIndex(), 1); } }); } @Override public void addedConnection(BTConnection con) { Validator.notNull(con, "Connection is null!"); update(con); } private void update(BTConnection con) { synchronized (con) { if (!con.isConnectionEstablished()) { return; } Time t = new Time(); if (!reqProv.couldAllocateRequest(this, con)) { if (con.getMyPeerStatus().isInterested() && con.getPendingRequests().isEmpty()) { con.sendInterested(false); } } else { if (con.getRemotePeerStatus().isChoking()) { if (!con.getMyPeerStatus().isInterested()) { con.sendInterested(true); } } else { assert reqProv.couldAllocateRequest(this, con); BTPart request = reqProv.allocateRequest(this, con); assert request != null : reqProv.couldAllocateRequest(this, con); LOG.finest("Got request " + request + " for " + con); do { con.send(new Request(request), null); } while ((request = reqProv.allocateRequest(this, con)) != null); if (t.delta() > 100) { LOG.warning("Allocation and sending took " + t.delta() + "ms for " + this); } } } } } @Override public void removedConnection(BTConnection con) { reqProv.cancelAllRequests(this, con); update(); } private void update() { for (BTConnection c : session.getConnections()) { update(c); } } @Override public void receivedBTMessage(final BTConnection from, BittorrentMessage message) { LOG.finest("Received " + message + " from " + from); message.accept(new BTMessageVisitorAdapter() { @Override public void visitHave(Have have) { update(from); } @Override public void visitBitField(BitField bitField) { LOG.finest("Received Bitfield: " + bitField); update(from); } @Override public void visitUnChoke(UnChoke unChoke) { update(from); } @Override public void visitChoke(Choke choke) { reqProv.cancelAllRequests(Download.this, from); from.purgePendingRequests(); update(); } @Override public void visitPiece(Piece piece) { update(from); } }); } @Override public void connectionEstablished(BTConnection connection) { update(connection); } @Override public void performDownload( org.torrent.internal.data.TorrentMetaInfo.Piece piece) { synchronized (tasks) { if (!tasks.contains(piece)) { tasks.add(piece); // BarUpdateRequestHandler.getInstance().sendUpdateBarData(BarUpdateRespondMessage.UPDATE_BAR_DATA, piece.getContentInfo().getInfoHash().asHexString(), piece.getIndex(), 1); fireDownloadAdded(piece); } } } private void fireDownloadAdded( final org.torrent.internal.data.TorrentMetaInfo.Piece piece) { BTUtil.invokeLater(new Runnable() { @Override public void run() { DownloadEvent evt = new DownloadEvent(Download.this, piece); for (DownloadEventListener l : listeners) { l.downloadAdded(evt); } // BarUpdateRequestHandler.getInstance().sendUpdateBarData(BarUpdateRespondMessage.UPDATE_BAR_DATA, piece.getContentInfo().getInfoHash().asHexString(), piece.getIndex(), 1); // } }); } @Override public void abortDownload( org.torrent.internal.data.TorrentMetaInfo.Piece piece) { synchronized (tasks) { if (tasks.remove(piece)) { fireDownloadAborted(piece); } } } private void firePieceDownloaded( final org.torrent.internal.data.TorrentMetaInfo.Piece piece) { BTUtil.invokeLater(new Runnable() { @Override public void run() { DownloadEvent evt = new DownloadEvent(Download.this, piece); for (DownloadEventListener l : listeners) { l.downloadCompleted(evt); // BarUpdateRequestHandler.getInstance().sendUpdateBarData(BarUpdateRespondMessage.UPDATE_BAR_DATA, piece.getContentInfo().getInfoHash().asHexString(), piece.getIndex(), 1); } } }); } private void fireDownloadAborted( final org.torrent.internal.data.TorrentMetaInfo.Piece piece) { BTUtil.invokeLater(new Runnable() { @Override public void run() { DownloadEvent evt = new DownloadEvent(Download.this, piece); for (DownloadEventListener l : listeners) { l.downloadAborted(evt); } } }); } }