package p2pp;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import org.klomp.snark.BitField;
import org.klomp.snark.CoordinatorListener;
import org.klomp.snark.MetaInfo;
import org.klomp.snark.Peer;
import org.klomp.snark.PeerCoordinator;
import org.klomp.snark.Storage;
import org.klomp.snark.TrackerClient;
public class VoDBTPeerCoordinator extends PeerCoordinator {
private final int DELAY = 30;
private final int w2 = 100;
private int w1;
private ArrayList<Integer> inOrderWindow = new ArrayList<Integer>();
private int positionInOrder = 0;
private int positionRarestFirst;
private ArrayList<Integer> requestedPieces = new ArrayList<Integer>();
private WebSeed webSeed;
//Constructor
public VoDBTPeerCoordinator(byte[] id, MetaInfo metainfo, int bitrate,
Storage storage, CoordinatorListener listener,
String serverUrl, boolean useName) throws MalformedURLException {
//Call super constructor
super(id, metainfo, storage, listener);
this.w1 = (DELAY * bitrate) / (8 * metainfo.getPieceLength(0));
System.out.println("In order window length " + w1);
this.positionRarestFirst = positionInOrder + w1;
updateInOrderWindow(positionInOrder);
this.webSeed = new WebSeed(serverUrl, metainfo, useName);
}
@Override
public boolean gotPiece(Peer peer, int piece, byte[] data)
throws IOException {
synchronized(wantedPieces) {
boolean valid = super.gotPiece(peer, piece, data);
if(valid) {
inOrderWindow.remove(new Integer(piece));
if(piece == positionRarestFirst)
positionRarestFirst++;
}
return valid;
}
}
@SuppressWarnings("unchecked")
@Override
public int wantPiece(Peer peer, BitField have) {
synchronized(wantedPieces) {
if(inOrderWindow.isEmpty()) {
for(int piece : wantedPieces)
if(have.get(piece) && positionRarestFirst <= piece &&
piece < positionRarestFirst + w2) {
if(downloadProgress != null)
downloadProgress.pieceRequested(peer, piece);
return piece;
}
}
else {
for(int piece : inOrderWindow) {
if(have.get(piece) && !requestedPieces.contains(piece)) {
requestedPieces.add(piece);
if(downloadProgress != null)
downloadProgress.pieceRequested(peer, piece);
if(requestedPieces.containsAll(inOrderWindow)) {
Thread server = new Thread(
serverDownload((List<Integer>)
inOrderWindow.clone(), positionInOrder));
inOrderWindow.clear();
requestedPieces.clear();
server.start();
}
return piece;
}
}
}
return -1;
}
}
@Override
public void setTracker(TrackerClient tracker) {
super.setTracker(tracker);
Thread order = new Thread(new Runnable() {
@SuppressWarnings("unchecked")
@Override
public void run() {
while(!VoDBTPeerCoordinator.this.completed()) {
try {
Thread.sleep(DELAY * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(wantedPieces) {
requestedPieces.clear();
if(!inOrderWindow.isEmpty()) {
Thread server = new Thread(
serverDownload((List<Integer>)
inOrderWindow.clone(), positionInOrder));
server.start();
}
updateWindows();
}
}
}
});
order.start();
}
private void updateInOrderWindow(int start) {
synchronized(wantedPieces) {
inOrderWindow.clear();
for(int piece : wantedPieces) {
if(start <= piece && piece < start + w1)
inOrderWindow.add(piece);
}
Collections.sort(inOrderWindow);
}
}
private void updateWindows() {
int calc = w1 - (positionRarestFirst - (positionInOrder + w1));
if(calc > 0)
positionRarestFirst += calc;
positionInOrder += w1;
updateInOrderWindow(positionInOrder);
}
/**
* Starts downloading pieces from the server. The pieces are downloaded
* in the order they apear in the list.
* @param pieces List of pieces to download.
* @param window Which window position. Used for logging.
* @return A runnable object whos run method starts the download
* of pieces from server.
*/
private Runnable serverDownload(final List<Integer> pieces,
final int window) {
return new Runnable() {
@Override
public void run() {
log.log(Level.INFO, "Server download session started [window=" +
window + "]");
Iterator<Integer> it = pieces.iterator();
while(it.hasNext()) {
int piece = it.next();
// Check if we still need this piece.
boolean stillWanted = wantedPieces.contains(piece);
if(stillWanted) {
// Download data from server.
byte[] data = getServerPiece(piece);
// If data is null error occurred. What to do? Panic?
if(data != null)
try {
// if gotPiece returns false the downloaded
// piece does not correspond to the hash in
// the torrent. This should not happen!
if(!gotPiece(null, piece, data))
log.log(Level.SEVERE, "Downloaded bad piece "
+ piece + " from web seed " + webSeed);
} catch (IOException e) {
// Ignore. Thrown when trying to abort Snark
// if an error during writing to disk occurred.
}
else
log.log(Level.SEVERE, "Data is null. " +
"Bad downloaded bad block form server!");
}
}
log.log(Level.INFO, "Server download session completed [window="
+ window + "]");
}
};
}
/**
* Retrieves a whole piece from the server.
* @param piece Piece to download.
* @return Returns the raw byte of that piece.
*/
private byte[] getServerPiece(int piece) {
MetaInfo info = getMetaInfo();
// Calculate start index.
long index = piece * info.getPieceLength(0);
try {
// Notify the download listener that we are requesting
// a piece from the server.
synchronized(downloadProgress) {
if(this.downloadProgress != null)
downloadProgress.pieceRequested(null, piece);
}
// Return the bytes.
return webSeed.getBlock(index, info.getPieceLength(piece));
}
catch (IOException e) {
log.log(Level.SEVERE, "Error while reading block from server.", e);
}
// Return null if not able to download block.
return null;
}
}