package com.limegroup.bittorrent; import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.concurrent.atomic.AtomicBoolean; import org.limewire.bittorrent.Torrent; import org.limewire.bittorrent.TorrentEvent; import org.limewire.bittorrent.TorrentManager; import org.limewire.bittorrent.TorrentState; import org.limewire.bittorrent.TorrentStatus; import org.limewire.concurrent.ManagedThread; import org.limewire.listener.EventListener; import com.limegroup.gnutella.ActivityCallback; import com.limegroup.gnutella.InsufficientDataException; import com.limegroup.gnutella.URN; import com.limegroup.gnutella.Uploader; import com.limegroup.gnutella.library.FileDesc; import com.limegroup.gnutella.uploader.UploadType; /** * Wraps the Torrent class in the Uplaoder interface to enable the gui to treat * the torrent uploader as a normal uploader. */ public class BTUploader implements Uploader, EventListener<TorrentEvent> { private final ActivityCallback activityCallback; private final Torrent torrent; private final AtomicBoolean cancelled = new AtomicBoolean(false); private final AtomicBoolean finished = new AtomicBoolean(false); private volatile URN urn = null; private final TorrentUploadManager torrentUploadManager; private final TorrentManager torrentManager; public BTUploader(Torrent torrent, ActivityCallback activityCallback, TorrentUploadManager torrentUploadManager, TorrentManager torrentManager) { this.torrent = torrent; this.activityCallback = activityCallback; this.torrentUploadManager = torrentUploadManager; this.torrentManager = torrentManager; } public void registerTorrentListener() { torrent.addListener(this); } @Override public void handleEvent(TorrentEvent event) { if (event == TorrentEvent.STOPPED) { if (!finished.get()) { cancel(); } else { remove(); } } else if (event == TorrentEvent.STATUS_CHANGED) { // considered to be finished uploading if seed ratio has been // reached boolean finished = torrent.isFinished(); float seedRatio = torrent.getSeedRatio(); TorrentStatus status = torrent.getStatus(); int seedTime = status != null ? status.getSeedingTime() : 0; float targetSeedRatio = torrentManager.getTorrentManagerSettings().getSeedRatioLimit(); int targetSeedTime = torrentManager.getTorrentManagerSettings().getSeedTimeLimit(); if (finished && (seedRatio >= targetSeedRatio || seedTime >= targetSeedTime)) { this.finished.set(true); torrent.stop(); } } } private void cancel() { cancelled.set(true); remove(); } private void remove() { torrent.removeListener(this); torrentUploadManager.removeMemento(torrent); activityCallback.removeUpload(this); }; @Override public void stop() { // TODO refactor to prompt from the gui if (activityCallback.promptTorrentUploadCancel(torrent)) { new ManagedThread(new Runnable() { @Override public void run() { torrent.stop(); } }, "BTUploader Stop Torrent").start(); cancel(); } } @Override public String getFileName() { return torrent.getName(); } @Override public long getFileSize() { TorrentStatus status = torrent.getStatus(); long fileSize = status != null ? status.getTotalWanted() : -1; return fileSize; } @Override public FileDesc getFileDesc() { return null; } @Override public int getIndex() { // negative will make sure it never conflicts with regular uploads return 0 - Math.abs(hashCode()); } @Override public long amountUploaded() { return torrent.getTotalUploaded(); } @Override public long getTotalAmountUploaded() { return torrent.getTotalUploaded(); } @Override public String getHost() { return BITTORRENT_UPLOAD; } @Override public UploadStatus getState() { if (cancelled.get()) { return UploadStatus.CANCELLED; } if (finished.get()) { return UploadStatus.COMPLETE; } TorrentStatus status = torrent.getStatus(); if (status == null) { return UploadStatus.CONNECTING; } if (torrent.isPaused() || (torrent.isFinished() && torrent.getNumUploads() == 0)) { return UploadStatus.QUEUED; } if (status.isError()) { // TODO add retry // TODO custom error state return UploadStatus.UNAVAILABLE_RANGE; } if (status.isPaused()) { return UploadStatus.UPLOADING; } else { TorrentState state = status.getState(); switch (state) { case DOWNLOADING: case FINISHED: case SEEDING: return UploadStatus.UPLOADING; case QUEUED_FOR_CHECKING: case CHECKING_FILES: case DOWNLOADING_METADATA: case ALLOCATING: return UploadStatus.CONNECTING; default: throw new UnsupportedOperationException("Unknown state: " + state); } } } @Override public UploadStatus getLastTransferState() { return getState(); } @Override public boolean isBrowseHostEnabled() { return false; } @Override public int getGnutellaPort() { return 0; } @Override public String getUserAgent() { return BITTORRENT_UPLOAD; } @Override public int getQueuePosition() { return 0; } @Override public boolean isInactive() { if (torrent.getStatus().isPaused() || torrent.getStatus().isFinished()) { return true; } return false; } @Override public void measureBandwidth() { // uneeded using libtorrent rate } @Override public float getMeasuredBandwidth() throws InsufficientDataException { return (torrent.getUploadRate() / 1024); } @Override public float getAverageBandwidth() { // Unused return (torrent.getUploadRate() / 1024); } @Override public String getCustomIconDescriptor() { if (torrent.isSingleFileTorrent()) { return null; } return BITTORRENT_UPLOAD; } @Override public UploadType getUploadType() { return UploadType.SHARED_FILE; } @Override public boolean isTLSCapable() { return false; } @Override public String getAddress() { return "torrent upload"; } @Override public InetAddress getInetAddress() { return null; } @Override public int getPort() { return -1; } @Override public InetSocketAddress getInetSocketAddress() { return null; } @Override public String getAddressDescription() { return null; } @Override public File getFile() { return torrent.getTorrentDataFile(); } @Override public URN getUrn() { if (urn == null) { synchronized (this) { if (urn == null) { try { urn = URN.createSha1UrnFromHex(torrent.getSha1()); } catch (IOException e) { throw new RuntimeException(e); } } } } return urn; } @Override public int getNumUploadConnections() { return torrent.getNumConnections(); } @Override public String getPresenceId() { return null; } @Override public float getSeedRatio() { return torrent.getSeedRatio(); } }