package lbms.plugins.mldht.azureus; import java.util.*; import java.util.concurrent.DelayQueue; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import lbms.plugins.mldht.kad.AnnounceTask; import lbms.plugins.mldht.kad.DBItem; import lbms.plugins.mldht.kad.DHT; import lbms.plugins.mldht.kad.Task; import lbms.plugins.mldht.kad.TaskListener; import lbms.plugins.mldht.kad.DHT.DHTtype; import org.gudy.azureus2.plugins.download.Download; import org.gudy.azureus2.plugins.download.DownloadAnnounceResult; import org.gudy.azureus2.plugins.download.DownloadAnnounceResultPeer; import org.gudy.azureus2.plugins.download.DownloadAttributeListener; import org.gudy.azureus2.plugins.download.DownloadException; import org.gudy.azureus2.plugins.download.DownloadListener; import org.gudy.azureus2.plugins.download.DownloadManagerListener; import org.gudy.azureus2.plugins.download.DownloadScrapeResult; import org.gudy.azureus2.plugins.download.DownloadTrackerListener; import org.gudy.azureus2.plugins.torrent.TorrentAttribute; /** * @author Damokles * */ public class Tracker implements TaskListener, DownloadListener, DownloadAttributeListener, DownloadTrackerListener, DownloadManagerListener { public static final int MAX_CONCURRENT_ANNOUNCES = 8; public static final int MAX_TRACKED_TORRENTS = 30; public static final int TRACKER_UPDATE_INTERVAL = 30 * 1000; public static final int SHORT_DELAY = 60 * 1000; public static final int MIN_ANNOUNCE_INTERVAL = 5 * 60 * 1000; //actually MIN is added to this public static final int MAX_ANNOUNCE_INTERVAL = 20 * 60 * 1000; public static final String PEER_SOURCE_NAME = "DHT"; // DownloadAnnounceResultPeer.PEERSOURCE_DHT; private int currentAnnounces = 0; private MlDHTPlugin plugin; private boolean running; private Random random = new Random(); private ScheduledFuture<?> timer; private TorrentAttribute ta_networks; private TorrentAttribute ta_peer_sources; private Map<Download, TrackedTorrent> trackedTorrents = new HashMap<Download, TrackedTorrent>(); private Queue<TrackedTorrent> announceQueue = new DelayQueue<TrackedTorrent>(); protected Tracker(MlDHTPlugin plugin) { // this.plugin = plugin; // ta_networks = plugin.getPluginInterface().getTorrentManager().getAttribute( // TorrentAttribute.TA_NETWORKS); // ta_peer_sources = plugin.getPluginInterface().getTorrentManager().getAttribute( // TorrentAttribute.TA_PEER_SOURCES); } protected void start() { if (running) { return; } DHT.logInfo("Tracker: starting..."); System.out.println("Tracker: starting..."); timer = DHT.getScheduler().scheduleAtFixedRate(new Runnable() { public void run() { checkAnnounceQueue(); } }, 100 * 1000, TRACKER_UPDATE_INTERVAL, TimeUnit.MILLISECONDS); // plugin.getPluginInterface().getDownloadManager().addListener(this); running = true; } protected void stop() { if (!running) { return; } DHT.logInfo("Tracker: stopping..."); if (timer != null) { timer.cancel(false); } announceQueue.clear(); trackedTorrents.clear(); plugin.getPluginInterface().getDownloadManager().removeListener(this); Download[] downloads = plugin.getPluginInterface().getDownloadManager().getDownloads(); for (Download dl : downloads) { // dl.removeAttributeListener(this, ta_networks, // DownloadAttributeListener.WRITTEN); // dl.removeAttributeListener(this, ta_peer_sources, // DownloadAttributeListener.WRITTEN); dl.removeListener(this); dl.removeTrackerListener(this); } running = false; } protected void announceDownload(Download dl) { if (running) { if (dl.getTorrent() == null) { return; } if (dl.getTorrent().isPrivate()) { DHT.logDebug("Announce for [" + dl.getName() + "] forbidden because Torrent is private."); return; } if (trackedTorrents.containsKey(dl)) { if (trackedTorrents.get(dl).isAnnouncing()) { DHT.logDebug("Announce for [" + dl.getName() + "] was denied since there is already one running."); return; } } DHT.logInfo("DHT Starting Announce for " + dl.getName()); if (trackedTorrents.containsKey(dl)) { trackedTorrents.get(dl).setAnnouncing(true); } for (DHTtype type : DHTtype.values()) { // dl.getTorrent().getHash(), AnnounceTask at = plugin.getDHT(type).announce( dl.getTorrent().getInfoHash().toByteArray(), plugin.getPluginInterface().getPluginconfig().getUnsafeIntParameter( "TCP.Listen.Port")); if (at != null) { currentAnnounces++; at.addListener(this); at.setInfo(dl.getName()); } } } } private void scheduleAnnounce(Download dl, boolean shortDelay) { if (!running) { return; } if (trackedTorrents.containsKey(dl)) { TrackedTorrent t = trackedTorrents.get(dl); if (announceQueue.contains(t)) { if (shortDelay) { announceQueue.remove(t); } } int delay = (shortDelay) ? SHORT_DELAY : (MIN_ANNOUNCE_INTERVAL + random.nextInt(MAX_ANNOUNCE_INTERVAL)); t.setDelay(delay); DHT.logInfo("Tracker: scheduled announce in " + t.getDelay(TimeUnit.SECONDS) + "sec for: " + dl.getName()); announceQueue.add(t); } } private void checkAnnounceQueue() { if (!running) { return; } TrackedTorrent t = null; if (currentAnnounces < MAX_CONCURRENT_ANNOUNCES) { t = announceQueue.poll(); } while (t != null) { Download dl = t.getDownload(); if (trackedTorrents.containsKey(dl) && trackedTorrents.get(dl).isAnnouncing()) { scheduleAnnounce(dl, false); } else { announceDownload(dl); } if (currentAnnounces < MAX_CONCURRENT_ANNOUNCES) { t = announceQueue.poll(); } else { break; } } } private boolean checkDownload(Download dl) { if (!running || dl.getTorrent() == null || dl.getTorrent().isPrivate()) { return false; } // String[] sources = dl.getListAttribute(ta_peer_sources); // // String[] networks = dl.getListAttribute(ta_networks); boolean ok = false; // if (networks != null) { // // for (int i = 0; i < networks.length; i++) { // // if (networks[i].equalsIgnoreCase("Public")) { // // ok = true; // // break; // } // } // } if (!ok) { removeTrackedTorrent(dl, "Network is not public anymore"); return false; } ok = false; // for (int i = 0; i < sources.length; i++) { // // if (sources[i].equalsIgnoreCase(PEER_SOURCE_NAME)) { // // ok = true; // // break; // } // } if (!ok) { removeTrackedTorrent(dl, "Peer source was disabled"); return false; } if (dl.getState() == Download.ST_DOWNLOADING || dl.getState() == Download.ST_SEEDING) { if (trackedTorrents.size() < MAX_TRACKED_TORRENTS) { //only act as backup tracker if (plugin.getPluginInterface().getPluginconfig().getPluginBooleanParameter( "backupOnly")) { DownloadAnnounceResult result = dl.getLastAnnounceResult(); if (result == null || result.getResponseType() == DownloadAnnounceResult.RT_ERROR) { addTrackedTorrent(dl, "BackupTracker"); return true; } else { return false; } } addTrackedTorrent(dl, "Normal"); return true; } } else { removeTrackedTorrent(dl, "Has stopped Downloading/Seeding"); return false; } return false; } private void addTrackedTorrent(Download dl, String reason) { if (!trackedTorrents.containsKey(dl)) { DHT.logInfo("Tracker: starting to track Torrent reason: " + reason + ", Torrent; " + dl.getName()); trackedTorrents.put(dl, new TrackedTorrent(dl)); scheduleAnnounce(dl, true); } } private void removeTrackedTorrent(Download dl, String reason) { if (trackedTorrents.containsKey(dl)) { DHT.logInfo("Tracker: stop tracking of Torrent reason: " + reason + ", Torrent; " + dl.getName()); announceQueue.remove(trackedTorrents.get(dl)); trackedTorrents.remove(dl); } } public List<TrackedTorrent> getTrackedTorrentList() { return new ArrayList<TrackedTorrent>(trackedTorrents.values()); } //---------------------[TaskListener]--------------------------------- /* (non-Javadoc) * @see lbms.plugins.mldht.kad.TaskListener#finished(lbms.plugins.mldht.kad.Task) */ public void finished(Task t) { DHT.logDebug("DHT Task done: " + t.getClass().getSimpleName()); if (t instanceof AnnounceTask) { AnnounceTask announce = (AnnounceTask) t; Set<DBItem> items = announce.getReturnedItems(); byte[] infoHash = announce.getInfoHash().getHash(); try { Download dl = plugin.getPluginInterface().getDownloadManager().getDownload( infoHash); currentAnnounces--; TrackedTorrent tor = trackedTorrents.get(dl); if (tor != null) { tor.setAnnouncing(false); } // schedule the next announce if there is not another one pending if (!announceQueue.contains(tor)) { scheduleAnnounce(dl, false); } if (items.size() > 0) { dl.setAnnounceResult(new DHTAnnounceResult(dl, items)); } DHT.logInfo("DHT Announce finished for " + dl.getName() + " found " + items.size() + " Peers."); } catch (DownloadException e) { } } } //---------------------[DownloadListener]--------------------------------- /* (non-Javadoc) * @see org.gudy.azureus2.plugins.download.DownloadListener#positionChanged(org.gudy.azureus2.plugins.download.Download, int, int) */ public void positionChanged(Download download, int oldPosition, int newPosition) { } /* (non-Javadoc) * @see org.gudy.azureus2.plugins.download.DownloadListener#stateChanged(org.gudy.azureus2.plugins.download.Download, int, int) */ public void stateChanged(Download download, int old_state, int new_state) { checkDownload(download); } //---------------------[DownloadAttributeListener]--------------------------------- /* (non-Javadoc) * @see org.gudy.azureus2.plugins.download.DownloadAttributeListener#attributeEventOccurred(org.gudy.azureus2.plugins.download.Download, org.gudy.azureus2.plugins.torrent.TorrentAttribute, int) */ public void attributeEventOccurred(Download download, TorrentAttribute attribute, int event_type) { // if (event_type == DownloadAttributeListener.WRITTEN && (attribute == ta_networks || attribute == ta_peer_sources)) { checkDownload(download); // } } //---------------------[DownloadTrackerListener]--------------------------------- /* (non-Javadoc) * @see org.gudy.azureus2.plugins.download.DownloadTrackerListener#announceResult(org.gudy.azureus2.plugins.download.DownloadAnnounceResult) */ public void announceResult(DownloadAnnounceResult result) { checkDownload(result.getDownload()); } /* (non-Javadoc) * @see org.gudy.azureus2.plugins.download.DownloadTrackerListener#scrapeResult(org.gudy.azureus2.plugins.download.DownloadScrapeResult) */ public void scrapeResult(DownloadScrapeResult result) { checkDownload(result.getDownload()); } //---------------------[DownloadTrackerListener]--------------------------------- /* (non-Javadoc) * @see org.gudy.azureus2.plugins.download.DownloadManagerListener#downloadAdded(org.gudy.azureus2.plugins.download.Download) */ public void downloadAdded(Download download) { // download.addAttributeListener(this, ta_networks, // DownloadAttributeListener.WRITTEN); // download.addAttributeListener(this, ta_peer_sources, // DownloadAttributeListener.WRITTEN); download.addListener(this); download.addTrackerListener(this); checkDownload(download); } /* (non-Javadoc) * @see org.gudy.azureus2.plugins.download.DownloadManagerListener#downloadRemoved(org.gudy.azureus2.plugins.download.Download) */ public void downloadRemoved(Download download) { // download.removeAttributeListener(this, ta_networks, // DownloadAttributeListener.WRITTEN); // download.removeAttributeListener(this, ta_peer_sources, // DownloadAttributeListener.WRITTEN); download.removeListener(this); download.removeTrackerListener(this); removeTrackedTorrent(download, "Download was removed"); } }