package edu.washington.cs.oneswarm.f2f.multisource; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.logging.Logger; import org.bouncycastle.util.encoders.Base64; import org.gudy.azureus2.core3.config.COConfigurationManager; import org.gudy.azureus2.core3.disk.DiskManagerFileInfo; import org.gudy.azureus2.core3.download.DownloadManager; import org.gudy.azureus2.core3.download.DownloadManagerListener; import org.gudy.azureus2.core3.global.GlobalManagerListener; import org.gudy.azureus2.core3.torrent.TOTorrentException; import org.gudy.azureus2.core3.util.HashWrapper; import com.aelitis.azureus.core.AzureusCore; import com.aelitis.azureus.core.impl.AzureusCoreImpl; import edu.washington.cs.oneswarm.f2f.FileListFile; import edu.washington.cs.oneswarm.f2f.multisource.Sha1HashManager.Sha1HashJobListener; 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 Sha1DownloadManager { public static final String ONESWARM_AUTO_ADDED = "oneswarm.auto.added"; public static String MULTI_TORRENT_SOURCE_DOWNLOAD_DIR = "oneswarm.multi.torrent.download.temp.dir"; private final static Logger logger = Logger.getLogger(Sha1DownloadManager.class.getName()); private final AzureusCore core; private Set<Sha1Peer> currentPeers = new HashSet<Sha1Peer>(); private Sha1HashManager sha1HashManager = Sha1HashManager.getInstance(); public Sha1DownloadManager() { core = AzureusCoreImpl.getSingleton(); core.getGlobalManager().addListener(new GlobalManagerListener() { public void seedingStatusChanged(boolean seedingOnlyMode) { } public void downloadManagerRemoved(DownloadManager dm) { } public void downloadManagerAdded(DownloadManager dm) { // first tell the hash manager that we got a new download manager sha1HashManager.downloadManagerAdded(dm, true); // basically we want to check if we need to add peers each time // a download starts dm.addListener(new DownloadManagerListener() { public void stateChanged(DownloadManager manager, int state) { checkForNewSha1Matches(manager); } private void checkForNewSha1Matches(DownloadManager manager) { if (manager.getState() == DownloadManager.STATE_DOWNLOADING) { logger.fine("download manager state changed, checking for sha1 matches: " + manager.getDisplayName()); try { downloadManagerDownloading(manager); } catch (TOTorrentException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public void positionChanged(DownloadManager download, int oldPosition, int newPosition) { } public void filePriorityChanged(DownloadManager download, DiskManagerFileInfo file) { checkForNewSha1Matches(download); } public void downloadComplete(DownloadManager manager) { } public void completionChanged(DownloadManager manager, boolean bCompleted) { } }); } public void destroyed() { } public void destroyInitiated() { } }); } public void addHashJobListener(Sha1HashJobListener l) { sha1HashManager.addJobListener(l); } public static File getMultiTorrentDownloadDir() throws IOException { String tempDir = COConfigurationManager.getStringParameter(MULTI_TORRENT_SOURCE_DOWNLOAD_DIR); if (tempDir == null || tempDir.length() == 0) { String downloadDir = COConfigurationManager.getStringParameter("Default save path"); File tempDirFile = new File(downloadDir, "additional_source_temp"); tempDir = tempDirFile.getCanonicalPath(); } File downloadDir = new File(tempDir); if (!downloadDir.isDirectory()) { downloadDir.mkdirs(); } if (!downloadDir.isDirectory()) { String msg = "unable to create multi_torrent source temp dir: '" + tempDir + "'"; logger.warning(msg); throw new IOException(msg); } return downloadDir; } @SuppressWarnings("unchecked") private void downloadManagerDownloading(final DownloadManager download) throws TOTorrentException { if (download.isDownloadComplete(false)) { logger.fine("skipping download manager, already completed..."); return; } List<DownloadManager> downloadManagers = core.getGlobalManager().getDownloadManagers(); // create a library of sha1->download manager mappings logger.finer("creating sha1 map"); HashMap<HashWrapper, List<DownloadManager>> sha1Mappings = new HashMap<HashWrapper, List<DownloadManager>>(); for (DownloadManager d : downloadManagers) { if (!download.equals(d)) { HashWrapper[] sha1Found = getHashesFromDownload(d, FileListFile.KEY_SHA1_HASH, false); /* * add everything we found */ for (int i = 0; i < sha1Found.length; i++) { HashWrapper sha1 = sha1Found[i]; if (sha1 == null) { continue; } if (!sha1Mappings.containsKey(sha1)) { sha1Mappings.put(sha1, new LinkedList<DownloadManager>()); } sha1Mappings.get(sha1).add(d); } } } /* * now, scan all files in the current torrent and check for matches */ logger.finest("scanning for matches"); HashWrapper[] torrentSha1s = getHashesFromDownload(download, FileListFile.KEY_SHA1_HASH, false); final HashSet<DownloadManager> interestingDms = new HashSet<DownloadManager>(); for (int i = 0; i < torrentSha1s.length; i++) { HashWrapper t = torrentSha1s[i]; if (t == null) { continue; } List<DownloadManager> matchingDms = sha1Mappings.get(t); if (matchingDms != null) { interestingDms.addAll(matchingDms); } } /* * last, we have a set of interesting dms, */ logger.fine("found " + interestingDms.size() + " interesting download managers"); DownloadManagerStarter.startDownload(download, new DownloadManagerStartListener() { public void downloadStarted() { for (final DownloadManager srcDm : interestingDms) { DownloadManagerStarter.startDownload(srcDm, new DownloadManagerStartListener() { public void downloadStarted() { addPeer(srcDm, download); addPeer(download, srcDm); } }); } } }); } static HashWrapper[] getHashesFromDownload(DownloadManager d, String type, boolean excludeSkipped) { String hashesAdded = d.getDownloadState().getAttribute( Sha1HashManager.OS_HASHES_ADDED); if (hashesAdded == null) { return new HashWrapper[0]; } DiskManagerFileInfo[] files = d.getDiskManagerFileInfo(); HashWrapper[] hashes = new HashWrapper[files.length]; String[] base64Hashes = d.getDownloadState().getListAttribute(type); for (int i = 0; i < base64Hashes.length; i++) { if (base64Hashes[i] == null) { continue; } if (excludeSkipped && files[i].isSkipped()) { continue; } hashes[i] = new HashWrapper(Base64.decode(base64Hashes[i])); } return hashes; } private void addPeer(DownloadManager source, final DownloadManager destination) { if (destination.isDownloadComplete(false)) { return; } boolean add = false; try { Sha1Peer peer = new Sha1Peer(this, source, destination); synchronized (currentPeers) { logger.finer("got request to add peer, currently active=" + currentPeers.size()); if (!currentPeers.contains(peer)) { currentPeers.add(peer); add = true; } } if (add) { logger.finer("adding peer"); destination.getPeerManager().addPeer(peer); } else { logger.finer("not adding peer (already exists)"); } } catch (PieceTranslationExcetion e) { // TODO Auto-generated catch block e.printStackTrace(); } } void peerClosed(Sha1Peer sha1Peer) { synchronized (currentPeers) { currentPeers.remove(sha1Peer); } } }