package edu.washington.cs.oneswarm.test.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
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.download.DownloadManagerPeerListener;
import org.gudy.azureus2.core3.peer.PEPeer;
import org.gudy.azureus2.core3.peer.PEPeerManager;
import org.gudy.azureus2.core3.torrent.TOTorrent;
import org.gudy.azureus2.core3.torrent.TOTorrentCreator;
import org.gudy.azureus2.core3.torrent.TOTorrentException;
import org.gudy.azureus2.core3.torrent.TOTorrentFactory;
import org.gudy.azureus2.core3.torrent.TOTorrentProgressListener;
import org.gudy.azureus2.core3.torrentdownloader.TorrentDownloader;
import org.gudy.azureus2.core3.torrentdownloader.TorrentDownloaderCallBackInterface;
import org.gudy.azureus2.core3.torrentdownloader.TorrentDownloaderFactory;
import org.gudy.azureus2.core3.util.ByteFormatter;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.TorrentUtils;
import com.aelitis.azureus.core.AzureusCore;
import com.aelitis.azureus.core.impl.AzureusCoreImpl;
import edu.washington.cs.oneswarm.f2f.TextSearchResult;
import edu.washington.cs.oneswarm.f2f.permissions.GroupBean;
import edu.washington.cs.oneswarm.f2f.permissions.PermissionsDAO;
import edu.washington.cs.oneswarm.plugins.PluginCallback;
import edu.washington.cs.oneswarm.ui.gwt.CoreInterface;
public class TorrentExperimentFunctions {
private static Logger logger = Logger.getLogger(TorrentExperimentFunctions.class.getName());
private final AzureusCore azCore;
private final CoreInterface coreInterface;
private final DownloadMonitor pht;
public TorrentExperimentFunctions(CoreInterface coreInterface, DownloadMonitor pht) {
azCore = AzureusCoreImpl.getSingleton();
this.coreInterface = coreInterface;
this.pht = pht;
}
public void downloadTorrentAndStart(final String url, final long at,
final ArrayList<GroupBean> perms, final int maxul_bytes) {
// we only use single file torrents, and are sure to remove things
// before adding to prevent unintended seeding.
TorrentDownloader downloader = TorrentDownloaderFactory.create(
new TorrentDownloaderCallBackInterface() {
@Override
public void TorrentDownloaderEvent(int state, TorrentDownloader inf) {
switch (state) {
case TorrentDownloader.STATE_CANCELLED:
case TorrentDownloader.STATE_DUPLICATE:
case TorrentDownloader.STATE_ERROR:
logger.warning("Error during download: " + url + " / " + state + " "
+ inf.getError());
break;
case TorrentDownloader.STATE_FINISHED:
final File file = inf.getFile();
(new File("/tmp/expfile")).delete();
logger.info("Waiting until: " + (new Date(at)) + " to start");
(new Thread("startWaiter for download: " + url) {
@Override
public void run() {
try {
while (System.currentTimeMillis() < at) {
Thread.sleep(100);
}
} catch (Exception e) {
e.printStackTrace();
}
logger.info("starting " + url + " @ " + (new Date()));
try {
TOTorrent tor = TOTorrentFactory
.deserialiseFromBEncodedFile(file);
byte[] hashBytes = tor.getHash();
PermissionsDAO.get().setGroupsForHash(
ByteFormatter.encodeString(hashBytes), perms, true);
DownloadManager dm = azCore.getGlobalManager()
.addDownloadManager(file.getAbsolutePath(),
"/tmp/expfile");
dm.getStats().setUploadRateLimitBytesPerSecond(maxul_bytes);
watch_locally(dm);
} catch (TOTorrentException e) {
logger.warning(e.toString());
e.printStackTrace();
}
}
}).start();
break;
}
}
}, url);
downloader.setDeleteFileOnCancel(true);
downloader.start();
}
public synchronized void watch_locally(final DownloadManager dm) {
if (pht == null) {
return;
}
dm.addListener(new DownloadManagerListener() {
@Override
public void completionChanged(DownloadManager manager, boolean completed) {
}
@Override
public void downloadComplete(DownloadManager manager) {
pht.downloadFinished(dm, (System.currentTimeMillis() - start_times.get(dm)));
start_times.remove(dm);
}
@Override
public void filePriorityChanged(DownloadManager download, DiskManagerFileInfo file) {
}
@Override
public void positionChanged(DownloadManager download, int oldPosition, int newPosition) {
}
@Override
public void stateChanged(DownloadManager manager, int state) {
}
});
dm.addPeerListener(new DownloadManagerPeerListener() {
final long added = System.currentTimeMillis();
@Override
public void peerAdded(PEPeer peer) {
if (start_times.containsKey(dm) == false) {
start_times.put(dm, System.currentTimeMillis());
}
pht.downloadBootstrapped(dm, (System.currentTimeMillis() - added));
}
@Override
public void peerManagerAdded(PEPeerManager manager) {
}
@Override
public void peerManagerRemoved(PEPeerManager manager) {
}
@Override
public void peerManagerWillBeAdded(PEPeerManager manager) {
}
@Override
public void peerRemoved(PEPeer peer) {
}
}, true);
}
public void createRandomAndShare(String name, long sizeBytes) throws IOException,
TOTorrentException {
logger.info("createRandomAndShare()");
Random r = new Random();
File randomBytes = File.createTempFile("ost", "randombytes");
randomBytes.deleteOnExit();
FileOutputStream out = new FileOutputStream(randomBytes);
byte[] chunk = new byte[16 * 1024];
long tot = 0;
while (tot < sizeBytes) {
r.nextBytes(chunk);
long howmany = Math.min(chunk.length, sizeBytes - tot);
out.write(chunk, 0, (int) howmany);
tot += howmany;
}
out.flush();
out.close();
logger.info("wrote " + tot + " random bytes, creating...");
creatTorrentAndShare(randomBytes);
}
public void downloadAndShare(String swarmName, String url) throws MalformedURLException,
IOException, TOTorrentException {
logger.info("downloadAndShare()");
HttpURLConnection conn = (HttpURLConnection) (new URL(url)).openConnection();
byte[] data = new byte[16 * 1024];
File outFile = File.createTempFile("ost", "sharescratch");
outFile.deleteOnExit();
FileOutputStream out = new FileOutputStream(outFile);
InputStream in = conn.getInputStream();
int read = 0, tot = 0;
int last_perc = 0;
while ((read = in.read(data)) > 0) {
out.write(data, 0, read);
tot += read;
if (conn.getContentLength() > 0) {
int perc = (tot * 100 / conn.getContentLength());
if (perc > last_perc) {
last_perc = perc;
logger.info("HTTP download completion: " + last_perc);
}
}
}
out.flush();
creatTorrentAndShare(outFile);
}
public void creatTorrentAndShare(File outFile) throws TOTorrentException, IOException {
TOTorrentCreator creator = TOTorrentFactory.createFromFileOrDirWithComputedPieceLength(
outFile, new URL("http://tracker.invalid/announce"), true);
creator.addListener(new TOTorrentProgressListener() {
@Override
public void reportCurrentTask(String task_description) {
logger.info("torrent creation: " + task_description);
}
@Override
public void reportProgress(int percent_complete) {
logger.info("torrent creation completion: " + percent_complete);
}
});
TOTorrent outTorrent = creator.create();
logger.info("created torrent: " + (new String(outTorrent.getName())));
addAndStartTorrent(outTorrent, outFile);
}
ConcurrentHashMap<DownloadManager, Long> start_times = new ConcurrentHashMap<DownloadManager, Long>();
public void downloadAndStart(final String base64hash, final long delay) throws IOException,
TOTorrentException {
if (delay > 0) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(delay);
_downloadAndStart(base64hash);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TOTorrentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
t.setDaemon(true);
t.setName("delayed download");
t.start();
} else {
_downloadAndStart(base64hash);
}
}
private void _downloadAndStart(final String base64hash) throws TOTorrentException {
int searchID = coreInterface.getF2FInterface().sendSearch("id:" + base64hash);
final ByteArrayOutputStream out = new ByteArrayOutputStream();
boolean requested = false;
long timeout = 60 * 1000;
long started = System.currentTimeMillis();
boolean done = false;
while (started + timeout > System.currentTimeMillis() && !done) {
List<TextSearchResult> results = coreInterface.getF2FInterface().getSearchResult(
searchID);
if (results.size() > 0 && !requested) {
requested = true;
final int channel_id = results.get(0).getFirstSeenChannelId();
final int connection_id = results.get(0).getFirstSeenConnectionId();
coreInterface.getF2FInterface().getMetaInfo(connection_id, channel_id, base64hash,
0, new PluginCallback<byte[]>() {
@Override
public void dataRecieved(long count) {
logger.finer("Read " + count);
}
@Override
public void errorOccured(String str) {
logger.warning("Error occurred during metainfo download: "
+ connection_id + " / " + channel_id + " / " + str);
}
@Override
public void progressUpdate(int percentage) {
logger.finer("Progress updated: " + percentage);
}
@Override
public void requestCompleted(byte[] bytes) {
try {
out.write(bytes);
} catch (IOException e) {
e.printStackTrace();
}
logger.fine("Metainfo DL completed: " + bytes.length + " / "
+ base64hash);
}
});
}
if (out.size() > 0) {
logger.info("Read " + out.size() + " bytes of metainfo");
// will crash if this doesn't parse, which is what we want
final TOTorrent torrent = TorrentUtils
.readFromBEncodedInputStream(new ByteArrayInputStream(out.toByteArray()));
(new Thread("f2f dl exp start wait thread for: " + base64hash) {
@Override
public void run() {
try {
final DownloadManager dm = addAndStartTorrent(torrent);
logger.info("successfully added: " + base64hash);
watch_locally(dm);
} catch (IOException e) {
logger.warning(e.toString());
e.printStackTrace();
} catch (TOTorrentException e) {
logger.warning(e.toString());
e.printStackTrace();
}
}
}).start();
done = true;
break;
}
try {
Thread.sleep(100);
} catch (Exception e) {
}
}
}
public DownloadManager addAndStartTorrent(TOTorrent torrent) throws TOTorrentException,
IOException {
return addAndStartTorrent(torrent, null);
}
public DownloadManager addAndStartTorrent(TOTorrent torrent, File outFile)
throws TOTorrentException, IOException {
File scratch = File.createTempFile("ost", "expdl");
scratch.deleteOnExit();
// (new FileOutputStream(scratch)).write(out.toByteArray());
torrent.serialiseToBEncodedFile(scratch);
if (outFile == null) {
outFile = File.createTempFile("ost", "savedl");
}
outFile.deleteOnExit();
ArrayList<GroupBean> converted_groups = new ArrayList<GroupBean>();
converted_groups.add(GroupBean.ALL_FRIENDS);
try {
PermissionsDAO.get().setGroupsForHash(ByteFormatter.encodeString(torrent.getHash()),
converted_groups, true);
logger.finest("add dl, groups: ");
for (GroupBean g : converted_groups) {
logger.finest(g.toString());
}
logger.finest("end groups");
} catch (Exception e) {
e.printStackTrace();
Debug.out("couldn't set perms for swarm! " + torrent.getName());
}
logger.info("set permissions");
return azCore.getGlobalManager().addDownloadManager(scratch.getAbsolutePath(),
outFile.getAbsolutePath());
}
@SuppressWarnings("unchecked")
public void cleardls() {
for (DownloadManager dm : (List<DownloadManager>) azCore.getGlobalManager()
.getDownloadManagers()) {
try {
azCore.getGlobalManager().removeDownloadManager(dm, true, true);
} catch (Exception e) {
logger.warning("Failed to clear dls: " + e.getMessage());
e.printStackTrace();
}
}
}
}