/****************************************************************************** * Copyright (c) 2006 Remy Suen. All rights reserved. This program and the * accompanying materials are made available under the terms of the Eclipse * Public License v1.0, which accompanies this distribution and is available at * http://www.eclipse.org/legal/epl-v10.html, and also the MIT license, which * also accompanies this distribution. This dual licensing scheme allows a * developer to choose either license for use when developing applications with * this code. * * Contributors: * Remy Suen <remy.suen@gmail.com> - initial API and implementation ******************************************************************************/ package org.eclipse.bittorrent; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.channels.SocketChannel; import java.util.Properties; import org.eclipse.bittorrent.internal.net.TorrentManager; /** * The <code>Torrent</code> class is used for hooking onto a * {@link TorrentFile} so that pieces can be exchanged with peers. */ public class Torrent { /** * The {@link TorrentManager} that is managing all of the backend functions * of this torrent. */ private final TorrentManager manager; /** * The 20-length SHA-1 hash of the torrent file's metainfo's * <code>info</code> dictionary. */ private final String infoHash; /** * Creates a new <code>Torrent</code> to begin with exchanging pieces with * other pieces for the given torrent. * * @param torrent * the torrent file to use * @param properties * a <code>Properties</code> instance for storing information, * this can be <code>null</code> * @throws IOException * If an I/O error occurs whilst creating or hooking up with the * files associated with the torrent */ Torrent(TorrentFile torrent, Properties properties) throws IOException { manager = new TorrentManager(torrent, properties); infoHash = torrent.getInfoHash(); } /** * Bridges the connection with the provided channel. * * @param channel * the channel to connect to * @throws UnsupportedEncodingException * If the <code>ISO-8859-1</code> encoding is not supported */ void connectTo(SocketChannel channel) throws UnsupportedEncodingException { manager.connectTo(channel); } /** * Contacts the tracker to begin exchanging pieces with any peers that are * found. * * @throws IOException * If an error occurs while querying the tracker or connecting * to one of the provided peers */ public void start() throws IOException { manager.start(); TorrentServer.addTorrent(infoHash, this); } /** * Stops downloading, seeding, or the hash checking process for this * torrent. It is crucial that this method be called for termination as it * will perform clean-up on lingering threads that are performing background * operations. * * @throws IOException * If an IOException occurred while informing the tracker that * the client is stopping */ public void stop() throws IOException { TorrentServer.remove(infoHash); manager.stop(); } /** * Removes all previously saved status and configuration information * regarding the opened torrent. This will call {@link #stop()} prior to the * deletion of the files. */ public void remove() { TorrentServer.remove(infoHash); manager.remove(); } /** * Removes all information pertaining to the opened torrent in addition to * removing the target that has been set for downloading or seeding. * * @return <code>true</code> if the target file or directory has been * deleted successfully, <code>false</code> otherwise */ public boolean delete() { TorrentServer.remove(infoHash); return manager.delete(); } /** * Performs a hash check on all pieces for this torrnet in a separate * thread. This may not necessarily run if the torrent is currently * performing other actions. The returned value will indicate whether the * hash check is being performed or not. * * @return <code>true</code> if the torrent will begin performing hash * check on its pieces on a separate thread or is already running a * hash check, </code>false</code> if the torrent is currently * performing other operations and is unable to run a hash check */ public boolean performHashCheck() { return manager.performHashCheck(); } /** * Adds the specified listener to the collection of listeners for this * torrent if it is not already contained. The listener will be notified of * the changes of the current state of the torrent's activity. The event's * state will correspond to the value returned from {@link #getState()}. * * @param listener * the listener to notify * @throws IllegalArgumentException * If <code>listener</code> is <code>null</code> */ public void addTorrentStateListener(ITorrentStateListener listener) throws IllegalArgumentException { if (listener == null) { throw new IllegalArgumentException("The listener cannot be null"); } manager.addTorrentStateListener(listener); } /** * Adds the specified listener to the collection of listeners for this * torrent if it is not already contained. The listener will be notified * when another piece has been completed by verifying it against a hash sum. * * @param listener * the listener to notify * @throws IllegalArgumentException * If <code>listener</code> is <code>null</code> */ public void addTorrentProgressListener(ITorrentProgressListener listener) throws IllegalArgumentException { if (listener == null) { throw new IllegalArgumentException("The listener cannot be null"); } manager.addTorrentProgressListener(listener); } /** * Adds the specified listener to the collection of listeners for this * torrent if it is not already contained. The listener will be notified * when a piece has downloaded some amount of additional bytes. * * @param listener * the listener to notify * @throws IllegalArgumentException * If <code>listener</code> is <code>null</code> */ public void addPieceProgressListener(IPieceProgressListener listener) throws IllegalArgumentException { if (listener == null) { throw new IllegalArgumentException("The listener cannot be null"); } manager.addPieceProgressListener(listener); } /** * Adds the specified listener to the collection of listeners for this * torrent if it is not already contained. The listener will be notified * when a piece has successfully completed its hash check. * * @param listener * the listener to notify * @throws IllegalArgumentException * If <code>listener</code> is <code>null</code> */ public void addHashCheckListener(IHashCheckListener listener) { if (listener == null) { throw new IllegalArgumentException("The listener cannot be null"); } manager.addHashCheckListener(listener); } /** * Adds the specified listener to the collection of listeners for this * torrent if it is not already contained. The listener will be notified * when errors such as tracker failures or hash check failures occurs. * * @param listener * the listener to notify * @throws IllegalArgumentException * If <code>listener</code> is <code>null</code> */ public void addTorrentErrorListener(ITorrentErrorListener listener) throws IllegalArgumentException { if (listener == null) { throw new IllegalArgumentException("The listener cannot be null"); } manager.addTorrentErrorListener(listener); } /** * Removes the specified listener so that it will no longer be notified of * state updates. * * @param listener * the listener to remove * @return <code>true</code> if one such listener was removed, * <code>false</code> otherwise */ public boolean removeTorrentStateListener(ITorrentStateListener listener) { return manager.removeTorrentStateListener(listener); } /** * Removes the specified listener so that it will no longer be notified when * pieces has been completed. * * @param listener * the listener to remove * @return <code>true</code> if one such listener was removed, * <code>false</code> otherwise */ public boolean removeTorrentProgressListener( ITorrentProgressListener listener) { return manager.removeTorrentProgressListener(listener); } /** * Removes the specified listener so that it will no longer be notified of a * piece having downloaded some additional bytes. * * @param listener * the listener to remove * @return <code>true</code> if one such listener was removed, * <code>false</code> otherwise */ public boolean removePieceProgressListener(IPieceProgressListener listener) { return manager.removePieceProgressListener(listener); } /** * Removes the specified listener so that it will no longer be notified of * pieces have completed the hash checking process. * * @param listener * the listener to remove * @return <code>true</code> if one such listener was removed, * <code>false</code> otherwise */ public boolean removeHashCheckListener(IHashCheckListener listener) { return manager.removeHashCheckListener(listener); } /** * Removes the specified listener so that it will no longer be notified of * errors. * * @param listener * the listener to remove * @return <code>true</code> if one such listener was removed, * <code>false</code> otherwise */ public boolean removeTorrentErrorListener(ITorrentErrorListener listener) { return manager.removeTorrentErrorListener(listener); } /** * Sets the maximum number of connections that this torrent should attempt * to connect to. The default value is set to 50 although 30 peers should * already be plenty. This value should not be heightened unless there is a * good reason to do so as it will likely cause network congestions. * * @param maxConnections * the maximum number of connections that should be used * @throws IllegalArgumentException * If <code>maxConnections</code> is negative */ public void setMaxConnections(int maxConnections) throws IllegalArgumentException { if (maxConnections < 0) { throw new IllegalArgumentException("The maximum number of " + "connections cannot be negative"); } manager.setMaxConnections(maxConnections); } /** * Setup file download priority levels and whether a file should even be * downloaded at all. * * @param downloadChoices * an integer array which stores a value greater than zero if the * file should have a high priority, a value equal to zero if it * should have a regular priority, or less than zero if it should * not be downloaded at all, the values should correspond to the * files returned from {@link TorrentFile}'s * {@link TorrentFile#getFilenames()} method * @throws IllegalArgumentException * If <code>downloadChoices</code> is <code>null</code> or * if the length of the array is not equal to the number of * files for this torrent */ public void setFilesToDownload(int[] downloadChoices) { if (downloadChoices == null) { throw new IllegalArgumentException("The array cannot be null"); } manager.setFilesToDownload(downloadChoices); } /** * Retrieves the amount that has been downloaded thus far since the original * call to {@link #start()}. * * @return the amount of bytes that has been downloaded from peers */ public long getDownloaded() { return manager.getDownloaded(); } /** * Returns the number of bytes that has been uploaded to peers thus far * since calling {@link #start()}. * * @return the amount of bytes that has been uploaded to peers */ public long getUploaded() { return manager.getUploaded(); } /** * Retreives the number of bytes that are required to complete the download. * * @return the number of bytes left to complete the download */ public long getRemaining() { return manager.getRemaining(); } /** * Gets the downloading speed as calculated over a twenty second rolling * average. * * @return the speed at which bytes are being downloaded from peers */ public long getDownSpeed() { return manager.getDownSpeed(); } /** * Retrieves the uploading speed per calculations over a twenty second * rolling average. * * @return the speed at which bytes are being uploaded to peers */ public long getUpSpeed() { return manager.getUpSpeed(); } /** * Sets the maximum number of bytes to download per second from peers. If * <code>maximum</code> is zero or negative, the speed capping limit will * be lifted. * * @param maximum * the maximum number of bytes that should be downloaded from * peers per second */ public void setMaxDownloadSpeed(long maximum) { manager.setMaxDownloadSpeed(maximum); } /** * Sets the maximum number of bytes to upload per second to peers. If * <code>maximum</code> is zero or negative, the speed capping limit will * be lifted. * * @param maximum * the maximum number of bytes that should be uploaded to peers * per second */ public void setMaxUploadSpeed(long maximum) { manager.setMaxUploadSpeed(maximum); } /** * Retrieves the time in seconds that are remaining for this download to * complete. If the returned value is <code>-1</code>, the time is * unknown. This will be returned when the downloading speed is at 0. * * @return the time remaining in seconds for the download to complete or * <code>-1</code> if the value is not known */ public long getTimeRemaining() { return manager.getTimeRemaining(); } /** * Retrieves the amount of data that has been discarded thus far. This is * caused by pieces that has failed the integrity hash check. * * @return the amount of bytes that has been discarded */ public long getDiscarded() { return manager.getDiscarded(); } /** * Retrieves the number of peers that connections have been created for thus * far. * * @return the number of connected peers */ public int getConnectedPeers() { return manager.getConnectedPeers(); } /** * Returns the number of seeds that are currently assisting with the * distribution. * * @return the number of connected seeds, if the value is <code>-1</code>, * the tracker does not support the distribution of this information * @see #getPeers() */ public int getSeeds() { return manager.getSeeds(); } /** * Returns the total number of peers that are attempting to download this * torrent. * * @return the total number of connected peers on the torrent, if the value * is <code>-1</code>, the tracker does not support the * distribution of this information * @see #getSeeds() */ public int getPeers() { return manager.getPeers(); } /** * Retrieves the torrent file that was used to create this torrent. * * @return the {@link TorrentFile} that initialized this */ public TorrentFile getTorrentFile() { return manager.getTorrentFile(); } /** * Retrieves the current state in which this torrent is currently in. This * could be any one of the states provided by the * {@link ITorrentStateListener} interface. * * @return the state that this torrent is currently in * @see ITorrentStateListener#STARTED * @see ITorrentStateListener#EXCHANGING * @see ITorrentStateListener#STOPPED * @see ITorrentStateListener#FINISHED * @see ITorrentStateListener#HASH_CHECKING */ public int getState() { return manager.getState(); } }