parent
33f1829cdc
commit
ef5484566e
@ -130,6 +130,9 @@ public class ClientP2P {
|
|||||||
t.join();
|
t.join();
|
||||||
} catch (InterruptedException e) {}
|
} catch (InterruptedException e) {}
|
||||||
c.scanner.close();
|
c.scanner.close();
|
||||||
|
smudp.setStop();
|
||||||
|
smtcp.setStop();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ public abstract class ProtocolP2PPacket {
|
|||||||
* @throws IOException
|
* @throws IOException
|
||||||
* @throws SocketClosed
|
* @throws SocketClosed
|
||||||
*/
|
*/
|
||||||
public abstract void sendResponse(ProtocolP2PPacket response) throws InternalError, IOException, SocketClosed;
|
public abstract <T extends ProtocolP2PPacket> void sendResponse(T response) throws InternalError, IOException, SocketClosed;
|
||||||
|
|
||||||
/** Get hostItem of the sender
|
/** Get hostItem of the sender
|
||||||
* @return hostItem of the sender
|
* @return hostItem of the sender
|
||||||
|
156
src/serverP2P/FileWatcher.java
Normal file
156
src/serverP2P/FileWatcher.java
Normal file
@ -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);
|
||||||
|
|
||||||
|
}
|
65
src/serverP2P/FileWatcherTCP.java
Normal file
65
src/serverP2P/FileWatcherTCP.java
Normal file
@ -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 p = (ProtocolP2PPacket)new ProtocolP2PPacketTCP((Payload)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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
64
src/serverP2P/FileWatcherUDP.java
Normal file
64
src/serverP2P/FileWatcherUDP.java
Normal file
@ -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 p = (ProtocolP2PPacket)new ProtocolP2PPacketUDP((Payload)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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
234
src/serverP2P/ServerManagement.java
Normal file
234
src/serverP2P/ServerManagement.java
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
package serverP2P;
|
||||||
|
import serverP2P.FileWatcher;
|
||||||
|
import tools.Logger;
|
||||||
|
import tools.LogLevel;
|
||||||
|
import tools.HostItem;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/** 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 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) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 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 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);
|
||||||
|
pd.sendResponse(createProtocolP2PPacket(new Payload(RequestResponseCode.EMPTY_DIRECTORY)));
|
||||||
|
} 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 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 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 {
|
||||||
|
pd.sendResponse(createProtocolP2PPacket(new Payload(RequestResponseCode.NOT_FOUND)));
|
||||||
|
} 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) {
|
||||||
|
pd.sendResponse(createProtocolP2PPacket(new Payload(RequestResponseCode.EMPTY_FILE)));
|
||||||
|
} 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 {
|
||||||
|
pd.sendResponse(createProtocolP2PPacket(new Payload(RequestResponseCode.NOT_FOUND)));
|
||||||
|
} 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);
|
||||||
|
ProtocolP2PPacket p = createProtocolP2PPacket((Payload)new Unregister(server));
|
||||||
|
p.sendRequest(getTrackerSocket());
|
||||||
|
} catch (Exception e) {
|
||||||
|
writeLog("Cannot unregister from tracker", LogLevel.Error);
|
||||||
|
writeLog(e, LogLevel.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -34,6 +34,8 @@ import protocolP2P.HashRequest;
|
|||||||
import protocolP2P.HashResponse;
|
import protocolP2P.HashResponse;
|
||||||
import protocolP2P.Register;
|
import protocolP2P.Register;
|
||||||
import protocolP2P.Unregister;
|
import protocolP2P.Unregister;
|
||||||
|
import serverP2P.ServerManagement;
|
||||||
|
import serverP2P.FileWatcherTCP;
|
||||||
|
|
||||||
|
|
||||||
/** Implementation of P2P-JAVA-PROJECT VERSION 1.0 protocol for TCP.
|
/** Implementation of P2P-JAVA-PROJECT VERSION 1.0 protocol for TCP.
|
||||||
@ -42,17 +44,9 @@ import protocolP2P.Unregister;
|
|||||||
* @author JS Auge
|
* @author JS Auge
|
||||||
* @version 1.0
|
* @version 1.0
|
||||||
*/
|
*/
|
||||||
public class ServerManagementTCP implements Runnable {
|
public class ServerManagementTCP extends ServerManagement {
|
||||||
|
|
||||||
private String[] fileList;
|
|
||||||
private Map<String, byte[]> sha512 = new HashMap<>();
|
|
||||||
private String baseDirectory;
|
|
||||||
private ServerSocket socket;
|
private ServerSocket socket;
|
||||||
private Logger logger;
|
|
||||||
private HostItem tracker;
|
|
||||||
private HostItem server;
|
|
||||||
private FileListWatcher fileListWatcher;
|
|
||||||
private volatile boolean stop;
|
|
||||||
|
|
||||||
/** Constructor for TCP implementation, with baseDirectory and TCPPort parameters.
|
/** Constructor for TCP implementation, with baseDirectory and TCPPort parameters.
|
||||||
* @param baseDirectory the root directory where files are stored
|
* @param baseDirectory the root directory where files are stored
|
||||||
@ -62,12 +56,11 @@ public class ServerManagementTCP implements Runnable {
|
|||||||
* @param tracker Tracker
|
* @param tracker Tracker
|
||||||
*/
|
*/
|
||||||
public ServerManagementTCP(String baseDirectory, String hostName, int port, Logger logger, HostItem tracker) {
|
public ServerManagementTCP(String baseDirectory, String hostName, int port, Logger logger, HostItem tracker) {
|
||||||
stop = false;
|
super(baseDirectory, new HostItem(hostName, port), tracker, logger);
|
||||||
server = new HostItem(hostName, port);
|
assert baseDirectory != null : "baseDirectory is null";
|
||||||
fileList = new String[0];
|
assert server != null : "server is null";
|
||||||
this.tracker = tracker;
|
assert tracker != null : "tracker is null";
|
||||||
this.logger = logger;
|
assert logger != null : "logger is null";
|
||||||
this.baseDirectory = baseDirectory;
|
|
||||||
try {
|
try {
|
||||||
socket = new ServerSocket(server.getPort(), 10, server.getInetAddress());
|
socket = new ServerSocket(server.getPort(), 10, server.getInetAddress());
|
||||||
} catch (SocketException e) {
|
} catch (SocketException e) {
|
||||||
@ -79,24 +72,12 @@ public class ServerManagementTCP implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Stop the thread */
|
|
||||||
public void setStop() {
|
|
||||||
stop = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Trigger a manual check of the file list
|
|
||||||
*/
|
|
||||||
public void updateFileList() {
|
|
||||||
if (fileListWatcher != null) {
|
|
||||||
fileListWatcher.trigger();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Implementation of runnable. This methods allows to run the server.
|
/** Implementation of runnable. This methods allows to run the server.
|
||||||
*/
|
*/
|
||||||
public void run() {
|
public void run() {
|
||||||
logger.writeTCP("Server sucessfully started", LogLevel.Info);
|
logger.writeTCP("Server sucessfully started", LogLevel.Info);
|
||||||
fileListWatcher = new FileListWatcher(10000); // checking every 10 seconds
|
fileListWatcher = (FileWatcher)new FileWatcherTCP(logger, 10000, server, tracker, baseDirectory); // checking every 10 seconds
|
||||||
(new Thread(fileListWatcher)).start();
|
(new Thread(fileListWatcher)).start();
|
||||||
while(!stop) {
|
while(!stop) {
|
||||||
try {
|
try {
|
||||||
@ -108,15 +89,7 @@ public class ServerManagementTCP implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fileListWatcher.setStop();
|
fileListWatcher.setStop();
|
||||||
// unregistering from tracker
|
sendUnregisterRequest();
|
||||||
try {
|
|
||||||
logger.writeTCP("Unregistering from tracker", LogLevel.Info);
|
|
||||||
ProtocolP2PPacket p = (ProtocolP2PPacket)new ProtocolP2PPacketTCP((Payload)new Unregister(server));
|
|
||||||
p.sendRequest((Object)tracker.getTCPSocket());
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.writeTCP("Cannot unregister from tracker", LogLevel.Error);
|
|
||||||
logger.writeTCP(e, LogLevel.Error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Private runnable class allowing to serve one client.
|
/** Private runnable class allowing to serve one client.
|
||||||
@ -193,240 +166,32 @@ public class ServerManagementTCP implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Implementation of writeLog
|
||||||
/** Init sha512 map.
|
* @param text Text to log
|
||||||
|
* @param logLevel level of logging
|
||||||
*/
|
*/
|
||||||
private void initSha512() {
|
protected void writeLog(String text, LogLevel logLevel) {
|
||||||
for(String f: fileList) {
|
logger.writeTCP(text, logLevel);
|
||||||
try {
|
|
||||||
MessageDigest md = MessageDigest.getInstance(HashAlgorithm.SHA512.getName());
|
|
||||||
sha512.put(f, md.digest(Files.readAllBytes(Paths.get(baseDirectory + f))));
|
|
||||||
md.reset();
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
logger.writeTCP("sha512 not supported", LogLevel.Error);
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.writeTCP("cannot read " + f, LogLevel.Warning);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send an internal error message.
|
/** Implementation of writeLog
|
||||||
* @param pd ProtocolP2PPacketTCP to respond
|
* @param e exception to log
|
||||||
|
* @param logLevel level of logging
|
||||||
*/
|
*/
|
||||||
private void sendInternalError(ProtocolP2PPacketTCP pd) {
|
protected void writeLog(Exception e, LogLevel logLevel) {
|
||||||
logger.writeTCP("Internal Error", LogLevel.Warning);
|
logger.writeTCP(e, logLevel);
|
||||||
try {
|
|
||||||
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.INTERNAL_ERROR)));
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.writeTCP(e, LogLevel.Error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send a NotATracker error message.
|
/** Create packets
|
||||||
* @param pd ProtocolP2PPacketTCP to respond
|
* @param payload Payload
|
||||||
*/
|
*/
|
||||||
private void sendNotATracker(ProtocolP2PPacketTCP pd) {
|
protected < T extends Payload > ProtocolP2PPacket createProtocolP2PPacket(T payload) {
|
||||||
try {
|
return (ProtocolP2PPacket)new ProtocolP2PPacketTCP((Payload) payload);
|
||||||
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.NOT_A_TRACKER)));
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.writeTCP(e, LogLevel.Error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send response to list request
|
/** Getter for tracker socket
|
||||||
* @param pd Request received
|
|
||||||
*/
|
*/
|
||||||
private void sendListResponse(ProtocolP2PPacketTCP pd) {
|
protected Object getTrackerSocket() {
|
||||||
try {
|
return (Object)tracker.getTCPSocket();
|
||||||
if (fileList.length == 0) {
|
|
||||||
logger.writeTCP("Sending EMPTY_DIRECTORY", LogLevel.Action);
|
|
||||||
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.EMPTY_DIRECTORY)));
|
|
||||||
} else {
|
|
||||||
logger.writeTCP("Sending LIST_RESPONSE", LogLevel.Action);
|
|
||||||
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP((Payload)(new FileList(fileList))));
|
|
||||||
}
|
|
||||||
} catch (Exception e2) {
|
|
||||||
logger.writeTCP(e2, LogLevel.Error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send hash response to hash request
|
|
||||||
* @param pd Request received
|
|
||||||
*/
|
|
||||||
private void sendHashResponse(ProtocolP2PPacketTCP 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(fileList, filename) >= 0) {
|
|
||||||
Map<HashAlgorithm, byte[]> hashes = new HashMap<>();
|
|
||||||
for (HashAlgorithm h : ((HashRequest)p).getAlgoList()) {
|
|
||||||
switch (h) {
|
|
||||||
case SHA512:
|
|
||||||
hashes.put(h, sha512.get(filename));
|
|
||||||
break;
|
|
||||||
case MD5:
|
|
||||||
default:
|
|
||||||
hashes.put(h, new byte[0]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP((Payload)(new HashResponse(filename, hashes))));
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.writeTCP(e, LogLevel.Error);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// file not found
|
|
||||||
try {
|
|
||||||
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.NOT_FOUND)));
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.writeTCP(e, LogLevel.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/** Send response to load request
|
|
||||||
* @param pd Request received
|
|
||||||
*/
|
|
||||||
private void sendLoadResponse(ProtocolP2PPacketTCP 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) {
|
|
||||||
logger.writeTCP("Sending last partialContent", LogLevel.Debug);
|
|
||||||
sizeToSend = fullLoad.length - offset;
|
|
||||||
} else {
|
|
||||||
sizeToSend = maxSizePartialContent;
|
|
||||||
}
|
|
||||||
logger.writeTCP("maxSizePartialContent: " + maxSizePartialContent, LogLevel.Debug);
|
|
||||||
logger.writeTCP("Sending " + filename + " from " + offset + " to " + (offset + sizeToSend), LogLevel.Debug);
|
|
||||||
byte[] load = Arrays.copyOfRange(fullLoad, (int)offset, (int)(offset + sizeToSend));
|
|
||||||
if (Arrays.binarySearch(fileList, filename) >= 0) {
|
|
||||||
try {
|
|
||||||
if (load.length == 0) {
|
|
||||||
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.EMPTY_FILE)));
|
|
||||||
} else {
|
|
||||||
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP((Payload)(new FilePart(filename, fullLoad.length, offset, load))));
|
|
||||||
}
|
|
||||||
} catch (Exception e2) {
|
|
||||||
logger.writeTCP(e2, LogLevel.Error);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.writeTCP("File requested not found: `" + filename + "` " + Arrays.binarySearch(fileList, filename), LogLevel.Debug);
|
|
||||||
logger.writeTCP("File list:", LogLevel.Debug);
|
|
||||||
for (String f: fileList) {
|
|
||||||
logger.writeTCP("- " + f, LogLevel.Debug);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new IOException(); // to send a NOT_FOUND in the catch block
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
try {
|
|
||||||
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.NOT_FOUND)));
|
|
||||||
} catch (Exception e2) {
|
|
||||||
logger.writeTCP(e2, LogLevel.Debug);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Private runnable class allowing to keep the tracker informed about file list. */
|
|
||||||
private class FileListWatcher implements Runnable {
|
|
||||||
private volatile boolean stop;
|
|
||||||
private long time;
|
|
||||||
private boolean force;
|
|
||||||
|
|
||||||
/** Register server on tracker
|
|
||||||
*/
|
|
||||||
private void registerTracker() {
|
|
||||||
try {
|
|
||||||
logger.writeTCP("Trying to into tracker", LogLevel.Info);
|
|
||||||
ProtocolP2PPacket p = (ProtocolP2PPacket)new ProtocolP2PPacketTCP((Payload)new Register(server));
|
|
||||||
p.sendRequest((Object)tracker.tryGetTCPSocket());
|
|
||||||
logger.writeTCP("Register request sent.", LogLevel.Debug);
|
|
||||||
tracker.closeTCPSocket();
|
|
||||||
} catch (Exception e) {
|
|
||||||
// error, trying again at next iteration
|
|
||||||
force = true;
|
|
||||||
logger.writeTCP("Cannot contact tracker, trying again at next iteration (" + time + " milliseconds).", LogLevel.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Update fileList and returns true if different than old list.
|
|
||||||
* @return true if changed
|
|
||||||
*/
|
|
||||||
private 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Constructor with millis parameter
|
|
||||||
* @param millis interval of time between checks
|
|
||||||
*/
|
|
||||||
public FileListWatcher(long millis) {
|
|
||||||
this.stop = false;
|
|
||||||
this.time = millis;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Ask the thread to stop
|
|
||||||
*/
|
|
||||||
public void setStop() {
|
|
||||||
stop = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Allow a manual check
|
|
||||||
*/
|
|
||||||
public void trigger() {
|
|
||||||
if (updateFileList() || force) {
|
|
||||||
force = false;
|
|
||||||
logger.writeTCP("File list watcher detected changes. Informing tracker.", LogLevel.Info);
|
|
||||||
registerTracker();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Runnable implementation */
|
|
||||||
public void run() {
|
|
||||||
logger.writeTCP("File list watcher started : delay " + time + " milliseconds.", LogLevel.Info);
|
|
||||||
while(!stop) {
|
|
||||||
trigger();
|
|
||||||
try {
|
|
||||||
Thread.sleep(time);
|
|
||||||
} catch(InterruptedException e) {
|
|
||||||
logger.writeTCP("File list watcher interrupted", LogLevel.Error);
|
|
||||||
setStop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,7 @@ import tools.HostItem;
|
|||||||
import protocolP2P.Register;
|
import protocolP2P.Register;
|
||||||
import protocolP2P.Unregister;
|
import protocolP2P.Unregister;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
|
import serverP2P.ServerManagement;
|
||||||
|
|
||||||
/** Implementation of P2P-JAVA-PROJECT VERSION 1.0 protocol for UDP.
|
/** Implementation of P2P-JAVA-PROJECT VERSION 1.0 protocol for UDP.
|
||||||
* @author Louis Royer
|
* @author Louis Royer
|
||||||
@ -41,17 +42,9 @@ import java.net.UnknownHostException;
|
|||||||
* @author JS Auge
|
* @author JS Auge
|
||||||
* @version 1.0
|
* @version 1.0
|
||||||
*/
|
*/
|
||||||
public class ServerManagementUDP implements Runnable {
|
public class ServerManagementUDP extends ServerManagement {
|
||||||
|
|
||||||
private String[] fileList = new String[0];
|
|
||||||
private Map<String, byte[]> sha512 = new HashMap<>();
|
|
||||||
private String baseDirectory;
|
|
||||||
private DatagramSocket socket;
|
private DatagramSocket socket;
|
||||||
private Logger logger;
|
|
||||||
private HostItem tracker;
|
|
||||||
private HostItem server;
|
|
||||||
private FileListWatcher fileListWatcher;
|
|
||||||
private volatile boolean stop;
|
|
||||||
|
|
||||||
/** Constructor for UDP implementation, with baseDirectory and UDPPort parameters.
|
/** Constructor for UDP implementation, with baseDirectory and UDPPort parameters.
|
||||||
* @param baseDirectory the root directory where files are stored
|
* @param baseDirectory the root directory where files are stored
|
||||||
@ -61,11 +54,11 @@ public class ServerManagementUDP implements Runnable {
|
|||||||
* @param tracker Tracker
|
* @param tracker Tracker
|
||||||
*/
|
*/
|
||||||
public ServerManagementUDP(String baseDirectory, String hostName, int port, Logger logger, HostItem tracker) {
|
public ServerManagementUDP(String baseDirectory, String hostName, int port, Logger logger, HostItem tracker) {
|
||||||
stop = false;
|
super(baseDirectory, new HostItem(hostName, port), tracker, logger);
|
||||||
server = new HostItem(hostName, port);
|
assert baseDirectory != null : "baseDirectory is null";
|
||||||
this.logger = logger;
|
assert server != null : "server is null";
|
||||||
this.baseDirectory = baseDirectory;
|
assert tracker != null : "tracker is null";
|
||||||
this.tracker = tracker;
|
assert logger != null : "logger is null";
|
||||||
try {
|
try {
|
||||||
socket = new DatagramSocket(server.getPort(), server.getInetAddress());
|
socket = new DatagramSocket(server.getPort(), server.getInetAddress());
|
||||||
} catch (SocketException e) {
|
} catch (SocketException e) {
|
||||||
@ -74,24 +67,12 @@ public class ServerManagementUDP implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Stop the thread */
|
|
||||||
public void setStop() {
|
|
||||||
stop = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Trigger a manual check of the file list
|
|
||||||
*/
|
|
||||||
public void updateFileList() {
|
|
||||||
if (fileListWatcher != null) {
|
|
||||||
fileListWatcher.trigger();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Implementation of runnable. This methods allows to run the server.
|
/** Implementation of runnable. This methods allows to run the server.
|
||||||
*/
|
*/
|
||||||
public void run() {
|
public void run() {
|
||||||
logger.writeUDP("Server sucessfully started", LogLevel.Info);
|
logger.writeUDP("Server sucessfully started", LogLevel.Info);
|
||||||
fileListWatcher = new FileListWatcher(10000); // checking every 10 seconds
|
fileListWatcher = (FileWatcher)new FileWatcherUDP(logger, 10000, server, tracker, baseDirectory); // checking every 10 seconds
|
||||||
(new Thread(fileListWatcher)).start();
|
(new Thread(fileListWatcher)).start();
|
||||||
while(!stop) {
|
while(!stop) {
|
||||||
try {
|
try {
|
||||||
@ -99,10 +80,11 @@ public class ServerManagementUDP implements Runnable {
|
|||||||
Payload p = pd.getPayload();
|
Payload p = pd.getPayload();
|
||||||
switch (p.getRequestResponseCode()) {
|
switch (p.getRequestResponseCode()) {
|
||||||
case LOAD_REQUEST:
|
case LOAD_REQUEST:
|
||||||
loadRequestManagement(pd);
|
sendLoadResponse(pd);
|
||||||
break;
|
break;
|
||||||
case LIST_REQUEST:
|
case LIST_REQUEST:
|
||||||
listRequestManagement(pd);
|
logger.writeUDP("Received LIST_REQUEST", LogLevel.Action);
|
||||||
|
sendListResponse(pd);
|
||||||
break;
|
break;
|
||||||
case HASH_REQUEST:
|
case HASH_REQUEST:
|
||||||
logger.writeUDP("Received HASH_REQUEST", LogLevel.Action);
|
logger.writeUDP("Received HASH_REQUEST", LogLevel.Action);
|
||||||
@ -127,249 +109,36 @@ public class ServerManagementUDP implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fileListWatcher.setStop();
|
fileListWatcher.setStop();
|
||||||
// unregistering from tracker
|
sendUnregisterRequest();
|
||||||
try {
|
|
||||||
logger.writeUDP("Unregistering from tracker", LogLevel.Info);
|
|
||||||
ProtocolP2PPacket p = (ProtocolP2PPacket)new ProtocolP2PPacketUDP((Payload)new Unregister(server));
|
|
||||||
p.sendRequest((Object)tracker.getUDPSocket());
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.writeUDP(e, LogLevel.Error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Init sha512 map.
|
|
||||||
|
/** Implementation of writeLog
|
||||||
|
* @param text Text to log
|
||||||
|
* @param logLevel level of logging
|
||||||
*/
|
*/
|
||||||
private void initSha512() {
|
protected void writeLog(String text, LogLevel logLevel) {
|
||||||
for(String f: fileList) {
|
logger.writeUDP(text, logLevel);
|
||||||
try {
|
|
||||||
MessageDigest md = MessageDigest.getInstance(HashAlgorithm.SHA512.getName());
|
|
||||||
sha512.put(f, md.digest(Files.readAllBytes(Paths.get(baseDirectory + f))));
|
|
||||||
md.reset();
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
logger.writeUDP("sha512 not supported", LogLevel.Error);
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.writeUDP("cannot read " + f, LogLevel.Warning);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send an internal error message.
|
/** Implementation of writeLog
|
||||||
* @param pd ProtocolP2PPacketUDP to respond
|
* @param e exception to log
|
||||||
|
* @param logLevel level of logging
|
||||||
*/
|
*/
|
||||||
private void sendInternalError(ProtocolP2PPacketUDP pd) {
|
protected void writeLog(Exception e, LogLevel logLevel) {
|
||||||
logger.writeUDP("Internal Error", LogLevel.Warning);
|
logger.writeUDP(e, logLevel);
|
||||||
try {
|
|
||||||
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.INTERNAL_ERROR)));
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.writeUDP(e, LogLevel.Error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Respond to LOAD requests
|
/** Create packets
|
||||||
* @param pd ProtocolP2PPacketUDP
|
* @param payload Payload
|
||||||
*/
|
*/
|
||||||
public void loadRequestManagement(ProtocolP2PPacketUDP pd){
|
protected < T extends Payload > ProtocolP2PPacket createProtocolP2PPacket(T payload) {
|
||||||
Payload p = pd.getPayload();
|
return (ProtocolP2PPacket)new ProtocolP2PPacketUDP((Payload) payload);
|
||||||
logger.writeUDP("Received LOAD_REQUEST", LogLevel.Action);
|
|
||||||
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) {
|
|
||||||
logger.writeUDP("Sending last partialContent", LogLevel.Debug);
|
|
||||||
sizeToSend = fullLoad.length - offset;
|
|
||||||
} else {
|
|
||||||
sizeToSend = maxSizePartialContent;
|
|
||||||
}
|
|
||||||
logger.writeUDP("maxSizePartialContent: " + maxSizePartialContent, LogLevel.Debug);
|
|
||||||
logger.writeUDP("Sending " + filename + " from " + offset + " to " + (offset + sizeToSend), LogLevel.Debug);
|
|
||||||
byte[] load = Arrays.copyOfRange(fullLoad, (int)offset, (int)(offset + sizeToSend));
|
|
||||||
if (Arrays.binarySearch(fileList, filename) >= 0) {
|
|
||||||
try {
|
|
||||||
if (load.length == 0) {
|
|
||||||
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.EMPTY_FILE)));
|
|
||||||
} else {
|
|
||||||
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP((Payload)(new FilePart(filename, fullLoad.length, offset, load))));
|
|
||||||
}
|
|
||||||
} catch (Exception e2) {
|
|
||||||
logger.writeUDP(e2, LogLevel.Error);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.writeUDP("File requested not found: `" + filename + "` " + Arrays.binarySearch(fileList, filename), LogLevel.Debug);
|
|
||||||
logger.writeUDP("File list:", LogLevel.Debug);
|
|
||||||
for (String f: fileList) {
|
|
||||||
logger.writeUDP("- " + f, LogLevel.Debug);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new IOException(); // to send a NOT_FOUND in the catch block
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
try {
|
|
||||||
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.NOT_FOUND)));
|
|
||||||
} catch (Exception e2) {
|
|
||||||
logger.writeUDP(e2, LogLevel.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Respond to LIST requests
|
/** Getter for tracker socket
|
||||||
* @param pd ProtocolP2PPacketUDP
|
|
||||||
*/
|
*/
|
||||||
public void listRequestManagement(ProtocolP2PPacketUDP pd) {
|
protected Object getTrackerSocket() {
|
||||||
logger.writeUDP("Received LIST_REQUEST", LogLevel.Action);
|
return (Object)tracker.getUDPSocket();
|
||||||
try {
|
|
||||||
if (fileList.length == 0) {
|
|
||||||
logger.writeUDP("Sending EMPTY_DIRECTORY to host " + pd.getHostItem(), LogLevel.Action);
|
|
||||||
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.EMPTY_DIRECTORY)));
|
|
||||||
} else {
|
|
||||||
logger.writeUDP("Sending LIST_RESPONSE to host " + pd.getHostItem(), LogLevel.Action);
|
|
||||||
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP((Payload)(new FileList(fileList))));
|
|
||||||
}
|
|
||||||
} catch (Exception e2) {
|
|
||||||
logger.writeUDP(e2, LogLevel.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Send a NotATracker error message.
|
|
||||||
* @param pd ProtocolP2PPacketUDP to respond
|
|
||||||
*/
|
|
||||||
private void sendNotATracker(ProtocolP2PPacketUDP pd) {
|
|
||||||
try {
|
|
||||||
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.NOT_A_TRACKER)));
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.writeUDP(e, LogLevel.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Send hash response to hash request
|
|
||||||
* @param pd Request received
|
|
||||||
*/
|
|
||||||
private void sendHashResponse(ProtocolP2PPacketUDP 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(fileList, filename) >= 0) {
|
|
||||||
Map<HashAlgorithm, byte[]> hashes = new HashMap<>();
|
|
||||||
for (HashAlgorithm h : ((HashRequest)p).getAlgoList()) {
|
|
||||||
switch (h) {
|
|
||||||
case SHA512:
|
|
||||||
hashes.put(h, sha512.get(filename));
|
|
||||||
break;
|
|
||||||
case MD5:
|
|
||||||
default:
|
|
||||||
hashes.put(h, new byte[0]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP((Payload)(new HashResponse(filename, hashes))));
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.writeUDP(e, LogLevel.Error);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// file not found
|
|
||||||
try {
|
|
||||||
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.NOT_FOUND)));
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.writeUDP(e, LogLevel.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Private runnable class allowing to keep the tracker informed about file list. */
|
|
||||||
private class FileListWatcher implements Runnable {
|
|
||||||
private volatile boolean stop;
|
|
||||||
private long time;
|
|
||||||
private boolean force;
|
|
||||||
|
|
||||||
/** Register server on tracker
|
|
||||||
*/
|
|
||||||
private void registerTracker() {
|
|
||||||
try {
|
|
||||||
logger.writeUDP("Trying to register into tracker", LogLevel.Info);
|
|
||||||
ProtocolP2PPacket p = (ProtocolP2PPacket)new ProtocolP2PPacketUDP((Payload)new Register(server));
|
|
||||||
p.sendRequest((Object)tracker.getUDPSocket());
|
|
||||||
logger.writeUDP("Register request sent (but cannot ensure reception).", LogLevel.Debug);
|
|
||||||
tracker.closeUDPSocket();
|
|
||||||
} catch (Exception e) {
|
|
||||||
force = true;
|
|
||||||
logger.writeUDP("Cannot contact tracker, trying again at next iteration (" + time + " milliseconds).", LogLevel.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Update fileList and returns true if different than old list.
|
|
||||||
* @return true if changed
|
|
||||||
*/
|
|
||||||
private 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Constructor with millis parameter
|
|
||||||
* @param millis interval of time between checks
|
|
||||||
*/
|
|
||||||
public FileListWatcher(long millis) {
|
|
||||||
this.force = true;
|
|
||||||
this.stop = false;
|
|
||||||
this.time = millis;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Ask the thread to stop
|
|
||||||
*/
|
|
||||||
public void setStop() {
|
|
||||||
stop = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Allow a manual check
|
|
||||||
*/
|
|
||||||
public void trigger() {
|
|
||||||
if (updateFileList() || force) {
|
|
||||||
force = false;
|
|
||||||
logger.writeUDP("File list watcher detected changes. Informing tracker.", LogLevel.Info);
|
|
||||||
registerTracker();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Runnable implementation */
|
|
||||||
public void run() {
|
|
||||||
logger.writeUDP("File list watcher started : delay " + time + " milliseconds.", LogLevel.Info);
|
|
||||||
while(!stop) {
|
|
||||||
trigger();
|
|
||||||
try {
|
|
||||||
Thread.sleep(time);
|
|
||||||
} catch(InterruptedException e) {
|
|
||||||
logger.writeUDP("File list watcher interrupted", LogLevel.Error);
|
|
||||||
setStop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user