import java.awt.EventQueue;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.JTable;
import javax.swing.JButton;
import javax.swing.JLabel;
import java.awt.Font;
import javax.swing.JTextPane;
import javax.swing.table.DefaultTableModel;
import javax.swing.JScrollPane;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.File;
import java.io.IOException;
import javax.swing.JTabbedPane;
import java.awt.BorderLayout;
import javax.swing.JPanel;
import javax.swing.JInternalFrame;
import javax.swing.border.BevelBorder;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.logging.Logger;
import GivenTools.ToolKit;
import GivenTools.TorrentInfo;
/**
* The Class RUBTClient is the front end of the torrent client
* that the user interacts with. Here, the user can pick the torrent
* file that they would like to use as well as see live stats
* as their torrent file is downloading and uploading.
*
* @author Deepak, Mike, Josh
*/
public class RUBTClient {
/** The client frame. */
private static JFrame clientFrame;
/** The file. */
public static File file;
/** The info table. */
private static JTable infoTable;
/** The peer table. */
private static JTable peerTable;
/** The tracker table. */
private static JTable trackerTable;
/** The gui logger. */
private static JTextPane guiLogger;
/** The progress bar. */
private static JProgressBar progressBar;
/** The Constant log. */
private static final Logger log = Log2.getLogger(RUBTClient.class);
/** The torrent reader. */
public static TorrentReader torrentReader = new TorrentReader();
/** The torrent info. */
public static TorrentInfo torrentInfo = null;
/** The manager. */
public static Manager manager = null;
/** Check is the program is paused */
public static boolean isPaused = false;
/** File that we are writing to */
public static File outputFile;
/** Torrent file we are using */
public static File torrentFile;
/** Number of hash fails that occur */
public int numHashFails = 0;
public long amountUploaded =0L;
public long amountDownloaded = 0L;
/** Check the command line args and make sure we have two of them */
public static boolean checkNumArguments(String[] args){
if(args.length != 2){
log.fine("Invalid number of command line arguments");
return false;
}
return true;
}
/**
* Launch the GUI application.
*
* @param args the arguments
*/
public static void main(String[] args) {
if(!checkNumArguments(args)){
System.out.println("Invalid number of command line arguments");
return;
}
final String torrentFileName = args[0];
final String outputFileName = args[1];
outputFile = new File(outputFileName);
torrentFile = new File(torrentFileName);
if(!torrentFile.exists()){
log.info("No such torrent file exists");
return;
}
TorrentReader torrentReader = new TorrentReader();
if((torrentInfo = torrentReader.parseTorrentFile(torrentFile)) == null){
log.severe("Unable to parse torrent file; Exiting.");
return;
} else{
log.info("Torrent file parsed successfully");
}
ToolKit.print(torrentInfo.torrent_file_map);
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
RUBTClient window = new RUBTClient();
window.clientFrame.setVisible(true);
window.log("Hello");
window.clientFrame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent evt) {
onExit();
}
});
Manager manager = new Manager(torrentInfo, outputFile);
boolean[] checkPieces;
boolean lastPieceSmaller = false;
if(outputFile.exists()){
if(torrentInfo.file_length % torrentInfo.piece_length != 0) {
checkPieces = new boolean[torrentInfo.file_length % torrentInfo.piece_length + 1];
lastPieceSmaller = true;
}
else {
checkPieces = new boolean[torrentInfo.file_length % torrentInfo.piece_length];
}
checkPieces = Utils.checkPieces(torrentInfo, outputFile);
manager.ourBitfield = checkPieces;
log("Using the verified pieces BitField");
boolean haveFullFile = true;
for(int i = 0; i < manager.ourBitfield.length; i++){
if(manager.ourBitfield[i] == false){
haveFullFile = false;
}
}
if(haveFullFile) {
manager.downloadingStatus = false;
addProgressBar(torrentInfo.piece_hashes.length);
manager.haveFullFile = true;
}
else {
manager.downloadingStatus = true;
for(int i=0;i<checkPieces.length;++i){
if(checkPieces[i]){
Tracker.downloaded+=torrentInfo.piece_length;
addProgressBar(1);
}
}
}
}
else {
outputFile.createNewFile();
manager.ourBitfield = new boolean[torrentInfo.piece_hashes.length];
Arrays.fill(manager.ourBitfield, false);
}
manager.init();
manager.isRunning = true;
manager.start();
setGuiTrackerInfo();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public RUBTClient() {
initialize();
setNumSeeds(0);
}
/**
* Stop the program
*/
private void Stop()
{
if(!isPaused)
{
if(manager != null) {
manager.pauseDownload();
isPaused = true;
toggleProgressBarLoading();
}
}
}
/**
* Start the program
* @throws IOException
*/
private static void Start() throws IOException
{
if(isPaused){
toggleProgressBarLoading();
manager.resumeDownload();
isPaused = false;
}
else {
File output = new File(torrentInfo.file_name);
manager = new Manager(torrentInfo, output);
manager.init();
manager.isRunning = true;
manager.start();
//toggleProgressBarLoading();
}
setGuiTrackerInfo();
}
/**
* Adds the amount downloaded.
*
* @param amountDownloaded the amount downloaded
*/
public static synchronized void addAmountDownloaded(int amountDownloaded)
{
infoTable.setValueAt(((Integer)infoTable.getValueAt(3, 1)) + amountDownloaded, 3, 1);
}
/**
* Sets the amount downloaded.
*
* @param amountDownloaded the new amount downloaded
*/
public static synchronized void setAmountDownloaded(int amountDownloaded)
{
infoTable.setValueAt(amountDownloaded, 3, 1);
}
/**
* Adds the amount uploaded.
*
* @param amountUploaded the amount uploaded
*/
public static synchronized void addAmountUploaded(int amountUploaded)
{
infoTable.setValueAt(((Integer)infoTable.getValueAt(4, 1)) + amountUploaded, 4, 1);
}
/**
* Sets the amount uploaded.
*
* @param amountUploaded the new amount uploaded
*/
public static synchronized void setAmountUploaded(int amountUploaded)
{
infoTable.setValueAt(amountUploaded, 4, 1);
}
/**
* Sets the download rate.
*
* @param downloadRate the new download rate
*/
public static synchronized void setDownloadRate(double downloadRate)
{
infoTable.setValueAt(downloadRate+ " Kb/s", 1, 1);
}
/**
* Sets the upload rate.
*
* @param uploadRate the new upload rate
*/
public static synchronized void setUploadRate(double uploadRate)
{
infoTable.setValueAt(uploadRate+ " Kb/s", 2, 1);
}
/**
* Sets the num peers.
*
* @param numPeers the new num peers
*/
public static synchronized void setNumPeers(int numPeers)
{
infoTable.setValueAt(numPeers, 5, 1);
}
/**
* Sets the num seeds.
*
* @param numSeeds the new num seeds
*/
public static synchronized void setNumSeeds(int numSeeds)
{
infoTable.setValueAt(numSeeds, 6, 1);
}
/**
* Sets the num seeds.
*
* @param numSeeds the new num seeds
*/
public static synchronized void addSeed()
{
infoTable.setValueAt(((Integer)infoTable.getValueAt(6, 1)) + 1, 6, 1);
}
/**
* Sets the share ratio.
*
* @param shareRatio the new share ratio
*/
public static synchronized void setShareRatio(double shareRatio)
{
infoTable.setValueAt(shareRatio, 8, 1);
}
/**
* Increase num hash fails.
*/
public static synchronized void increaseNumHashFails(int num)
{
infoTable.setValueAt(num, 7, 1);
}
/**
* Adds the progress.
*
* @param toAdd the to add
*/
public static synchronized void addProgress(int toAdd)
{
infoTable.setValueAt((Integer)infoTable.getValueAt(0,1) + toAdd, 0, 1);
}
/**
* Sets the file name.
*
* @param fileName the new file name
*/
public static synchronized void setFileName(String fileName)
{
trackerTable.setValueAt(fileName, 0, 1);
}
/**
* Sets the file length.
*
* @param length the new file length
*/
public static synchronized void setFileLength(int length)
{
trackerTable.setValueAt(length, 1, 1);
}
/**
* Sets the piece length.
*
* @param length the new piece length
*/
public static synchronized void setPieceLength(int length)
{
trackerTable.setValueAt(length, 2, 1);
}
/**
* Sets the num pieces.
*
* @param num the new num pieces
*/
public static synchronized void setNumPieces(int num)
{
trackerTable.setValueAt(num, 3, 1);
}
/**
* Log.
*
* @param text the text
*/
public static synchronized void log(String text)
{
guiLogger.setText(guiLogger.getText() +text + "\n");
}
/**
* Adds the progress bar.
*
* @param progress the progress
*/
public static synchronized void addProgressBar(int progress)
{
progressBar.setValue(progressBar.getValue() + progress);
}
/**
* Toggle progress bar loading.
*/
public static synchronized void toggleProgressBarLoading()
{
progressBar.setIndeterminate(!progressBar.isIndeterminate());
}
/**
* Adds the peer.
*
* @param i the row we want to add to
* @param ip the ip
* @param p the name of the peer
* @param downloadRate the download rate
* @param uploadRate the upload rate
* @param chokeStatus the choke status of the peer
*/
public static synchronized void addPeer(int i, String ip, Peer p, double downloadRate, double uploadRate, boolean chokeStatus)
{
if(i>peerTable.getRowCount()-1) {
( (DefaultTableModel) peerTable.getModel() ).addRow(new Object[]{p, ip, downloadRate, uploadRate, chokeStatus});
}
else {
peerTable.setValueAt(p, i, 0);
peerTable.setValueAt(ip, i, 1);
peerTable.setValueAt(downloadRate, i, 2);
peerTable.setValueAt(uploadRate, i, 3);
peerTable.setValueAt(chokeStatus, i, 4);
}
}
/**
* Update peer down rate.
*
* @param p the peer
* @param downRate the download rate
*/
public static synchronized void updatePeerDownRate(Peer p, double downRate) {
for(int j = 0; j < peerTable.getRowCount(); ++j) {
Object obj1 = getData(peerTable, j, 0);
if(obj1 == p) {
peerTable.setValueAt(downRate, j, 2);
}
}
}
/**
* Update peer up rate.
*
* @param p the peer
* @param d the upload rate
*/
public static synchronized void updatePeerUpRate(Peer p, double d) {
for(int j = 0; j < peerTable.getRowCount(); ++j) {
Object obj1 = getData(peerTable, j, 0);
if(obj1 == p) {
peerTable.setValueAt(d, j, 3);
}
}
}
/**
* Update peer choke status.
*
* @param p the p
* @param choke the choke status of the peer
*/
public static synchronized void updatePeerChokeStatus(Peer p, boolean choke) {
for(int j = 0; j < peerTable.getRowCount(); ++j) {
Object obj1 = getData(peerTable, j, 0);
if(obj1!= null)
{
if(p.equals(obj1)) {
if(choke)
{
peerTable.setValueAt(true, j, 4);
}
else
{
peerTable.setValueAt(false, j, 4);
}
}
}
}
}
/**
* Initialize the contents of the frame.
*/
@SuppressWarnings("serial")
private void initialize() {
clientFrame = new JFrame();
clientFrame.setTitle("RUBT Client - Group 2");
clientFrame.setBounds(100, 100, 744, 559);
clientFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
clientFrame.getContentPane().setLayout(null);
JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.TOP);
tabbedPane.setBounds(10, 11, 461, 445);
clientFrame.getContentPane().add(tabbedPane);
infoTable = new JTable();
infoTable.setFillsViewportHeight(true);
infoTable.setBorder(null);
infoTable.setFont(new Font("Tahoma", Font.PLAIN, 15));
infoTable.setShowVerticalLines(false);
infoTable.setModel(new DefaultTableModel(
new Object[][] {
{"Progress:", new Integer(0)},
{"Download Rate:", null},
{"Upload Rate:", null},
{"Downloaded:", new Integer(0)},
{"Uploaded:", new Integer(0)},
{"Peers:", null},
{"Seeds:", "0"},
{"Wasted(Hashfails):", "0"},
{"Share Ratio:", null},
},
new String[] {
"Description", "New column"
}
) {
boolean[] columnEditables = new boolean[] {
false, false
};
public boolean isCellEditable(int row, int column) {
return columnEditables[column];
}
});
infoTable.getColumnModel().getColumn(0).setPreferredWidth(96);
tabbedPane.addTab("Information", null, infoTable, null);
trackerTable = new JTable();
trackerTable.setFont(new Font("Tahoma", Font.PLAIN, 15));
trackerTable.setModel(new DefaultTableModel(
new Object[][] {
{"File Name", null},
{"Length", null},
{"Piece Length", null},
{"Pieces", null},
},
new String[] {
"Tracker Property", "Value"
}
) {
boolean[] columnEditables = new boolean[] {
false, true
};
public boolean isCellEditable(int row, int column) {
return columnEditables[column];
}
});
tabbedPane.addTab("File Info", null, trackerTable, null);
peerTable = new JTable();
peerTable.setFont(new Font("Tahoma", Font.PLAIN, 14));
peerTable.setBorder(null);
peerTable.setModel(new DefaultTableModel(
new Object[][] {
{"Peer ID", "IP", "Download Rate", "Upload Rate", "Choke Status"},
{null, null, null, null, null},
{null, null, null, null, null},
{null, null, null, null, null},
{null, null, null, null, null},
{null, null, null, null, null},
{null, null, null, null, null},
{null, null, null, null, null},
{null, null, null, null, null},
{null, null, null, null, null},
{null, null, null, null, null},
{null, null, null, null, null},
{null, null, null, null, null},
{null, null, null, null, null},
},
new String[] {
"Peer ID", "IP", "Download Rate", "Upload Rate", "Choke Status"
}
) {
boolean[] columnEditables = new boolean[] {
true, true, false, true, true
};
public boolean isCellEditable(int row, int column) {
return columnEditables[column];
}
});
peerTable.getColumnModel().getColumn(0).setPreferredWidth(140);
peerTable.getColumnModel().getColumn(1).setPreferredWidth(81);
peerTable.getColumnModel().getColumn(2).setPreferredWidth(94);
tabbedPane.addTab("Peer Information", null, peerTable, null);
progressBar = new JProgressBar();
progressBar.setMaximum(55);
progressBar.setBounds(10, 486, 398, 23);
clientFrame.getContentPane().add(progressBar);
JScrollPane scrollPane = new JScrollPane();
scrollPane.setBounds(481, 50, 237, 406);
clientFrame.getContentPane().add(scrollPane);
guiLogger = new JTextPane();
guiLogger.setEditable(false);
scrollPane.setViewportView(guiLogger);
JLabel lblLogger = new JLabel("Logger:");
lblLogger.setFont(new Font("Tahoma", Font.PLAIN, 11));
lblLogger.setBounds(498, 15, 46, 14);
clientFrame.getContentPane().add(lblLogger);
JLabel lblProgress = new JLabel("Progress:");
lblProgress.setFont(new Font("Tahoma", Font.PLAIN, 11));
lblProgress.setBounds(10, 467, 61, 23);
clientFrame.getContentPane().add(lblProgress);
JLabel lblProgressComplete = new JLabel("Complete!");
lblProgress.setFont(new Font("Tahoma", Font.PLAIN, 11));
lblProgress.setBounds(10, 467, 61, 23);
}
/**
* Parses the torrent.
*
* @param torrentFile the torrent file
*/
public void parseTorrent(File torrentFile) {
if((torrentInfo = torrentReader.parseTorrentFile(torrentFile)) == null){
log.severe("Unable to parse torrent file; Exiting.");
return;
} else{
log.info("Torrent file parsed successfully");
}
ToolKit.print(torrentInfo.torrent_file_map);
}
/**
* Sets the gui tracker info.
*
* Updates the tracker info table in the GUI with values
* from the info hash
*/
public static void setGuiTrackerInfo(){
setFileName(torrentInfo.file_name);
setFileLength(torrentInfo.file_length);
setPieceLength(torrentInfo.piece_length);
if(torrentInfo.file_length % torrentInfo.piece_length != 0) {
setNumPieces(torrentInfo.file_length / torrentInfo.piece_length + 1);
progressBar.setMaximum(torrentInfo.file_length / torrentInfo.piece_length + 1);
}
else {
setNumPieces(torrentInfo.file_length / torrentInfo.piece_length);
progressBar.setMaximum(torrentInfo.file_length / torrentInfo.piece_length);
}
}
/**
* Gets the data.
*
* This is used when we are dynamically changing peer data on the
* peer table
*
* @param table the table
* @param row_index the row_index
* @param col_index the col_index
* @return the object
*/
public static Object getData(JTable table, int row_index, int col_index){
return table.getModel().getValueAt(row_index, col_index);
}
public static void onExit() {
String up = Integer.toString(Tracker.uploaded);
try {
File tFile = new File(outputFile.getName() + ".stats");
BufferedWriter out = new BufferedWriter(new FileWriter(tFile));
out.write(up);
out.close();
} catch (IOException e) {
log.severe("Error: Unable to write tracker stats to a file.");
}
System.exit(0);
}
}