Enabling Peer-to-Peer BitTorrent Downloads with Azureus
Pages: 1, 2, 3, 4
The ClientTorrentManager class
The ClientTorrentManager singleton class in the client implementation is what the TorrentSeederTrackerServer class is to the server implementation--the core component where all the logic is situated. The ClientFrame class, which is the caller in this case, starts off by setting the name of the tracker server (setServer(String server) method) and the download destination directory (setDestination(File destination) method). It then invokes the startTorrentProcess() method, which downloads the torrent file from the server and starts the torrent data file download process.
public class ClientTorrentManager implements DownloadManagerTrackerListener{
...
/**
* The relative path on the server where the torrent of the latest data file is kept.
*/
public static final String TORRENT_PATH = "/torrent-server/download.torrent";
/**
* Singleton instance of this class
*/
private static ClientTorrentManager trClient;
...
private AzureusCore core;
private DownloadManager fileDownloadManager;
...
private File torrentFile;
private File destination;
private String server;
private URL torrentFileURL;
...
private Stack<String> messages; //Might be used by calling clients.
...
public boolean startTorrentProcess() throws IllegalStateException{
...
if(torrentFile == null){
//We need to download the torrentFile
boolean success = downloadTorrentFile();
...
}
//Now that we have the torrent file,
//we can fire up the manager and start downloading...
GlobalManager globalManager = core.getGlobalManager();
fileDownloadManager = globalManager.addDownloadManager(torrentFile.getAbsolutePath(),
destination.getAbsolutePath());
fileDownloadManager.setForceStart(true);
File saveLocation = fileDownloadManager.getSaveLocation();
if(saveLocation.exists() == true){
//This file already exists on disk
//attempt to delete it if the size does not match the torrent's
long torrentFileSize = fileDownloadManager.getSize();
long actualFileSize = saveLocation.length();
if(torrentFileSize != actualFileSize){
addMessage("Length of torrent file (" + torrentFileSize + ") " +
"and file already on disk (" + actualFileSize + ") " +
"does not match. " +
"It will be overwritten.");
saveLocation.delete();
}
}
fileDownloadManager.addTrackerListener(this);
fileDownloadManager.addListener(new DownloadManagerListener(){
...
public void stateChanged(DownloadManager manager, int state) {
switch(state){
..
case DownloadManager.STATE_ERROR:
addMessage("Torrent Error Encountered... - " + manager.getErrorDetails());
break;
...
}
}
});
globalManager.startAllDownloads();
addMessage("Started downloading torrent file....");
running = true;
return true;
}
...
public void setDestination(File destination) {
this.destination = destination;
}
...
public void setServer(String server) {
this.server = server;
}
...
}
With the torrent download process up and running, the ClientFrame gathers statistics for display to the user every few seconds using the following methods:
public boolean isDownloading(){...}
public boolean isSeeding(){...}
public double getPercentDone(){...}
public double getTotalBytesUploaded(){...}
public double getTotalBytesDownloaded(){...}
public double getDataSendRate(){...}
public double getDataReceiveRate(){...}
public Collection<String> getMessages(){..}
public boolean isRunning() {...}
Detecting When a New Data File Is Uploaded to the Server
As stated earlier, the client implementation also needs to be able to detect when a new data file has been uploaded to the server. Fortunately, this is made possible by the Azureus API through the registration of a DownloadManagerTrackerListener instance (in this case the ClientTorrentManager itself) as a listener on the DownloadManager instance.
...
fileDownloadManager.addTrackerListener(this);
...
The scrapeResult() method is then subsequently invoked for all tracker related events. If it then found that the original torrent file is no longer available, then all registered listeners are notified (in the application the ClientFrame) and the appropriate action is taken.
public class ClientTorrentManager implements DownloadManagerTrackerListener{
...
private List<WeakReference<TorrentFileListener>> torrentChangeListeners;
...
public void scrapeResult(TRTrackerScraperResponse response) {
addMessage("Tracker Scrape Result : " + response.getStatusString());
if(response.getStatus() == TRTrackerScraperResponse.ST_ERROR){
if(response.getStatusString().indexOf("File Not Found") != -1){
//We need to notify all the listeners
for(WeakReference<TorrentFileListener> ref : torrentChangeListeners){
ref.get().trackerTorrentFileChanged(new TorrentFileEvent(this));
}
}
}
}
...
}
Conclusion
The size and amount of files being distributed over the internet is increasing daily. If we as content providers are to carry on distributing these files using the traditional client-server paradigm, so too will our costs. Hopefully this article will serve as an introduction to peer-to-peer file distribution, the cost benefits it offers, and how you can leverage the freely available Azureus engine to implement BitTorrent file sharing in your own applications.
Resources
- Source code for this article. Please refer to the dependencies.txt file for details of the JARs you require.
- BitTorrent Protocol Specification
- Azureus
- Jakarta Commons File Upload library (used by the
NewTorrentFileUploadServletservlet)
Jacobus Steenkamp has been developing in Java for the last 5 years and has worked in various sectors including the financial, pharmaceutical and energy industries.
Return to ONJava.com.