@ -0,0 +1 @@
|
||||
<mxfile host="app.diagrams.net" modified="2020-03-19T14:50:01.302Z" agent="Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0" etag="dOXguTcOAZ-R9XuIfENW" version="12.8.8" type="device"><diagram name="Page-1" id="13e1069c-82ec-6db2-03f1-153e76fe0fe0">7Vtfc+I2EP80zFwfkrEt28BjQkh60+tdEtJrp2/CFqBGWFSI/LlP3xWWDZbAEJ8hZpo8MNJqvZJ2f1rtSkoL9aYvNwLPJr/zmLCW58QvLXTV8rxO2IFfRXhNCX63mxLGgsYpyV0RBvQH0URHUxc0JvMCo+ScSTorEiOeJCSSBRoWgj8X2UacFXud4TGxCIMIM5v6J43lRFNdx1k1/EroeKK77gS6YYijx7Hgi0T31/LQaPmXNk9xJkvzzyc45s9rJNRvoZ7gXKal6UuPMKXaTG3pd9dbWvNxC5LIfT5oD/EQu5E/jLz2yB86Z14q4QmzhdbFg4A5EaHHK18zHcHQZ6q4mLIvdEQYTaB2OSOCTokEfnTFNPl2Rbt8nlBJBjMcqU+fATxAm8gpg5oLRTCoxPCJyOuM4dmcDpe9OkARJFqIOX0i92Se4kZR+UKqnno5Hpasyg4k1qJyVTtLuVMa6TLDQ8Iuc8P1OOOq+4QvJzSXgj/mKFCCRjDGazylTIH7OxExTrAmayS7YMdLzOg4gUoEplhO3baNNtcTEZK8rJG0rW4IB7WJV2DRrZ6vcaPXVUdXn9dAmmF0sobPIGPEemGMc9ErcEBB42NPrCALKz1GYXLuB1YagBXfaRJW2hZWWuji2/AfZYEPtLw/Wrphk9DSsdAyIAIm8+FZmoAVt1GexQ8tsHzlkmxFSqIad0JAW9X1oaw0QyE4vNC6lHy2pllGRlJ9AKJoMn5QbVdnYcHwztENX79DQHsaGQU12Lj9RzIVf9Hf/h7Iq3+vF/0wnm6ITO/7N58HD/37T79Yxl6zpGm9IZeST6GBJPGFyhUUjfHosWxxSCzGRG5BoB4ZiQtJha3ZNdVlK0EQhiW4kUJnm1Snpd1yCiNbrcSgaCQ3NFbYnC9ERPRX63mAISg37zZBqQIsQUtL5lOsblzfMu4XMC1Q7vt3lYw7hwHLzLwc5Ga0a8qyjzcAYMmil1GnMiA6bwdEWA8gvG7Rjp5TERAo2CFoCyBAofh1jW2mGOYlA+5s7meFr1RirWgLtqNtcPtpRBkBxffUwQEUvWre5V0BuK9H0kZwzl2/bbn3miBp+qg8h32zj3J3CNoCyZWgjJGPRnNyED/WtZB19XnQ+/a9f5+ha54FsgpfacVbr6Bmwi01UjncSiGJ3s0nItMnmmHovgD0TSSbgmryiabv9dERfGK2uKycy7Pw+JFzHT/nCoL3zLk2Asa1APOTAflJbJn1+6fQqSmID8P3DeKzvPNEo/gyr9iEUL4qLKxQ/siw2HD064VMnZnE9AmKY1UshEl3yoGkLNDjGlcjQVQaGqFGux4roulWxFhgxuamoJpCI98r9oPaTum4svYt/AcKpeyLMTO/9Fb55SkE/GWusanQNnbVymmnH+4QVBO0A/PA1by6N8eFSvkPBO3/50GdVvmZc+6gUAs+AGSN232/sjd2dghqwEmJax/CpfkmsmD0kW8eP98MjY2zAfmmfc/X8Hxz9xYanNQWmu0wP72FWoJq2kJDw4G63WNsifZjhVPaEstw2YSstyrmrKx3T8zVBgv7KqDkkulkk4APD1ZvEmAG9f6O/LZTyn8Yj5fNfb/8tqH3p6WnNp09UL3vhVZ+x+o4CBW3JzumquvO1cxZg4q497rd86BcVF0PAYwTpCyQ3Dqydil/Efm2gopzOFyak0feW26E72C5RBNKxCkuk4YfbppZsFuT87cE1XXvaxxuhsc40fE8C6BfASpg1QmB32j5oh8Kj4n6r5rslYKrppXEeR2pNBA/Ee3zAXUXUIQsOGEcK7YIJ/A7JGOanFtAP/rTzK1wf8MjWMP/hBsy5MyAhQy5bYCnvgeS9rHzHo9PTiHyPK33JqbfCaq+wTNPeMMDbb3muWN2j1zR70B19Q9sKfvqnwRR/z8=</diagram></mxfile>
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 29 KiB |
@ -0,0 +1,409 @@
|
||||
package clientP2P;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
import java.io.IOException;
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import exception.LocalException;
|
||||
import exception.RemoteException;
|
||||
import localException.SocketClosed;
|
||||
import localException.ProtocolError;
|
||||
import localException.InternalError;
|
||||
import localException.TransmissionError;
|
||||
import localException.SizeError;
|
||||
import localException.VersionError;
|
||||
import remoteException.EmptyDirectory;
|
||||
import remoteException.EmptyFile;
|
||||
import remoteException.VersionRemoteError;
|
||||
import remoteException.ProtocolRemoteError;
|
||||
import remoteException.NotFound;
|
||||
import remoteException.InternalRemoteError;
|
||||
import remoteException.NotATracker;
|
||||
import protocolP2P.HashAlgorithm;
|
||||
import protocolP2P.HashResponse;
|
||||
import protocolP2P.HashRequest;
|
||||
import protocolP2P.Payload;
|
||||
import protocolP2P.FilePart;
|
||||
import protocolP2P.LoadRequest;
|
||||
import protocolP2P.ProtocolP2PPacket;
|
||||
import clientP2P.ClientDownloadPart;
|
||||
import tools.HostItem;
|
||||
import tools.Logger;
|
||||
import tools.LogLevel;
|
||||
import tools.ServeErrors;
|
||||
|
||||
/** Class to download file
|
||||
* @author Louis Royer
|
||||
* @author Flavien Haas
|
||||
* @author JS Auge
|
||||
* @version 1.0
|
||||
*/
|
||||
public abstract class ClientDownload extends ServeErrors implements Runnable {
|
||||
protected List<HostItem> hostList;
|
||||
protected String filename;
|
||||
protected byte[] hash512;
|
||||
protected List<ClientDownloadPart> sockList = new ArrayList<ClientDownloadPart>();
|
||||
protected List<Long> offsetsToAsk = new ArrayList<Long>();
|
||||
protected List<Long> offsetsPending = new ArrayList<Long>();
|
||||
protected boolean stop;
|
||||
protected long size;
|
||||
protected static final long MAX_PARTIAL_SIZE = 4096;
|
||||
protected String partsSubdir;
|
||||
protected String dirStorage;
|
||||
protected boolean success = false;
|
||||
protected Logger logger;
|
||||
|
||||
/** Constructor with parameters: filename, list of hosts, parts subdirectory and dirStorage
|
||||
* @param filename name of file to download
|
||||
* @param hostList list of servers
|
||||
* @param partsSubdir directory to store .part files
|
||||
* @param dirStorage directory to write assembled file
|
||||
* @param logger Logger
|
||||
*/
|
||||
public ClientDownload(String filename, List<HostItem> hostList, String partsSubdir, String dirStorage, Logger logger) {
|
||||
this.partsSubdir = partsSubdir;
|
||||
this.dirStorage = dirStorage;
|
||||
this.filename = filename;
|
||||
this.hostList = hostList;
|
||||
this.logger = logger;
|
||||
this.stop = false;
|
||||
}
|
||||
|
||||
/** Success getter.
|
||||
* @return true when file have successfully been reassembled.
|
||||
*/
|
||||
public boolean getSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
/** Getter for hash512sum
|
||||
* @return hash512sum
|
||||
*/
|
||||
public byte[] getHashSum512() {
|
||||
return hash512;
|
||||
}
|
||||
|
||||
/** Stop threads */
|
||||
protected void stopTasks() {
|
||||
for(ClientDownloadPart c : sockList) {
|
||||
try {
|
||||
c.setStop();
|
||||
} catch (InterruptedException e) {
|
||||
writeLog(e, LogLevel.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Asks thread to stop
|
||||
*/
|
||||
public void setStop() {
|
||||
stop = true;
|
||||
}
|
||||
|
||||
/** Assign tasks randomly to threads.
|
||||
* @throws InternalError
|
||||
*/
|
||||
protected void assignTasks() throws InternalError {
|
||||
Random rand = new Random();
|
||||
for(long offset : offsetsToAsk) {
|
||||
try {
|
||||
sockList.get(rand.nextInt(sockList.size())).assignTask(offset);
|
||||
offsetsPending.add(offset);
|
||||
System.err.println("Assigned task "+ offset);
|
||||
writeLog("Assigned task "+ offset, LogLevel.Info);
|
||||
} catch(InterruptedException e) {
|
||||
writeLog(e, LogLevel.Error);
|
||||
throw new InternalError();
|
||||
}
|
||||
}
|
||||
offsetsToAsk.removeAll(offsetsPending);
|
||||
}
|
||||
|
||||
/** Create a clientDownloadPart
|
||||
* @param filename name of the file to download
|
||||
* @param hostItem Hostitem of the server
|
||||
*/
|
||||
protected abstract ClientDownloadPart createDownloadPart(String filename, HostItem hostItem);
|
||||
|
||||
/** Starts threads for each server in hostList.
|
||||
*/
|
||||
protected void initThreads() {
|
||||
for(HostItem hostItem: hostList) {
|
||||
sockList.add(createDownloadPart(filename, hostItem));
|
||||
}
|
||||
for(ClientDownloadPart c: sockList) {
|
||||
Thread t = new Thread(c);
|
||||
t.start();
|
||||
}
|
||||
writeLog("Threads initialized", LogLevel.Info);
|
||||
}
|
||||
|
||||
/** Remove tasks from failed threads. Update done status.
|
||||
* @throws InternalError
|
||||
*/
|
||||
protected void checkTasksStatus() throws InternalError {
|
||||
try {
|
||||
synchronized(this) {
|
||||
this.wait();
|
||||
List<ClientDownloadPart> sockListCpy = new ArrayList<>(sockList);
|
||||
for(ClientDownloadPart c: sockListCpy) {
|
||||
if (c.hasFailed() == true) {
|
||||
sockList.remove(c);
|
||||
offsetsPending.removeAll(c.getFailed());
|
||||
offsetsToAsk.addAll(c.getFailed());
|
||||
}
|
||||
try {
|
||||
offsetsPending.removeAll(c.getDone());
|
||||
} catch (InterruptedException e) {
|
||||
throw new InternalError();
|
||||
}
|
||||
}
|
||||
writeLog("Task check status: " + offsetsToAsk.size() + " to asks, " + offsetsPending.size() + " pending", LogLevel.Info);
|
||||
if (offsetsToAsk.isEmpty() && offsetsPending.isEmpty()) {
|
||||
stop = true;
|
||||
}
|
||||
if (sockList.size() == 0) {
|
||||
logger.writeUDP("No thread working", LogLevel.Error);
|
||||
throw new InternalError();
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw new InternalError();
|
||||
}
|
||||
}
|
||||
|
||||
/** Get hashsum from server.
|
||||
* @param hostItem server to ask hash
|
||||
* @return hash512sum
|
||||
* @throws InternalError
|
||||
*/
|
||||
protected byte[] getHashSum512(HostItem hostItem) throws InternalError {
|
||||
byte[] hash;
|
||||
HashAlgorithm[] hashesAlgo = new HashAlgorithm[1];
|
||||
hashesAlgo[0] = HashAlgorithm.SHA512;
|
||||
ProtocolP2PPacket<?> d = createProtocolP2PPacket(new HashRequest(filename, hashesAlgo));
|
||||
try {
|
||||
d.sendRequest(getHostItemSocket(hostItem));
|
||||
try {
|
||||
Payload pHash = d.receiveResponse().getPayload();
|
||||
assert pHash instanceof HashResponse : "This payload must be instance of HashResponse";
|
||||
if (!(pHash instanceof HashResponse)) {
|
||||
throw new InternalError();
|
||||
} else {
|
||||
hash = ((HashResponse)pHash).getHash(HashAlgorithm.SHA512);
|
||||
}
|
||||
} catch (EmptyDirectory e) {
|
||||
writeLog(e, LogLevel.Error);
|
||||
hash = new byte[0];
|
||||
} catch (NotFound e) {
|
||||
writeLog(e, LogLevel.Error);
|
||||
hash = new byte[0];
|
||||
} catch (LocalException e) {
|
||||
writeLog(e, LogLevel.Error);
|
||||
throw new InternalError();
|
||||
} catch (RemoteException e) {
|
||||
writeLog(e, LogLevel.Error);
|
||||
throw new InternalError();
|
||||
}
|
||||
return hash;
|
||||
} catch (IOException e) {
|
||||
writeLog(e, LogLevel.Error);
|
||||
throw new InternalError();
|
||||
} catch (SocketClosed e){
|
||||
System.err.println("getHashSum512 : SocketClosed");
|
||||
writeLog("getHashSum512 : SocketClosed", LogLevel.Error);
|
||||
throw new InternalError();
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes servers not owning the correct file to download from list.
|
||||
* This is done by comparing hash512sum.
|
||||
* @throws InternalError
|
||||
*/
|
||||
protected void purgeList() throws InternalError {
|
||||
List<HostItem> blackList = new ArrayList<HostItem>();
|
||||
boolean first = false;
|
||||
byte[] hashsum;
|
||||
for(HostItem host: hostList) {
|
||||
// already have hashsum from 1st server
|
||||
if (!first) {
|
||||
first = true;
|
||||
continue;
|
||||
}
|
||||
// ask hashsum
|
||||
hashsum = getHashSum512(host);
|
||||
if (!Arrays.equals(hash512, hashsum)) {
|
||||
blackList.add(host);
|
||||
}
|
||||
}
|
||||
// purge list
|
||||
for(HostItem host: blackList) {
|
||||
hostList.remove(host);
|
||||
}
|
||||
writeLog("Host list purge: done", LogLevel.Info);
|
||||
}
|
||||
|
||||
|
||||
/** Reassemble file from file parts.
|
||||
* Set success to true if file is reassembled successfully.
|
||||
*/
|
||||
protected void reassembleFile() {
|
||||
boolean firstPart = true;
|
||||
boolean abort = false;
|
||||
long nextOffset = 0;
|
||||
do {
|
||||
if (firstPart) {
|
||||
writeLog("Reassembling: First part", LogLevel.Info);
|
||||
try {
|
||||
// create file
|
||||
Files.copy(new File(partsSubdir + filename + "_" + nextOffset + ".part").toPath(), new File(dirStorage + filename).toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
nextOffset = (new File(dirStorage + filename)).length();
|
||||
firstPart = false;
|
||||
} catch (IOException e) {
|
||||
writeLog("Reassembling: aborting on first part", LogLevel.Warning);
|
||||
abort = true;
|
||||
}
|
||||
} else if (nextOffset >= size) {
|
||||
success = true;
|
||||
writeLog("Reassembling: success", LogLevel.Info);
|
||||
} else {
|
||||
// append to file
|
||||
try {
|
||||
Files.write(new File(dirStorage + filename).toPath(), Files.readAllBytes(new File(partsSubdir + filename + "_" + nextOffset + ".part").toPath()), StandardOpenOption.APPEND);
|
||||
nextOffset = (new File(dirStorage + filename)).length();
|
||||
} catch (IOException e) {
|
||||
abort = true;
|
||||
writeLog("Aborting: bad number " + nextOffset, LogLevel.Error);
|
||||
}
|
||||
}
|
||||
} while((!success) && (!abort));
|
||||
}
|
||||
|
||||
|
||||
/** Set size of file to download. Also download first file part.
|
||||
* @throws InternalError
|
||||
*/
|
||||
protected void setSize() throws InternalError {
|
||||
ProtocolP2PPacket<?> d = createProtocolP2PPacket(new LoadRequest(filename, 0, MAX_PARTIAL_SIZE));
|
||||
try {
|
||||
d.sendRequest(getHostItemSocket(hostList.get(0)));
|
||||
try {
|
||||
Payload p = d.receiveResponse().getPayload();
|
||||
assert p instanceof FilePart : "This payload must be instance of FilePart";
|
||||
if (!(p instanceof FilePart)) {
|
||||
System.err.println("Error: cannot get size.");
|
||||
writeLog("cannot get size.", LogLevel.Error);
|
||||
throw new InternalError();
|
||||
} else {
|
||||
FilePart fp = (FilePart)p;
|
||||
if (!fp.getFilename().equals(filename)) {
|
||||
System.err.println("Error: wrong file received: `" + fp.getFilename() + "`");
|
||||
writeLog("wrong file received: `" + fp.getFilename() + "`", LogLevel.Error);
|
||||
throw new ProtocolError();
|
||||
}
|
||||
if (fp.getOffset() == 0) {
|
||||
try {
|
||||
Files.write(new File(partsSubdir + filename + "_0.part").toPath(), fp.getPartialContent());
|
||||
} catch (IOException e) {
|
||||
System.err.println("Error: cannot write file (" + partsSubdir + filename + "_0.part)");
|
||||
writeLog("cannot write file (" + partsSubdir + filename + "_0.part)", LogLevel.Error);
|
||||
}
|
||||
size = fp.getTotalSize();
|
||||
if (fp.getPartialContent().length == size) {
|
||||
stop = true;
|
||||
}
|
||||
} else {
|
||||
System.err.println("Error: wrong file part received.");
|
||||
writeLog("wrong file part received.", LogLevel.Error);
|
||||
throw new InternalError();
|
||||
}
|
||||
}
|
||||
} catch (EmptyDirectory e) {
|
||||
System.err.println("Error: empty directory.");
|
||||
writeLog("empty directory.", LogLevel.Error);
|
||||
throw new InternalError();
|
||||
} catch (LocalException e) {
|
||||
writeLog(e, LogLevel.Error);
|
||||
throw new InternalError();
|
||||
} catch (RemoteException e) {
|
||||
writeLog(e, LogLevel.Error);
|
||||
throw new InternalError();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
writeLog(e, LogLevel.Error);
|
||||
throw new InternalError();
|
||||
} catch (SocketClosed e){
|
||||
System.err.println("setSize : SocketClosed");
|
||||
writeLog("setSize : SocketClosed", LogLevel.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/** Close HostItem socket
|
||||
* @param hostItem HostItem
|
||||
*/
|
||||
protected abstract void closeHostItemSocket(HostItem hostItem);
|
||||
|
||||
|
||||
/** Runnable implementation
|
||||
*/
|
||||
public void run() {
|
||||
try {
|
||||
init();
|
||||
if (stop) {
|
||||
writeLog("File is smaller than part max size.", LogLevel.Info);
|
||||
closeHostItemSocket(hostList.get(0));
|
||||
} else {
|
||||
writeLog("File is bigger than part max size.", LogLevel.Info);
|
||||
purgeList();
|
||||
initThreads();
|
||||
while(!stop) {
|
||||
assignTasks();
|
||||
checkTasksStatus();
|
||||
|
||||
}
|
||||
}
|
||||
writeLog("Reassembling file parts.", LogLevel.Info);
|
||||
reassembleFile();
|
||||
} catch(InternalError e) {
|
||||
writeLog("Error while downloading file. Aborting.", LogLevel.Error);
|
||||
} finally {
|
||||
stopTasks();
|
||||
}
|
||||
}
|
||||
|
||||
/** Initialize infos about file to download (size, hash512sum, partslist to dl).
|
||||
* Also download first partfile (to get size).
|
||||
* @throws InternalError
|
||||
*/
|
||||
protected void init() throws InternalError {
|
||||
// get size
|
||||
setSize();
|
||||
|
||||
// get hashsum from 1st server in list
|
||||
hash512 = getHashSum512(hostList.get(0));
|
||||
if (hash512.length == 0) {
|
||||
writeLog("no hash512sum support.", LogLevel.Error);
|
||||
throw new InternalError();
|
||||
}
|
||||
|
||||
// Add tasks
|
||||
if (!stop) {
|
||||
for(long i=MAX_PARTIAL_SIZE; i<size;i+=MAX_PARTIAL_SIZE) {
|
||||
offsetsToAsk.add(Long.valueOf(i));
|
||||
}
|
||||
writeLog("Adding tasks: done", LogLevel.Info);
|
||||
}
|
||||
}
|
||||
|
||||
/** Getter for HostItem socket
|
||||
* @param hostItem HostItem
|
||||
*/
|
||||
protected abstract Object getHostItemSocket(HostItem hostItem);
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,310 @@
|
||||
package clientP2P;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import protocolP2P.ProtocolP2PPacket;
|
||||
import protocolP2P.Payload;
|
||||
import protocolP2P.LoadRequest;
|
||||
import protocolP2P.FilePart;
|
||||
import localException.InternalError;
|
||||
import localException.ProtocolError;
|
||||
import localException.TransmissionError;
|
||||
import localException.VersionError;
|
||||
import localException.SizeError;
|
||||
import localException.SocketClosed;
|
||||
import remoteException.EmptyDirectory;
|
||||
import remoteException.EmptyFile;
|
||||
import remoteException.InternalRemoteError;
|
||||
import remoteException.VersionRemoteError;
|
||||
import remoteException.ProtocolRemoteError;
|
||||
import remoteException.NotFound;
|
||||
import remoteException.NotATracker;
|
||||
import exception.LocalException;
|
||||
import exception.RemoteException;
|
||||
import tools.Logger;
|
||||
import tools.LogLevel;
|
||||
import tools.ServeErrors;
|
||||
|
||||
/** Class to download file parts.
|
||||
* @author Louis Royer
|
||||
* @author Flavien Haas
|
||||
* @author JS Auge
|
||||
* @version 1.0
|
||||
*/
|
||||
public abstract class ClientDownloadPart extends ServeErrors implements Runnable {
|
||||
protected List<Long> toDoTasks;
|
||||
protected List<Long> pendingTasks;
|
||||
protected List<Long> tasksDone;
|
||||
protected volatile boolean tasksListsLock;
|
||||
protected volatile boolean stop;
|
||||
protected volatile boolean failed;
|
||||
protected String filename;
|
||||
protected volatile boolean noTask;
|
||||
protected String partsSubdir;
|
||||
protected static final long MAX_PARTIAL_SIZE = 4096;
|
||||
protected ClientDownload manager;
|
||||
protected Logger logger;
|
||||
|
||||
/** Constructor with filename, socket, and part subdir
|
||||
* @param filename name of file to download
|
||||
* @param socket socket to use
|
||||
* @param partsSubdir directory to store .part files
|
||||
* @param logger Logger
|
||||
*/
|
||||
public ClientDownloadPart(ClientDownload manager, String filename, String partsSubdir, Logger logger) {
|
||||
this.manager = manager;
|
||||
this.partsSubdir = partsSubdir;
|
||||
this.filename = filename;
|
||||
this.logger = logger;
|
||||
stop = false;
|
||||
failed = false;
|
||||
pendingTasks = new ArrayList<>();
|
||||
toDoTasks = new ArrayList<>();
|
||||
tasksDone = new ArrayList<>();
|
||||
noTask = true;
|
||||
tasksListsLock = false;
|
||||
}
|
||||
|
||||
/** True if thread has failed to get a file.
|
||||
* @return true if thread has failed to get a file
|
||||
*/
|
||||
public boolean hasFailed() {
|
||||
return failed;
|
||||
}
|
||||
|
||||
/** Asks to stop thread.
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
public synchronized void setStop() throws InterruptedException {
|
||||
stop = true;
|
||||
this.notifyAll();
|
||||
}
|
||||
|
||||
/** Runnable implementation */
|
||||
public void run() {
|
||||
while(!stop) {
|
||||
try {
|
||||
doTasks();
|
||||
synchronized(manager) {
|
||||
manager.notify();
|
||||
}
|
||||
} catch(InterruptedException e) {
|
||||
try {
|
||||
setStop();
|
||||
synchronized(manager) {
|
||||
manager.notify();
|
||||
}
|
||||
} catch (InterruptedException e2) {
|
||||
}
|
||||
}
|
||||
}
|
||||
writeLog("Closing socket", LogLevel.Info);
|
||||
try{
|
||||
closeSocket();
|
||||
} catch(IOException e){
|
||||
writeLog("can't close socket", LogLevel.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/** Close the socket
|
||||
*/
|
||||
protected abstract void closeSocket() throws IOException;
|
||||
|
||||
/** Get list of offsets that have not be downloaded if failed, else
|
||||
* empty list.
|
||||
* @return list of offsets
|
||||
*/
|
||||
public List<Long> getFailed() {
|
||||
List<Long> ret = new ArrayList<>();
|
||||
if (failed) {
|
||||
ret.addAll(pendingTasks);
|
||||
ret.addAll(toDoTasks);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Get list of downloaded file parts offset, then clear this list.
|
||||
* @return list of offsets
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
public List<Long> getDone() throws InterruptedException {
|
||||
if (tasksDone.size() == 0) {
|
||||
return new ArrayList<>();
|
||||
} else {
|
||||
synchronized (this) {
|
||||
while(tasksListsLock) {
|
||||
this.wait();
|
||||
}
|
||||
tasksListsLock = true;
|
||||
List<Long> ret = new ArrayList<>(tasksDone);
|
||||
tasksDone.clear();
|
||||
tasksListsLock = false;
|
||||
this.notifyAll();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Adds offset of files parts to download.
|
||||
* @param task offset to download
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
public synchronized void assignTask(Long task) throws InterruptedException {
|
||||
synchronized(this) {
|
||||
while(tasksListsLock) {
|
||||
this.wait();
|
||||
}
|
||||
tasksListsLock = true;
|
||||
toDoTasks.add(task);
|
||||
noTask = false;
|
||||
tasksListsLock = false;
|
||||
this.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Send one request and wait for one response. Blocks when no task.
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
public synchronized void doTasks() throws InterruptedException {
|
||||
while(noTask && !stop) {
|
||||
this.wait();
|
||||
}
|
||||
if (!stop) {
|
||||
try {
|
||||
Long offset = toDoTasks.get(0);
|
||||
ProtocolP2PPacket<?> p = reqPart(offset);
|
||||
if (p == null) {
|
||||
stop = true;
|
||||
}
|
||||
|
||||
failed = downloadPart(p);
|
||||
if (failed) {
|
||||
System.err.println("Error: DownloadPart failed.");
|
||||
writeLog("DownloadPart failed.", LogLevel.Error);
|
||||
stop = true;
|
||||
} else if (toDoTasks.isEmpty()) {
|
||||
noTask = true;
|
||||
}
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
writeLog(e, LogLevel.Error);
|
||||
noTask = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Send a request for a specific offset.
|
||||
* @param offset Offset of the file part to download
|
||||
* @return ProtocolP2PPacketTCP used to send request
|
||||
*/
|
||||
protected ProtocolP2PPacket<?> reqPart(Long offset) {
|
||||
writeLog("New request: " + offset, LogLevel.Info);
|
||||
// maintain tracking of tasks
|
||||
if (toDoTasks.contains(offset)) {
|
||||
try {
|
||||
synchronized (this) {
|
||||
while(tasksListsLock) {
|
||||
this.wait();
|
||||
}
|
||||
tasksListsLock = true;
|
||||
toDoTasks.remove(offset);
|
||||
pendingTasks.add(offset);
|
||||
tasksListsLock = false;
|
||||
this.notifyAll();
|
||||
}
|
||||
} catch(InterruptedException e) {
|
||||
writeLog("reqPart interruptedException", LogLevel.Error);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
writeLog("reqPart (offset " + offset + " not in toDoTasks)", LogLevel.Error);
|
||||
return null;
|
||||
}
|
||||
// send request
|
||||
try {
|
||||
ProtocolP2PPacket<?> d = createProtocolP2PPacket(new LoadRequest(filename, offset.longValue(), MAX_PARTIAL_SIZE));
|
||||
d.sendRequest(getSocket());
|
||||
return d;
|
||||
} catch (InternalError e) {
|
||||
writeLog("reqPart internalError", LogLevel.Error);
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
writeLog("reqPart ioexception", LogLevel.Error);
|
||||
writeLog(e, LogLevel.Error);
|
||||
return null;
|
||||
} catch (SocketClosed e){
|
||||
writeLog("reqPart SocketClosed", LogLevel.Error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the socket */
|
||||
protected abstract Object getSocket();
|
||||
|
||||
|
||||
/** Download file part associated to the request send (d).
|
||||
* @param d request packet
|
||||
* @return true on failure, else false
|
||||
*/
|
||||
public < T extends ProtocolP2PPacket<?> > boolean downloadPart(T d) {
|
||||
if (d == null) {
|
||||
writeLog("downloadPart -> d is null.", LogLevel.Error);
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
Payload p = d.receiveResponse().getPayload();
|
||||
assert p instanceof FilePart : "This payload must be instance of FilePart";
|
||||
if (!(p instanceof FilePart)) {
|
||||
writeLog("cannot get size.", LogLevel.Error);
|
||||
return true;
|
||||
} else {
|
||||
FilePart fp = (FilePart)p;
|
||||
if (!fp.getFilename().equals(filename)) {
|
||||
writeLog("wrong file received: `" + fp.getFilename() + "`", LogLevel.Error);
|
||||
return true;
|
||||
}
|
||||
Long offset = Long.valueOf(fp.getOffset());
|
||||
if (pendingTasks.contains(offset)) {
|
||||
try {
|
||||
Files.write(new File(partsSubdir + filename + "_" + offset + ".part").toPath(), fp.getPartialContent());
|
||||
} catch (IOException e) {
|
||||
writeLog("cannot write file (" + partsSubdir + filename + "_" + offset + ".part)", LogLevel.Error);
|
||||
}
|
||||
} else {
|
||||
writeLog("wrong file part received.", LogLevel.Error);
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
synchronized(this) {
|
||||
while(tasksListsLock) {
|
||||
this.wait();
|
||||
}
|
||||
tasksListsLock = true;
|
||||
pendingTasks.remove(offset);
|
||||
tasksDone.add(offset);
|
||||
tasksListsLock = false;
|
||||
this.notifyAll();
|
||||
}
|
||||
} catch(InterruptedException e) {
|
||||
writeLog("DownloadPart Interrupted exception", LogLevel.Error);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (LocalException e) {
|
||||
writeLog(e, LogLevel.Error);
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
writeLog(e, LogLevel.Error);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
System.err.println("Error: downloadPart ioexception");
|
||||
writeLog("downloadPart ioexception", LogLevel.Error);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
package exception;
|
||||
public class InternalError extends Exception {
|
||||
|
||||
public abstract class LocalException extends Exception {
|
||||
private static final long serialVersionUID = 12L;
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
package exception;
|
||||
public class ProtocolError extends Exception {
|
||||
|
||||
public abstract class RemoteException extends Exception {
|
||||
private static final long serialVersionUID = 12L;
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
package exception;
|
||||
public class SocketClosed extends Exception {
|
||||
private static final long serialVersionUID = 12L;
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
package exception;
|
||||
public class TransmissionError extends Exception {
|
||||
private static final long serialVersionUID = 12L;
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
package exception;
|
||||
public class VersionError extends Exception {
|
||||
private static final long serialVersionUID = 12L;
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package localException;
|
||||
|
||||
import exception.LocalException;
|
||||
|
||||
public class InternalError extends LocalException {
|
||||
private static final long serialVersionUID = 12L;
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package localException;
|
||||
|
||||
import exception.LocalException;
|
||||
|
||||
public class ProtocolError extends LocalException {
|
||||
private static final long serialVersionUID = 12L;
|
||||
}
|
@ -1,5 +1,9 @@
|
||||
package exception;
|
||||
package localException;
|
||||
|
||||
import exception.LocalException;
|
||||
|
||||
|
||||
/** Used on reception side when size as set in Packet is too big, and we cant store this in a int/long as usual. */
|
||||
public class SizeError extends Exception {
|
||||
public class SizeError extends LocalException {
|
||||
private static final long serialVersionUID = 12L;
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package localException;
|
||||
|
||||
import exception.LocalException;
|
||||
|
||||
public class SocketClosed extends LocalException {
|
||||
private static final long serialVersionUID = 12L;
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package localException;
|
||||
|
||||
import exception.LocalException;
|
||||
|
||||
public class TransmissionError extends LocalException {
|
||||
private static final long serialVersionUID = 12L;
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package localException;
|
||||
|
||||
import exception.LocalException;
|
||||
|
||||
public class VersionError extends LocalException {
|
||||
private static final long serialVersionUID = 12L;
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
package protocolP2P;
|
||||
import protocolP2P.Payload;
|
||||
import tools.BytesArrayTools;
|
||||
import localException.InternalError;
|
||||
import localException.SizeError;
|
||||
import localException.ProtocolError;
|
||||
import localException.TransmissionError;
|
||||
|
||||
/** Representation of payload for discover request.
|
||||
* @author Louis Royer
|
||||
* @author Flavien Haas
|
||||
* @author JS Auge
|
||||
* @version 1.0
|
||||
*/
|
||||
public class DiscoverRequest extends Payload {
|
||||
|
||||
private String filename;
|
||||
|
||||
/** Constructor with filename (typically used by client). If filename is null, it is initialized with "".
|
||||
* @param filename Name of the file you want a server list of.
|
||||
* @throws InternalError
|
||||
*/
|
||||
public DiscoverRequest(String filename) throws InternalError {
|
||||
super(RequestResponseCode.DISCOVER_REQUEST);
|
||||
if (filename == null) {
|
||||
this.filename = "";
|
||||
} else {
|
||||
this.filename = filename;
|
||||
}
|
||||
}
|
||||
|
||||
/** Constructor (typically used by server) with a byte[] parameter containing the Packet received.
|
||||
* @param packet the full Packet received
|
||||
* @throws SizeError
|
||||
* @throws InternalError
|
||||
* @throws ProtocolError
|
||||
* @throws TransmissionError
|
||||
*/
|
||||
protected DiscoverRequest(byte[] packet) throws SizeError, ProtocolError, InternalError, TransmissionError {
|
||||
super(packet);
|
||||
int size = getPayloadSize(packet);
|
||||
filename = BytesArrayTools.readString(packet, Payload.PAYLOAD_START_POSITION, size);
|
||||
}
|
||||
|
||||
/** Returns a byte[] containing Packet with padding.
|
||||
* This Packet is still incomplete and should not be send directly.
|
||||
* ProtocolP2PPacket will use this method to generate the complete Packet.
|
||||
* @return Packet with padding
|
||||
* @throws InternalError
|
||||
*/
|
||||
protected byte[] toPacket() throws InternalError {
|
||||
// compute total size
|
||||
int size = PAYLOAD_START_POSITION + filename.length();
|
||||
byte[] packet = new byte[size + 1]; // java initialize all to zero
|
||||
// set request/response code
|
||||
packet[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
|
||||
// set Payload size
|
||||
setPayloadSize(size - PAYLOAD_START_POSITION, packet);
|
||||
// write filename to Packet
|
||||
BytesArrayTools.write(packet, filename, PAYLOAD_START_POSITION);
|
||||
return packet;
|
||||
}
|
||||
|
||||
/** Filename getter.
|
||||
* @return filename
|
||||
*/
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
package protocolP2P;
|
||||
import protocolP2P.Payload;
|
||||
import tools.HostItem;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import localException.InternalError;
|
||||
import localException.SizeError;
|
||||
import localException.ProtocolError;
|
||||
import localException.TransmissionError;
|
||||
import tools.BytesArrayTools;
|
||||
|
||||
/** Representation of payload for discover response.
|
||||
* @author Louis Royer
|
||||
* @author Flavien Haas
|
||||
* @author JS Auge
|
||||
* @version 1.0
|
||||
*/
|
||||
public class DiscoverResponse extends Payload {
|
||||
|
||||
private List<HostItem> hostList;
|
||||
private String filename;
|
||||
private static final int FILENAME_SIZE_POSITION = PAYLOAD_START_POSITION;
|
||||
private static final int FILENAME_POSITION = FILENAME_SIZE_POSITION + 4;
|
||||
|
||||
/** Constructor with filename (typically used by tracker). If filename is null, it is initialized with "".
|
||||
* @param filename Name of the file related to the server list.
|
||||
* @param hostList List of servers
|
||||
* @throws InternalError
|
||||
*/
|
||||
public DiscoverResponse(String filename, List<HostItem> hostList) throws InternalError {
|
||||
super(RequestResponseCode.DISCOVER_RESPONSE);
|
||||
this.filename = filename;
|
||||
this.hostList = hostList;
|
||||
}
|
||||
|
||||
/** Constructor (typically used by server) with a byte[] parameter containing the Packet received.
|
||||
* @param packet the full Packet received
|
||||
* @throws SizeError
|
||||
* @throws InternalError
|
||||
* @throws ProtocolError
|
||||
* @throws TransmissionError
|
||||
*/
|
||||
protected DiscoverResponse(byte[] packet) throws SizeError, ProtocolError, InternalError, TransmissionError {
|
||||
super(packet);
|
||||
int size = getPayloadSize(packet);
|
||||
/* Read filename size */
|
||||
int filenameSize = BytesArrayTools.readInt(packet, FILENAME_SIZE_POSITION);
|
||||
|
||||
/* Read filename */
|
||||
filename = BytesArrayTools.readString(packet, FILENAME_POSITION, filenameSize);
|
||||
|
||||
// TODO
|
||||
hostList = new ArrayList<>();
|
||||
int i = FILENAME_POSITION + filenameSize;
|
||||
while(i<size) {
|
||||
int port = BytesArrayTools.readInt16Bits(packet, i);
|
||||
i += 2;
|
||||
String hostname = BytesArrayTools.readString(packet, i, "\n");
|
||||
i += hostname.length();
|
||||
hostList.add(new HostItem(hostname, port));
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns a byte[] containing Packet with padding.
|
||||
* This Packet is still incomplete and should not be send directly.
|
||||
* ProtocolP2PPacket will use this method to generate the complete Packet.
|
||||
* @return Packet with padding
|
||||
* @throws InternalError
|
||||
*/
|
||||
protected byte[] toPacket() throws InternalError {
|
||||
int filenameSize = filename.length();
|
||||
int hostListSize = 0;
|
||||
for (HostItem hostItem: hostList) {
|
||||
hostListSize += (2 + hostItem.getHostname().length() + 1);
|
||||
}
|
||||
// compute total size
|
||||
int size = FILENAME_POSITION + filename.length() + hostListSize;
|
||||
byte[] packet = new byte[size + 1]; // java initialize all to zero
|
||||
// set request/response code
|
||||
packet[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
|
||||
// set Payload size
|
||||
setPayloadSize(size - PAYLOAD_START_POSITION, packet);
|
||||
// write filename size
|
||||
BytesArrayTools.write(packet, FILENAME_SIZE_POSITION, filenameSize);
|
||||
// write filename
|
||||
BytesArrayTools.write(packet, filename, FILENAME_POSITION);
|
||||
|
||||
int i = FILENAME_POSITION + filename.length();
|
||||
// write hostList
|
||||
for(HostItem hostItem: hostList) {
|
||||
try {
|
||||
BytesArrayTools.write16Bits(packet, i, hostItem.getPort());
|
||||
i+=2;
|
||||
String hostname = hostItem.getHostname() + "\n";
|
||||
BytesArrayTools.write(packet, hostname, i);
|
||||
i+=hostname.length();
|
||||
} catch (SizeError e) {
|
||||
throw new InternalError();
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
/** HostList getter.
|
||||
* @return hostList
|
||||
*/
|
||||
public List<HostItem> getHostList() {
|
||||
return hostList;
|
||||
}
|
||||
|
||||
/** Filename getter.
|
||||
* @return filename
|
||||
*/
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
package protocolP2P;
|
||||
import protocolP2P.Payload;
|
||||
import tools.HostItem;
|
||||
import tools.BytesArrayTools;
|
||||
import localException.SizeError;
|
||||
import localException.InternalError;
|
||||
import localException.TransmissionError;
|
||||
import localException.ProtocolError;
|
||||
|
||||
/** Representation of payload for unregister.
|
||||
* @author Louis Royer
|
||||
* @author Flavien Haas
|
||||
* @author JS Auge
|
||||
* @version 1.0
|
||||
*/
|
||||
public class Register extends Payload {
|
||||
private HostItem hostItem;
|
||||
private static final int HOSTNAME_START_POSITION = PAYLOAD_START_POSITION + 2;
|
||||
|
||||
/** Constructor with hostItem (typically used by client)
|
||||
* @param hostItem Host you want to register.
|
||||
* @throws InternalError
|
||||
*/
|
||||
public Register(HostItem hostItem) throws InternalError {
|
||||
super(RequestResponseCode.REGISTER);
|
||||
this.hostItem = hostItem;
|
||||
}
|
||||
|
||||
/** Constructor (typically used by server) with a byte[] parameter containing the Packet received.
|
||||
* @param packet the full Packet received
|
||||
* @throws SizeError
|
||||
* @throws InternalError
|
||||
* @throws ProtocolError
|
||||
* @throws TransmissionError
|
||||
*/
|
||||
protected Register(byte[] packet) throws SizeError, ProtocolError, InternalError, TransmissionError {
|
||||
super(packet);
|
||||
int size = getPayloadSize(packet);
|
||||
int port = BytesArrayTools.readInt16Bits(packet, PAYLOAD_START_POSITION);
|
||||
String hostname = BytesArrayTools.readString(packet, HOSTNAME_START_POSITION, size - HOSTNAME_START_POSITION + PAYLOAD_START_POSITION);
|
||||
hostItem = new HostItem(hostname, port);
|
||||
}
|
||||
|
||||
/** Returns a byte[] containing Packet with padding.
|
||||
* This Packet is still incomplete and should not be send directly.
|
||||
* ProtocolP2PPacket will use this method to generate the complete Packet.
|
||||
* @return Packet with padding
|
||||
* @throws InternalError
|
||||
*/
|
||||
protected byte[] toPacket() throws InternalError {
|
||||
String hostname = hostItem.getHostname();
|
||||
// compute total size
|
||||
int size = HOSTNAME_START_POSITION + hostname.length();
|
||||
byte[] packet = new byte[size + 1]; // java initialize all to zero
|
||||
// set request/response code
|
||||
packet[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
|
||||
// set Payload size
|
||||
setPayloadSize(size - PAYLOAD_START_POSITION, packet);
|
||||
// write port to Packet
|
||||
try {
|
||||
BytesArrayTools.write16Bits(packet, PAYLOAD_START_POSITION, hostItem.getPort());
|
||||
} catch (SizeError e) {
|
||||
throw new InternalError();
|
||||
}
|
||||
// write hostname to Packet
|
||||
BytesArrayTools.write(packet, hostname, HOSTNAME_START_POSITION);
|
||||
return packet;
|
||||
}
|
||||
|
||||
/** HostItem getter.
|
||||
* @return hostItem
|
||||
*/
|
||||
public HostItem getHostItem() {
|
||||
return hostItem;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
package protocolP2P;
|
||||
import protocolP2P.Payload;
|
||||
import tools.HostItem;
|
||||
import tools.BytesArrayTools;
|
||||
import localException.SizeError;
|
||||
import localException.InternalError;
|
||||
import localException.TransmissionError;
|
||||
import localException.ProtocolError;
|
||||
|
||||
/** Representation of payload for unregister.
|
||||
* @author Louis Royer
|
||||
* @author Flavien Haas
|
||||
* @author JS Auge
|
||||
* @version 1.0
|
||||
*/
|
||||
public class Unregister extends Payload {
|
||||
private HostItem hostItem;
|
||||
private static final int HOSTNAME_START_POSITION = PAYLOAD_START_POSITION + 2;
|
||||
|
||||
/** Constructor with hostItem (typically used by client)
|
||||
* @param hostItem Host you want to register.
|
||||
* @throws InternalError
|
||||
*/
|
||||
public Unregister(HostItem hostItem) throws InternalError {
|
||||
super(RequestResponseCode.UNREGISTER);
|
||||
this.hostItem = hostItem;
|
||||
}
|
||||
|
||||
/** Constructor (typically used by server) with a byte[] parameter containing the Packet received.
|
||||
* @param packet the full Packet received
|
||||
* @throws SizeError
|
||||
* @throws InternalError
|
||||
* @throws ProtocolError
|
||||
* @throws TransmissionError
|
||||
*/
|
||||
protected Unregister(byte[] packet) throws SizeError, ProtocolError, InternalError, TransmissionError {
|
||||
super(packet);
|
||||
int size = getPayloadSize(packet);
|
||||
int port = BytesArrayTools.readInt16Bits(packet, PAYLOAD_START_POSITION);
|
||||
String hostname = BytesArrayTools.readString(packet, HOSTNAME_START_POSITION, size - HOSTNAME_START_POSITION + PAYLOAD_START_POSITION);
|
||||
hostItem = new HostItem(hostname, port);
|
||||
}
|
||||
|
||||
/** Returns a byte[] containing Packet with padding.
|
||||
* This Packet is still incomplete and should not be send directly.
|
||||
* ProtocolP2PPacket will use this method to generate the complete Packet.
|
||||
* @return Packet with padding
|
||||
* @throws InternalError
|
||||
*/
|
||||
protected byte[] toPacket() throws InternalError {
|
||||
String hostname = hostItem.getHostname();
|
||||
// compute total size
|
||||
int size = HOSTNAME_START_POSITION + hostname.length();
|
||||
byte[] packet = new byte[size + 1]; // java initialize all to zero
|
||||
// set request/response code
|
||||
packet[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
|
||||
// set Payload size
|
||||
setPayloadSize(size - PAYLOAD_START_POSITION, packet);
|
||||
// write port to Packet
|
||||
try {
|
||||
BytesArrayTools.write16Bits(packet, PAYLOAD_START_POSITION, hostItem.getPort());
|
||||
} catch (SizeError e) {
|
||||
throw new InternalError();
|
||||
}
|
||||
// write hostname to Packet
|
||||
BytesArrayTools.write(packet, hostname, HOSTNAME_START_POSITION);
|
||||
return packet;
|
||||
}
|
||||
|
||||
/** HostItem getter.
|
||||
* @return hostItem
|
||||
*/
|
||||
public HostItem getHostItem() {
|
||||
return hostItem;
|
||||
}
|
||||
|
||||
}
|
@ -1,4 +1,7 @@
|
||||
package remoteException;
|
||||
public class EmptyDirectory extends Exception {
|
||||
|
||||
import exception.RemoteException;
|
||||
|
||||
public class EmptyDirectory extends RemoteException {
|
||||
private static final long serialVersionUID = 12L;
|
||||
}
|
||||
|
@ -1,4 +1,7 @@
|
||||
package remoteException;
|
||||
public class EmptyFile extends Exception {
|
||||
|
||||
import exception.RemoteException;
|
||||
|
||||
public class EmptyFile extends RemoteException {
|
||||
private static final long serialVersionUID = 12L;
|
||||
}
|
||||
|
@ -1,4 +1,7 @@
|
||||
package remoteException;
|
||||
public class InternalRemoteError extends Exception {
|
||||
|
||||
import exception.RemoteException;
|
||||
|
||||
public class InternalRemoteError extends RemoteException {
|
||||
private static final long serialVersionUID = 12L;
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
package remoteException;
|
||||
|
||||
import exception.RemoteException;
|
||||
|
||||
public class NotATracker extends RemoteException {
|
||||
private static final long serialVersionUID = 12L;
|
||||
}
|
@ -1,4 +1,7 @@
|
||||
package remoteException;
|
||||
public class NotFound extends Exception {
|
||||
|
||||
import exception.RemoteException;
|
||||
|
||||
public class NotFound extends RemoteException {
|
||||
private static final long serialVersionUID = 12L;
|
||||
}
|
||||
|
@ -1,4 +1,7 @@
|
||||
package remoteException;
|
||||
public class ProtocolRemoteError extends Exception {
|
||||
|
||||
import exception.RemoteException;
|
||||
|
||||
public class ProtocolRemoteError extends RemoteException {
|
||||
private static final long serialVersionUID = 12L;
|
||||
}
|
||||
|
@ -1,4 +1,7 @@
|
||||
package remoteException;
|
||||
public class VersionRemoteError extends Exception {
|
||||
|
||||
import exception.RemoteException;
|
||||
|
||||
public class VersionRemoteError extends RemoteException {
|
||||
private static final long serialVersionUID = 12L;
|
||||
}
|
||||
|
@ -0,0 +1,156 @@
|
||||
package serverP2P;
|
||||
import tools.Logger;
|
||||
import tools.LogLevel;
|
||||
import tools.HostItem;
|
||||
import java.io.File;
|
||||
import java.util.Vector;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import protocolP2P.HashAlgorithm;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.io.IOException;
|
||||
|
||||
/** Class allowing to keep the tracker informed about file list
|
||||
* @author Louis Royer
|
||||
* @author Flavien Haas
|
||||
* @author JS Auge
|
||||
* @version 1.0
|
||||
*/
|
||||
public abstract class FileWatcher implements Runnable {
|
||||
protected String[] fileList = new String[0];
|
||||
protected Logger logger;
|
||||
protected volatile boolean stop;
|
||||
protected long time;
|
||||
protected boolean force;
|
||||
protected HostItem server;
|
||||
protected HostItem tracker;
|
||||
protected String baseDirectory;
|
||||
protected Map<String, byte[]> sha512 = new HashMap<>();
|
||||
|
||||
|
||||
/** Constructor
|
||||
* @param logger Logger
|
||||
* @param millis Time interval before recheck
|
||||
* @param server HostItem for the server
|
||||
* @param tracker HostItem for the tracker
|
||||
* @param baseDirectory Directory to search files
|
||||
*/
|
||||
public FileWatcher(Logger logger, long millis, HostItem server, HostItem tracker, String baseDirectory) {
|
||||
assert logger != null : "Logger is null";
|
||||
assert server != null : "Server is null";
|
||||
assert tracker != null : "Tracker is null";
|
||||
assert baseDirectory != null : "baseDirectory is null";
|
||||
this.logger = logger;
|
||||
time = millis;
|
||||
this.server = server;
|
||||
this.tracker = tracker;
|
||||
this.baseDirectory = baseDirectory;
|
||||
}
|
||||
|
||||
/** FileList getter
|
||||
* @return fileList
|
||||
*/
|
||||
public String[] getFileList() {
|
||||
return fileList;
|
||||
}
|
||||
|
||||
/** Sha512 map getter
|
||||
* @return sha512 hashmap
|
||||
*/
|
||||
public Map<String, byte[]> getSha512Map() {
|
||||
return sha512;
|
||||
}
|
||||
|
||||
/** Allow a manual check
|
||||
*/
|
||||
public void trigger() {
|
||||
if (updateFileList() || force) {
|
||||
force = false;
|
||||
writeLog("File list watcher detected changes. Informing tracker.", LogLevel.Info);
|
||||
registerTracker();
|
||||
}
|
||||
}
|
||||
|
||||
/** Runnable implementation */
|
||||
public void run() {
|
||||
writeLog("File list watcher started : delay " + time + " milliseconds.", LogLevel.Info);
|
||||
while(!stop) {
|
||||
trigger();
|
||||
try {
|
||||
Thread.sleep(time);
|
||||
} catch(InterruptedException e) {
|
||||
writeLog("File list watcher interrupted", LogLevel.Error);
|
||||
setStop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Register server on tracker
|
||||
*/
|
||||
protected abstract void registerTracker();
|
||||
|
||||
/** Update fileList and returns true if different than old list.
|
||||
* @return true if changed
|
||||
*/
|
||||
protected boolean updateFileList() {
|
||||
File folder = new File(baseDirectory);
|
||||
Vector<String> v = new Vector<String>();
|
||||
File[] files = folder.listFiles();
|
||||
/* Add non-recursively files's names to fileList */
|
||||
for (File f : files) {
|
||||
if (f.isFile()) {
|
||||
v.add(f.getName());
|
||||
}
|
||||
}
|
||||
String[] newFileList = new String[v.size()];
|
||||
v.toArray(newFileList);
|
||||
Arrays.sort(newFileList);
|
||||
if (!Arrays.equals(newFileList, fileList)) {
|
||||
fileList = newFileList;
|
||||
initSha512();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Ask the thread to stop
|
||||
*/
|
||||
public void setStop() {
|
||||
stop = true;
|
||||
}
|
||||
|
||||
/** Init sha512 map.
|
||||
*/
|
||||
protected void initSha512() {
|
||||
for(String f: fileList) {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance(HashAlgorithm.SHA512.getName());
|
||||
sha512.put(f, md.digest(Files.readAllBytes(Paths.get(baseDirectory + f))));
|
||||
md.reset();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
writeLog("sha512 not supported", LogLevel.Error);
|
||||
} catch (IOException e) {
|
||||
writeLog("cannot read " + f, LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Implementation of writeLog
|
||||
* @param text Text to log
|
||||
* @param logLevel level of logging
|
||||
*/
|
||||
protected abstract void writeLog(String text, LogLevel logLevel);
|
||||
|
||||
/** Implementation of writeLog
|
||||
* @param e exception to log
|
||||
* @param logLevel level of logging
|
||||
*/
|
||||
protected abstract void writeLog(Exception e, LogLevel logLevel);
|
||||
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package serverP2P;
|
||||
import tools.Logger;
|
||||
import tools.LogLevel;
|
||||
import protocolP2P.ProtocolP2PPacket;
|
||||
import protocolP2P.ProtocolP2PPacketTCP;
|
||||
import protocolP2P.Register;
|
||||
import protocolP2P.Payload;
|
||||
import tools.HostItem;
|
||||
|
||||
/** Class allowing to keep the tracker informed about file list (TCP impl.)
|
||||
* @author Louis Royer
|
||||
* @author Flavien Haas
|
||||
* @author JS Auge
|
||||
* @version 1.0
|
||||
*/
|
||||
public class FileWatcherTCP extends FileWatcher {
|
||||
|
||||
/** Constructor
|
||||
* @param logger Logger
|
||||
* @param millis Time interval before recheck
|
||||
* @param server HostItem for the server
|
||||
* @param tracker HostItem for the tracker
|
||||
* @param baseDirectory Directory to search files
|
||||
*/
|
||||
public FileWatcherTCP(Logger logger, long millis, HostItem server, HostItem tracker, String baseDirectory) {
|
||||
super(logger, millis, server, tracker, baseDirectory);
|
||||
assert logger != null : "Logger is null";
|
||||
assert server != null : "Server is null";
|
||||
assert tracker != null : "Tracker is null";
|
||||
assert baseDirectory != null : "baseDirectory is null";
|
||||
}
|
||||
|
||||
/** Implementation of writeLog
|
||||
* @param text Text to log
|
||||
* @param logLevel level of logging
|
||||
*/
|
||||
protected void writeLog(String text, LogLevel logLevel) {
|
||||
logger.writeTCP(text, logLevel);
|
||||
}
|
||||
|
||||
/** Implementation of writeLog
|
||||
* @param e exception to log
|
||||
* @param logLevel level of logging
|
||||
*/
|
||||
protected void writeLog(Exception e, LogLevel logLevel) {
|
||||
logger.writeTCP(e, logLevel);
|
||||
}
|
||||
|
||||
/** Register server on tracker
|
||||
*/
|
||||
protected void registerTracker() {
|
||||
try {
|
||||
writeLog("Trying to into tracker", LogLevel.Info);
|
||||
ProtocolP2PPacket<Register> p = (ProtocolP2PPacket<Register>)new ProtocolP2PPacketTCP<Register>(new Register(server));
|
||||
p.sendRequest((Object)tracker.tryGetTCPSocket());
|
||||
writeLog("Register request sent.", LogLevel.Debug);
|
||||
tracker.closeTCPSocket();
|
||||
} catch (Exception e) {
|
||||
// error, trying again at next iteration
|
||||
force = true;
|
||||
writeLog("Cannot contact tracker, trying again at next iteration (" + time + " milliseconds).", LogLevel.Error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package serverP2P;
|
||||
import tools.Logger;
|
||||
import tools.LogLevel;
|
||||
import protocolP2P.ProtocolP2PPacket;
|
||||
import protocolP2P.ProtocolP2PPacketUDP;
|
||||
import protocolP2P.Register;
|
||||
import protocolP2P.Payload;
|
||||
import tools.HostItem;
|
||||
|
||||
/** Class allowing to keep the tracker informed about file list (UDP impl.)
|
||||
* @author Louis Royer
|
||||
* @author Flavien Haas
|
||||
* @author JS Auge
|
||||
* @version 1.0
|
||||
*/
|
||||
public class FileWatcherUDP extends FileWatcher {
|
||||
|
||||
/** Constructor
|
||||
* @param logger Logger
|
||||
* @param millis Time interval before recheck
|
||||
* @param server HostItem for the server
|
||||
* @param tracker HostItem for the tracker
|
||||
* @param baseDirectory Directory to search files
|
||||
*/
|
||||
public FileWatcherUDP(Logger logger, long millis, HostItem server, HostItem tracker, String baseDirectory) {
|
||||
super(logger, millis, server, tracker, baseDirectory);
|
||||
assert logger != null : "Logger is null";
|
||||
assert server != null : "Server is null";
|
||||
assert tracker != null : "Tracker is null";
|
||||
assert baseDirectory != null : "baseDirectory is null";
|
||||
}
|
||||
|
||||
/** Implementation of writeLog
|
||||
* @param text Text to log
|
||||
* @param logLevel level of logging
|
||||
*/
|
||||
protected void writeLog(String text, LogLevel logLevel) {
|
||||
logger.writeUDP(text, logLevel);
|
||||
}
|
||||
|
||||
/** Implementation of writeLog
|
||||
* @param e exception to log
|
||||
* @param logLevel level of logging
|
||||
*/
|
||||
protected void writeLog(Exception e, LogLevel logLevel) {
|
||||
logger.writeUDP(e, logLevel);
|
||||
}
|
||||
|
||||
/** Register server on tracker
|
||||
*/
|
||||
protected void registerTracker() {
|
||||
try {
|
||||
writeLog("Trying to register into tracker", LogLevel.Info);
|
||||
ProtocolP2PPacket<Register> p = (ProtocolP2PPacket<Register>)new ProtocolP2PPacketUDP<Register>(new Register(server));
|
||||
p.sendRequest((Object)tracker.getUDPSocket());
|
||||
writeLog("Register request sent (but cannot ensure reception).", LogLevel.Debug);
|
||||
tracker.closeUDPSocket();
|
||||
} catch (Exception e) {
|
||||
force = true;
|
||||
writeLog("Cannot contact tracker, trying again at next iteration (" + time + " milliseconds).", LogLevel.Error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,234 @@
|
||||
package serverP2P;
|
||||
import serverP2P.FileWatcher;
|
||||
import tools.Logger;
|
||||
import tools.LogLevel;
|
||||
import tools.HostItem;
|
||||
import tools.ServeErrors;
|
||||
import protocolP2P.ProtocolP2PPacket;
|
||||
import protocolP2P.Payload;
|
||||
import protocolP2P.RequestResponseCode;
|
||||
import protocolP2P.FileList;
|
||||
import protocolP2P.FilePart;
|
||||
import protocolP2P.LoadRequest;
|
||||
import protocolP2P.HashRequest;
|
||||
import protocolP2P.HashResponse;
|
||||
import protocolP2P.HashAlgorithm;
|
||||
import protocolP2P.Unregister;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.io.IOException;
|
||||
import exception.LocalException;
|
||||
|
||||
/** Implementation of P2P-JAVA-PROJECT VERSION 1.0 protocol.
|
||||
* @author Louis Royer
|
||||
* @author Flavien Haas
|
||||
* @author JS Auge
|
||||
* @version 1.0
|
||||
*/
|
||||
public abstract class ServerManagement extends ServeErrors implements Runnable {
|
||||
|
||||
protected volatile boolean stop;
|
||||
protected FileWatcher fileListWatcher;
|
||||
protected Logger logger;
|
||||
protected String baseDirectory;
|
||||
protected HostItem server;
|
||||
protected HostItem tracker;
|
||||
|
||||
/** Constructor */
|
||||
public ServerManagement(String baseDirectory, HostItem server, HostItem tracker, Logger logger) {
|
||||
super();
|
||||
assert baseDirectory != null : "baseDirectory is null";
|
||||
assert server != null : "server is null";
|
||||
assert tracker != null : "tracker is null";
|
||||
assert logger != null : "logger is null";
|
||||
stop = false;
|
||||
this.baseDirectory = baseDirectory;
|
||||
this.server = server;
|
||||
this.tracker = tracker;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
/** Stop the thread */
|
||||
public void setStop() {
|
||||
stop = true;
|
||||
}
|
||||
|
||||
/** Trigger a manual check of the file list
|
||||
*/
|
||||
public void updateFileList() {
|
||||
if (fileListWatcher != null) {
|
||||
fileListWatcher.trigger();
|
||||
}
|
||||
}
|
||||
|
||||
/** Send response to list request
|
||||
* @param pd Request received
|
||||
*/
|
||||
protected < T extends ProtocolP2PPacket<?> > void sendListResponse(T pd) {
|
||||
try {
|
||||
String[] fileList = fileListWatcher.getFileList();
|
||||
if (fileList.length == 0) {
|
||||
writeLog("Sending EMPTY_DIRECTORY to host " + pd.getHostItem(), LogLevel.Action);
|
||||
sendEmptyDirectory(pd);
|
||||
} else {
|
||||
writeLog("Sending LIST_RESPONSE to host " + pd.getHostItem(), LogLevel.Action);
|
||||
pd.sendResponse(createProtocolP2PPacket((Payload)(new FileList(fileList))));
|
||||
}
|
||||
} catch (Exception e2) {
|
||||
writeLog(e2, LogLevel.Error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Send hash response to hash request
|
||||
* @param pd Request received
|
||||
*/
|
||||
protected < T extends ProtocolP2PPacket<?> > void sendHashResponse(T pd) {
|
||||
Payload p = pd.getPayload();
|
||||
assert p instanceof HashRequest : "payload must be an instance of HashRequest";
|
||||
if (!(p instanceof HashRequest)) {
|
||||
sendInternalError(pd);
|
||||
} else {
|
||||
String filename = ((HashRequest)p).getFilename();
|
||||
if (Arrays.binarySearch(fileListWatcher.getFileList(), filename) >= 0) {
|
||||
Map<HashAlgorithm, byte[]> hashes = new HashMap<>();
|
||||
for (HashAlgorithm h : ((HashRequest)p).getAlgoList()) {
|
||||
switch (h) {
|
||||
case SHA512:
|
||||
hashes.put(h, fileListWatcher.getSha512Map().get(filename));
|
||||
break;
|
||||
case MD5:
|
||||
default:
|
||||
hashes.put(h, new byte[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
try {
|
||||
pd.sendResponse(createProtocolP2PPacket((Payload)(new HashResponse(filename, hashes))));
|
||||
} catch (Exception e) {
|
||||
writeLog(e, LogLevel.Error);
|
||||
}
|
||||
} else {
|
||||
// file not found
|
||||
try {
|
||||
sendNotFound(pd);
|
||||
} catch (Exception e) {
|
||||
writeLog(e, LogLevel.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Send response to load request
|
||||
* @param pd Request received
|
||||
*/
|
||||
protected < T extends ProtocolP2PPacket<?> > void sendLoadResponse(T pd) {
|
||||
Payload p = pd.getPayload();
|
||||
assert p instanceof LoadRequest : "payload must be an instance of LoadRequest";
|
||||
if (!(p instanceof LoadRequest)) {
|
||||
sendInternalError(pd);
|
||||
} else {
|
||||
String filename = ((LoadRequest)p).getFilename();
|
||||
long offset = ((LoadRequest)p).getOffset();
|
||||
long maxSizePartialContent = ((LoadRequest)p).getMaxSizePartialContent();
|
||||
try {
|
||||
byte[] fullLoad = Files.readAllBytes(Paths.get(baseDirectory + filename));
|
||||
long sizeToSend = 0;
|
||||
if (fullLoad.length - offset < maxSizePartialContent) {
|
||||
writeLog("Sending last partialContent", LogLevel.Debug);
|
||||
sizeToSend = fullLoad.length - offset;
|
||||
} else {
|
||||
sizeToSend = maxSizePartialContent;
|
||||
}
|
||||
writeLog("maxSizePartialContent: " + maxSizePartialContent, LogLevel.Debug);
|
||||
writeLog("Sending " + filename + " from " + offset + " to " + (offset + sizeToSend), LogLevel.Debug);
|
||||
byte[] load = Arrays.copyOfRange(fullLoad, (int)offset, (int)(offset + sizeToSend));
|
||||
String[] fileList = fileListWatcher.getFileList();
|
||||
if (Arrays.binarySearch(fileList, filename) >= 0) {
|
||||
try {
|
||||
if (load.length == 0) {
|
||||
sendEmptyFile(pd);
|
||||
} else {
|
||||
pd.sendResponse(createProtocolP2PPacket((Payload)(new FilePart(filename, fullLoad.length, offset, load))));
|
||||
}
|
||||
} catch (Exception e2) {
|
||||
writeLog(e2, LogLevel.Error);
|
||||
}
|
||||
} else {
|
||||
writeLog("File requested not found: `" + filename + "` " + Arrays.binarySearch(fileList, filename), LogLevel.Debug);
|
||||
writeLog("File list:", LogLevel.Debug);
|
||||
for (String f: fileList) {
|
||||
writeLog("- " + f, LogLevel.Debug);
|
||||
}
|
||||
|
||||
throw new IOException(); // to send a NOT_FOUND in the catch block
|
||||
}
|
||||
} catch (IOException e) {
|
||||
try {
|
||||
sendNotFound(pd);
|
||||
} catch (Exception e2) {
|
||||
writeLog(e2, LogLevel.Debug);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Getter for tracker socket
|
||||
*/
|
||||
protected abstract Object getTrackerSocket();
|
||||
|
||||
/** Send unregister request to tracker
|
||||
*/
|
||||
protected void sendUnregisterRequest() {
|
||||
// unregistering from tracker
|
||||
try {
|
||||
writeLog("Unregistering from tracker", LogLevel.Info);
|
||||
createProtocolP2PPacket(new Unregister(server)).sendRequest(getTrackerSocket());
|
||||
} catch (Exception e) {
|
||||
writeLog("Cannot unregister from tracker", LogLevel.Error);
|
||||
writeLog(e, LogLevel.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/** Handle request.
|
||||
* @throws LocalException
|
||||
*/
|
||||
protected < T extends ProtocolP2PPacket<?> > void handleRequest(T pd) throws LocalException {
|
||||
Payload p = pd.getPayload();
|
||||
switch (p.getRequestResponseCode()) {
|
||||
case LOAD_REQUEST:
|
||||
writeLog("Received LOAD_REQUEST from host " + pd.getHostItem(), LogLevel.Action);
|
||||
sendLoadResponse(pd);
|
||||
break;
|
||||
case LIST_REQUEST:
|
||||
writeLog("Received LIST_REQUEST from host " + pd.getHostItem(), LogLevel.Action);
|
||||
sendListResponse(pd);
|
||||
break;
|
||||
case HASH_REQUEST:
|
||||
writeLog("Received HASH_REQUEST from host " + pd.getHostItem(), LogLevel.Action);
|
||||
sendHashResponse(pd);
|
||||
break;
|
||||
case DISCOVER_REQUEST:
|
||||
writeLog("Received DISCOVER_REQUEST from host " + pd.getHostItem(), LogLevel.Action);
|
||||
sendNotATracker(pd);
|
||||
break;
|
||||
case UNREGISTER:
|
||||
writeLog("Received UNREGISTER from host " + pd.getHostItem(), LogLevel.Action);
|
||||
sendNotATracker(pd);
|
||||
break;
|
||||
case REGISTER:
|
||||
writeLog("Received REGISTER from host " + pd.getHostItem(), LogLevel.Action);
|
||||
sendNotATracker(pd);
|
||||
break;
|
||||
default:
|
||||
writeLog("Received grabbage from host " + pd.getHostItem(), LogLevel.Action);
|
||||
sendInternalError(pd);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
package tools;
|
||||
|
||||
import java.util.Scanner;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.InputMismatchException;
|
||||
import tools.HostItem;
|
||||
|
||||
/** Helper to get the server list from the user
|
||||
* @author Louis Royer
|
||||
* @author Flavien Haas
|
||||
* @author JS Auge
|
||||
* @version 1.0
|
||||
*/
|
||||
public class HostList {
|
||||
/**
|
||||
* Let the user enter all server and puts it in a list
|
||||
* @return list of servers
|
||||
*/
|
||||
public static List<HostItem> getServList() {
|
||||
List<HostItem> serverList = new ArrayList<HostItem>();
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
String servName;
|
||||
int port = 0;
|
||||
do {
|
||||
System.out.println("Enter hostname of next server: (or \"stop\" when finished, default: localhost).");
|
||||
servName = scanner.nextLine();
|
||||
if (servName.equals("")) {
|
||||
servName = "localhost";
|
||||
}
|
||||
if (!servName.equals("stop")) {
|
||||
boolean err = false;
|
||||
do {
|
||||
System.out.println("Enter port for this server");
|
||||
try {
|
||||
port = scanner.nextInt();
|
||||
scanner.nextLine();
|
||||
if (port > 65535 || port <= 0) {
|
||||
err = true;
|
||||
System.out.println("Port number must be in 1-65535 range. Try again.");
|
||||
} else {
|
||||
err = false;
|
||||
}
|
||||
} catch (InputMismatchException e) {
|
||||
System.out.println("Invalid number. Try again.");
|
||||
err = true;
|
||||
}
|
||||
} while (err);
|
||||
serverList.add(new HostItem(servName, port));
|
||||
}
|
||||
|
||||
} while (!servName.equals("stop"));
|
||||
return serverList;
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package tools;
|
||||
|
||||
/** Test ports.
|
||||
* @author Louis Royer
|
||||
* @author Flavien Haas
|
||||
* @author JS Auge
|
||||
* @version 1.0
|
||||
*/
|
||||
public class PortRange {
|
||||
|
||||
protected int portMax;
|
||||
protected int portMin;
|
||||
protected int defaultPort;
|
||||
protected String type;
|
||||
|
||||
/** Port range constructor
|
||||
* @param portMin minimum port
|
||||
* @param portMax maximum port
|
||||
* @param defaultPort default port
|
||||
* @param type type of range
|
||||
*/
|
||||
public PortRange(int portMin, int portMax, int defaultPort, String type) {
|
||||
this.portMax = portMax;
|
||||
this.portMin = portMin;
|
||||
this.defaultPort = defaultPort;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/** test if port given correspond a range : registered ports, can be used without superuser privileges
|
||||
* @return true if port was valid
|
||||
*/
|
||||
public boolean isPortInRange(int port) {
|
||||
return ((port >= portMin) && (port <= portMax));
|
||||
}
|
||||
|
||||
/** To String
|
||||
* @return String representation
|
||||
*/
|
||||
public String toString() {
|
||||
return "default " + type + "port: " + defaultPort + "(range: " + portMin + " -> " + portMax + ")";
|
||||
}
|
||||
|
||||
/** Default port getter
|
||||
* @return default port
|
||||
*/
|
||||
public int getDefaultPort() {
|
||||
return defaultPort;
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package tools;
|
||||
import tools.LogLevel;
|
||||
import protocolP2P.ProtocolP2PPacket;
|
||||
import protocolP2P.Payload;
|
||||
import protocolP2P.RequestResponseCode;
|
||||
|
||||
public abstract class ServeErrors {
|
||||
|
||||
/** Implementation of writeLog
|
||||
* @param text Text to log
|
||||
* @param logLevel level of logging
|
||||
*/
|
||||
protected abstract void writeLog(String text, LogLevel logLevel);
|
||||
|
||||
/** Implementation of writeLog
|
||||
* @param e exception to log
|
||||
* @param logLevel level of logging
|
||||
*/
|
||||
protected abstract void writeLog(Exception e, LogLevel logLevel);
|
||||
|
||||
/** Create packets
|
||||
* @param payload Payload
|
||||
*/
|
||||
protected abstract < T extends Payload > ProtocolP2PPacket<?> createProtocolP2PPacket(T payload);
|
||||
|
||||
/** Send a NotATracker error message.
|
||||
* @param pd Request received
|
||||
*/
|
||||
protected < T extends ProtocolP2PPacket<?> > void sendNotATracker(T pd) {
|
||||
try {
|
||||
pd.sendResponse(createProtocolP2PPacket(new Payload(RequestResponseCode.NOT_A_TRACKER)));
|
||||
} catch (Exception e) {
|
||||
writeLog(e, LogLevel.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/** Send an internal error message.
|
||||
* @param pd Request received
|
||||
*/
|
||||
protected < T extends ProtocolP2PPacket<?> > void sendInternalError(T pd) {
|
||||
writeLog("Internal Error", LogLevel.Warning);
|
||||
try {
|
||||
pd.sendResponse(createProtocolP2PPacket(new Payload(RequestResponseCode.INTERNAL_ERROR)));
|
||||
} catch (Exception e) {
|
||||
writeLog(e, LogLevel.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/** Send a not found message.
|
||||
* @param pd Request received
|
||||
*/
|
||||
protected < T extends ProtocolP2PPacket<?> > void sendNotFound(T pd) {
|
||||
try {
|
||||
pd.sendResponse(createProtocolP2PPacket(new Payload(RequestResponseCode.NOT_FOUND)));
|
||||
} catch (Exception e) {
|
||||
writeLog(e, LogLevel.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/** Send an empty directory message.
|
||||
* @param pd Request received
|
||||
*/
|
||||
protected < T extends ProtocolP2PPacket<?> > void sendEmptyDirectory(T pd) {
|
||||
try {
|
||||
pd.sendResponse(createProtocolP2PPacket(new Payload(RequestResponseCode.EMPTY_DIRECTORY)));
|
||||
} catch (Exception e) {
|
||||
writeLog(e, LogLevel.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/** Send an empty file message.
|
||||
* @param pd Request received
|
||||
*/
|
||||
protected < T extends ProtocolP2PPacket<?> > void sendEmptyFile(T pd) {
|
||||
try {
|
||||
pd.sendResponse(createProtocolP2PPacket(new Payload(RequestResponseCode.EMPTY_FILE)));
|
||||
} catch (Exception e) {
|
||||
writeLog(e, LogLevel.Error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package tools;
|
||||
import tools.PortRange;
|
||||
|
||||
public class ServerPortRange extends PortRange {
|
||||
|
||||
/** Constructor */
|
||||
public ServerPortRange() {
|
||||
super(7000, 7999, 7070, "server");
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package tools;
|
||||
import tools.PortRange;
|
||||
|
||||
public class TrackerPortRange extends PortRange {
|
||||
|
||||
/** Constructor */
|
||||
public TrackerPortRange() {
|
||||
super(6000, 6999, 6969, "tracker");
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
package tracker;
|
||||
|
||||
import java.util.Scanner;
|
||||
import tracker.TrackerManagementTCP;
|
||||
import tracker.TrackerManagementUDP;
|
||||
import tools.Directories;
|
||||
import tools.Logger;
|
||||
import tools.LogLevel;
|
||||
import tools.TrackerPortRange;
|
||||
import tools.HostItem;
|
||||
|
||||
/** Tracker implementation
|
||||
* First argument of main method is port listened by the tracker, and is mandatory.
|
||||
* @author Louis Royer
|
||||
* @author Flavien Haas
|
||||
* @author JS Auge
|
||||
* @version 1.0
|
||||
*/
|
||||
public class Tracker {
|
||||
private HostItem tracker;
|
||||
private Directories directories;
|
||||
private Logger logger;
|
||||
|
||||
/** Constructor with portStr containing a port number.
|
||||
* @param hostname hostname to bind
|
||||
* @param port port to bind
|
||||
*/
|
||||
public Tracker(String hostname, int port) {
|
||||
tracker = new HostItem(hostname, port);
|
||||
directories = new Directories("P2P_JAVA_PROJECT_TRACKER_" + port);
|
||||
logger = new Logger(directories.getDataHomeDirectory() + "tracker.log");
|
||||
System.out.println("Tracker will listen on port " + port + " and write logs into " + directories.getDataHomeDirectory());
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
directories.askOpenDataHomeDirectory(null, scanner);
|
||||
scanner.close();
|
||||
}
|
||||
|
||||
/** Main program entry point
|
||||
* first parameter is port number and is mandatory
|
||||
* to test, run with: java serverP2P.ServerP2P
|
||||
* @param args parameters
|
||||
*/
|
||||
public static void main(String [] args) {
|
||||
final TrackerPortRange trackerPortRange = new TrackerPortRange();
|
||||
final String defaultHostname = "localhost";
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
String hostname = "";
|
||||
int port = 0;
|
||||
Tracker t;
|
||||
|
||||
if ((args.length != 3) && (args.length != 0)){
|
||||
System.out.println("usage : java tracker.Tracker (interactive) or java trackerP2P.trackerP2P -- <hostname> <PORT> (" + trackerPortRange +")");
|
||||
System.exit(1);
|
||||
} else if (args.length == 3){
|
||||
hostname = args[1];
|
||||
port = Integer.valueOf(Integer.parseInt(args[2]));
|
||||
} else {
|
||||
System.out.println("Tracker Server, enter hostname to bind (default = localhost): ");
|
||||
hostname = scanner.nextLine();
|
||||
if(hostname.equals("")){
|
||||
hostname = defaultHostname;
|
||||
System.out.println("using default hostname : " + hostname);
|
||||
}
|
||||
System.out.println("enter port (default = " + trackerPortRange.getDefaultPort() +"): ");
|
||||
String portStr = scanner.nextLine();
|
||||
if(portStr.equals("")){
|
||||
port = trackerPortRange.getDefaultPort();
|
||||
System.out.println("using default port : " + port);
|
||||
} else {
|
||||
port = Integer.valueOf(Integer.parseInt(portStr));
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("using hostname : " + hostname);
|
||||
if(trackerPortRange.isPortInRange(port)) {
|
||||
System.out.println("using port : " + port);
|
||||
t = new Tracker(hostname, port);
|
||||
}
|
||||
else {
|
||||
System.out.println("Port not in range. " + trackerPortRange);
|
||||
t = new Tracker(hostname, trackerPortRange.getDefaultPort());
|
||||
}
|
||||
|
||||
TrackerManagementUDP tmudp = new TrackerManagementUDP(t.tracker, t.logger);
|
||||
TrackerManagementTCP tmtcp = new TrackerManagementTCP(t.tracker, t.logger);
|
||||
Thread tudp = new Thread(tmudp);
|
||||
tudp.setName("Tracker UDP P2P-JAVA-PROJECT");
|
||||
tudp.start();
|
||||
Thread ttcp = new Thread(tmtcp);
|
||||
ttcp.setName("Tracker TCP P2P-JAVA-PROJECT");
|
||||
ttcp.start();
|
||||
}
|
||||
}
|
@ -0,0 +1,201 @@
|
||||
package tracker;
|
||||
import tools.ServeErrors;
|
||||
import tools.HostItem;
|
||||
import tools.Logger;
|
||||
import tools.LogLevel;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import protocolP2P.ProtocolP2PPacket;
|
||||
import protocolP2P.Payload;
|
||||
import protocolP2P.DiscoverRequest;
|
||||
import protocolP2P.DiscoverResponse;
|
||||
import protocolP2P.FileList;
|
||||
import protocolP2P.Unregister;
|
||||
import protocolP2P.Register;
|
||||
import protocolP2P.RequestResponseCode;
|
||||
import localException.InternalError;
|
||||
import remoteException.EmptyDirectory;
|
||||
import exception.LocalException;
|
||||
|
||||
|
||||
/** Tracker management implementation
|
||||
* @author Louis Royer
|
||||
* @author Flavien Haas
|
||||
* @author JS Auge
|
||||
* @version 1.0
|
||||
*/
|
||||
public abstract class TrackerManagement extends ServeErrors implements Runnable {
|
||||
protected HostItem tracker;
|
||||
protected Logger logger;
|
||||
protected List<HostItem> hostList = new ArrayList<>();
|
||||
protected Map<String, List<HostItem>> fileList = new HashMap<>();
|
||||
protected volatile boolean stop;
|
||||
|
||||
/** Constructor
|
||||
* @param tracker Tracker HostItem
|
||||
* @param logger Logger
|
||||
*/
|
||||
public TrackerManagement(HostItem tracker, Logger logger) {
|
||||
stop = false;
|
||||
this.tracker = tracker;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
/** Handle Discover request
|
||||
* @param pd Received request
|
||||
* @throws InternalError
|
||||
*/
|
||||
protected < T extends ProtocolP2PPacket<?> > void handleDiscover(T pd) throws InternalError {
|
||||
Payload p = pd.getPayload();
|
||||
assert p instanceof DiscoverRequest : "payload must be an instance of DiscoverRequest";
|
||||
if (!(p instanceof DiscoverRequest)) {
|
||||
sendInternalError(pd);
|
||||
} else {
|
||||
String filename = ((DiscoverRequest)p).getFilename();
|
||||
try {
|
||||
pd.sendResponse(createProtocolP2PPacket(new DiscoverResponse(filename, fileList.getOrDefault(filename, hostList))));
|
||||
} catch (Exception e) {
|
||||
writeLog(e, LogLevel.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Handle List Responses
|
||||
* @param pd Received response
|
||||
* @throws InternalError
|
||||
*/
|
||||
protected <T extends ProtocolP2PPacket<?> > void handleListResponse(T pd, HostItem host) throws InternalError {
|
||||
Payload p = pd.getPayload();
|
||||
assert p instanceof FileList: "payload must be an instance of FileList";
|
||||
if (!(p instanceof FileList)) {
|
||||
throw new InternalError();
|
||||
} else {
|
||||
String[] f = ((FileList)p).getFileList();
|
||||
for (String file: f) {
|
||||
List<HostItem> h = fileList.get(file);
|
||||
if (h != null) {
|
||||
if (!h.contains(host)) {
|
||||
h.add(host);
|
||||
}
|
||||
} else {
|
||||
List<HostItem> emptyH = new ArrayList<>();
|
||||
emptyH.add(host);
|
||||
fileList.put(file, emptyH);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Handle Unregistering
|
||||
* @param pd Request received
|
||||
* @throws InternalError
|
||||
*/
|
||||
protected < T extends ProtocolP2PPacket<?> > void handleUnregister(T pd) throws InternalError {
|
||||
Payload p = pd.getPayload();
|
||||
assert p instanceof Unregister : "payload must be an instance of Unregister";
|
||||
if (!(p instanceof Unregister)) {
|
||||
sendInternalError(pd);
|
||||
throw new InternalError();
|
||||
}
|
||||
HostItem host = ((Unregister)p).getHostItem();
|
||||
writeLog("Received UNREGISTER from host " + pd.getHostItem() + ". Removing host " + host, LogLevel.Action);
|
||||
hostList.remove(host);
|
||||
for(String f: fileList.keySet()) {
|
||||
fileList.get(f).remove(host);
|
||||
if(fileList.get(f).isEmpty()) {
|
||||
fileList.remove(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Getter for HostItem socket
|
||||
* @param hostItem HostItem
|
||||
*/
|
||||
protected abstract Object getHostItemSocket(HostItem hostItem);
|
||||
|
||||
/** Close HostItem socket
|
||||
* @param hostItem HostItem
|
||||
*/
|
||||
protected abstract void closeHostItemSocket(HostItem hostItem);
|
||||
|
||||
/** Handle Registering
|
||||
* @param pd Received request
|
||||
* @throws InternalError
|
||||
*/
|
||||
protected < T extends ProtocolP2PPacket<?> > void handleRegister(T pd) throws InternalError {
|
||||
Payload p = pd.getPayload();
|
||||
assert p instanceof Register : "payload must be an instance of Register";
|
||||
if (!(p instanceof Register)) {
|
||||
throw new InternalError();
|
||||
}
|
||||
// add host to known host list
|
||||
HostItem host = ((Register)p).getHostItem();
|
||||
if (!hostList.contains(host)) {
|
||||
hostList.add(host);
|
||||
}
|
||||
// send a list request
|
||||
try {
|
||||
ProtocolP2PPacket<?> pLReq = createProtocolP2PPacket(new Payload(RequestResponseCode.LIST_REQUEST));
|
||||
pLReq.sendRequest(getHostItemSocket(host));
|
||||
writeLog("Received REGISTER from host " + pd.getHostItem() + ". Adding host " + host + " to list. Sending List request", LogLevel.Action);
|
||||
handleListResponse(pLReq.receiveResponse(), host);
|
||||
writeLog("Received LIST RESPONSE from host " + pd.getHostItem(), LogLevel.Action);
|
||||
closeHostItemSocket(host);
|
||||
} catch (EmptyDirectory e) {
|
||||
writeLog("Empty Directory", LogLevel.Debug);
|
||||
hostList.remove(host);
|
||||
writeLog("Received EMPTY DIRECTORY from host " + pd.getHostItem() + ". Aborting.", LogLevel.Action);
|
||||
} catch (Exception e) {
|
||||
// remove from list because list request could not be send
|
||||
hostList.remove(host);
|
||||
writeLog("Aborting the add of host " + host, LogLevel.Action);
|
||||
writeLog(e, LogLevel.Error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Handle requests
|
||||
* @throws LocalException
|
||||
*/
|
||||
protected <T extends ProtocolP2PPacket<?> > void handleRequest(T pd) throws LocalException {
|
||||
Payload p = pd.getPayload();
|
||||
switch (p.getRequestResponseCode()) {
|
||||
case LOAD_REQUEST:
|
||||
writeLog("Received LOAD_REQUEST from host " + pd.getHostItem() + ", sending NOT_FOUND", LogLevel.Action);
|
||||
sendNotFound(pd);
|
||||
break;
|
||||
case LIST_REQUEST:
|
||||
writeLog("Received LIST_REQUEST from host " + pd.getHostItem() + ", sending EMPTY_DIRECTORY", LogLevel.Action);
|
||||
sendEmptyDirectory(pd);
|
||||
break;
|
||||
case HASH_REQUEST:
|
||||
writeLog("Received HASH_REQUEST from host " + pd.getHostItem() + ", sending NOT_FOUND", LogLevel.Action);
|
||||
sendNotFound(pd);
|
||||
break;
|
||||
case REGISTER:
|
||||
writeLog("Received REGISTER from host " + pd.getHostItem(), LogLevel.Debug);
|
||||
handleRegister(pd);
|
||||
break;
|
||||
case UNREGISTER:
|
||||
writeLog("Received UNREGISTER from host " + pd.getHostItem(), LogLevel.Debug);
|
||||
handleUnregister(pd);
|
||||
break;
|
||||
case DISCOVER_REQUEST:
|
||||
writeLog("Received DISCOVER REQUEST from host " + pd.getHostItem(), LogLevel.Action);
|
||||
handleDiscover(pd);
|
||||
break;
|
||||
default:
|
||||
writeLog("Received grabbage from host " + pd.getHostItem(), LogLevel.Action);
|
||||
sendInternalError(pd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** Stop the thread */
|
||||
public void setStop() {
|
||||
stop = true;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,155 @@
|
||||
package tracker;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.net.InetAddress;
|
||||
import protocolP2P.ProtocolP2PPacketTCP;
|
||||
import protocolP2P.ProtocolP2PPacket;
|
||||
import protocolP2P.RequestResponseCode;
|
||||
import protocolP2P.Payload;
|
||||
import protocolP2P.Register;
|
||||
import protocolP2P.Unregister;
|
||||
import protocolP2P.DiscoverRequest;
|
||||
import protocolP2P.DiscoverResponse;
|
||||
import protocolP2P.FileList;
|
||||
import exception.LocalException;
|
||||
import remoteException.EmptyDirectory;
|
||||
import localException.InternalError;
|
||||
import localException.SocketClosed;
|
||||
import tracker.TrackerManagement;
|
||||
import tools.HostItem;
|
||||
import tools.Logger;
|
||||
import tools.LogLevel;
|
||||
|
||||
/** Tracker management implementation with tcp
|
||||
* @author Louis Royer
|
||||
* @author Flavien Haas
|
||||
* @author JS Auge
|
||||
* @version 1.0
|
||||
*/
|
||||
public class TrackerManagementTCP extends TrackerManagement {
|
||||
private ServerSocket socket;
|
||||
|
||||
/** Constructor with port and logger.
|
||||
* @param tracker hostitem of the tracker.
|
||||
* @param logger Logger object
|
||||
*/
|
||||
public TrackerManagementTCP(HostItem tracker, Logger logger) {
|
||||
super(tracker, logger);
|
||||
try {
|
||||
socket = new ServerSocket(tracker.getPort(), 10, tracker.getInetAddress());
|
||||
} catch (SocketException e) {
|
||||
writeLog("Error: cannot listen on" + tracker, LogLevel.Error);
|
||||
System.exit(-1);
|
||||
} catch (IOException e) {
|
||||
writeLog("Error: cannot open socket", LogLevel.Error);
|
||||
System.exit(-2);
|
||||
}
|
||||
}
|
||||
|
||||
/** Implementation of runnable. This methods allows to run the server.
|
||||
*/
|
||||
public void run() {
|
||||
writeLog("Tracker sucessfully started", LogLevel.Info);
|
||||
while (!stop) {
|
||||
try {
|
||||
Socket s = socket.accept();
|
||||
ClientHandler c = new ClientHandler(s);
|
||||
(new Thread(c)).start();
|
||||
} catch (IOException e) {
|
||||
writeLog("Error while accepting new connection", LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Private runnable class allowing to serve one client.
|
||||
*/
|
||||
private class ClientHandler implements Runnable {
|
||||
private HostItem addr;
|
||||
/** Constructor with a socket.
|
||||
* @param s Socket of this client
|
||||
*/
|
||||
public ClientHandler(Socket s) {
|
||||
this.addr = new HostItem(s);
|
||||
}
|
||||
|
||||
/** Implementation of runnable. This method allow to serve one client.
|
||||
*/
|
||||
public void run() {
|
||||
|
||||
boolean end = false;
|
||||
writeLog("[ " + addr + "] New connection", LogLevel.Action);
|
||||
do {
|
||||
end = handleClientRequest();
|
||||
} while(!end);
|
||||
writeLog("[ " + addr + "] End of connection", LogLevel.Action);
|
||||
}
|
||||
|
||||
/** Respond to next request incomming on socket s.
|
||||
* @param s Socket used to read request and send response
|
||||
* @return true if cannot expect another request (ie, socket is closed)
|
||||
*/
|
||||
private boolean handleClientRequest() {
|
||||
try {
|
||||
ProtocolP2PPacketTCP<?> pd = new ProtocolP2PPacketTCP<>((Object)addr.getTCPSocket());
|
||||
handleRequest(pd);
|
||||
} catch (IOException e) {
|
||||
writeLog(e, LogLevel.Warning);
|
||||
return true;
|
||||
} catch (SocketClosed e) {
|
||||
return true;
|
||||
} catch (LocalException e) {
|
||||
writeLog(e, LogLevel.Warning);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Implementation of writeLog
|
||||
* @param text Text to log
|
||||
* @param logLevel level of logging
|
||||
*/
|
||||
protected void writeLog(String text, LogLevel logLevel) {
|
||||
logger.writeTCP(text, logLevel);
|
||||
}
|
||||
|
||||
/** Implementation of writeLog
|
||||
* @param e exception to log
|
||||
* @param logLevel level of logging
|
||||
*/
|
||||
protected void writeLog(Exception e, LogLevel logLevel) {
|
||||
logger.writeTCP(e, logLevel);
|
||||
}
|
||||
|
||||
/** Create packets
|
||||
* @param payload Payload
|
||||
*/
|
||||
protected < T extends Payload > ProtocolP2PPacket<T> createProtocolP2PPacket(T payload) {
|
||||
return (ProtocolP2PPacket<T>)new ProtocolP2PPacketTCP<T>(payload);
|
||||
}
|
||||
|
||||
|
||||
/** Getter for HostItem socket
|
||||
* @param hostItem HostItem
|
||||
*/
|
||||
protected Object getHostItemSocket(HostItem hostItem) {
|
||||
return (Object)hostItem.getTCPSocket();
|
||||
}
|
||||
|
||||
/** Close HostItem socket
|
||||
* @param hostItem HostItem
|
||||
*/
|
||||
protected void closeHostItemSocket(HostItem hostItem) {
|
||||
hostItem.closeTCPSocket();
|
||||
}
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
package tracker;
|
||||
import tools.Logger;
|
||||
import tools.LogLevel;
|
||||
import java.net.DatagramSocket;
|
||||
import protocolP2P.ProtocolP2PPacketUDP;
|
||||
import protocolP2P.ProtocolP2PPacket;
|
||||
import protocolP2P.RequestResponseCode;
|
||||
import protocolP2P.Payload;
|
||||
import protocolP2P.Register;
|
||||
import protocolP2P.Unregister;
|
||||
import tools.HostItem;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.io.IOException;
|
||||
import java.net.SocketException;
|
||||
import exception.LocalException;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import protocolP2P.DiscoverRequest;
|
||||
import protocolP2P.DiscoverResponse;
|
||||
import protocolP2P.FileList;
|
||||
import localException.InternalError;
|
||||
import remoteException.EmptyDirectory;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import tracker.TrackerManagement;
|
||||
|
||||
/** Tracker management implementation with udp
|
||||
* @author Louis Royer
|
||||
* @author Flavien Haas
|
||||
* @author JS Auge
|
||||
* @version 1.0
|
||||
*/
|
||||
public class TrackerManagementUDP extends TrackerManagement {
|
||||
private DatagramSocket socket;
|
||||
/** Constructor with port and logger.
|
||||
* @param tracker hostitem of the tracker.
|
||||
* @param logger Logger object
|
||||
*/
|
||||
public TrackerManagementUDP(HostItem tracker, Logger logger) {
|
||||
super(tracker, logger);
|
||||
try {
|
||||
socket = new DatagramSocket(tracker.getPort(), tracker.getInetAddress());
|
||||
} catch (SocketException e) {
|
||||
logger.writeUDP("Error: cannot listen on " + tracker, LogLevel.Error);
|
||||
System.exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
/** Implementation of runnable. This methods allows to run the tracker.
|
||||
*/
|
||||
public void run() {
|
||||
logger.writeUDP("Tracker successfully started", LogLevel.Info);
|
||||
while(!stop) {
|
||||
try {
|
||||
ProtocolP2PPacketUDP<?> pd = new ProtocolP2PPacketUDP<>((Object)socket);
|
||||
handleRequest(pd);
|
||||
} catch (IOException e) {
|
||||
logger.writeUDP(e, LogLevel.Warning);
|
||||
} catch (LocalException e) {
|
||||
logger.writeUDP(e, LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Implementation of writeLog
|
||||
* @param text Text to log
|
||||
* @param logLevel level of logging
|
||||
*/
|
||||
protected void writeLog(String text, LogLevel logLevel) {
|
||||
logger.writeUDP(text, logLevel);
|
||||
}
|
||||
|
||||
/** Implementation of writeLog
|
||||
* @param e exception to log
|
||||
* @param logLevel level of logging
|
||||
*/
|
||||
protected void writeLog(Exception e, LogLevel logLevel) {
|
||||
logger.writeUDP(e, logLevel);
|
||||
}
|
||||
|
||||
/** Create packets
|
||||
* @param payload Payload
|
||||
*/
|
||||
protected < T extends Payload > ProtocolP2PPacket<T> createProtocolP2PPacket(T payload) {
|
||||
return (ProtocolP2PPacket<T>)new ProtocolP2PPacketUDP<T>(payload);
|
||||
}
|
||||
|
||||
|
||||
/** Getter for HostItem socket
|
||||
* @param hostItem HostItem
|
||||
*/
|
||||
protected Object getHostItemSocket(HostItem hostItem) {
|
||||
return (Object)hostItem.getUDPSocket();
|
||||
}
|
||||
|
||||
/** Close HostItem socket
|
||||
* @param hostItem HostItem
|
||||
*/
|
||||
protected void closeHostItemSocket(HostItem hostItem) {
|
||||
hostItem.closeUDPSocket();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue