ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

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

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.