Classes abstraites pour le Serveur

Fix #40
This commit is contained in:
Louis Royer 2020-03-22 12:23:56 +01:00
parent 33f1829cdc
commit ef5484566e
8 changed files with 582 additions and 526 deletions

View File

@ -130,6 +130,9 @@ public class ClientP2P {
t.join();
} catch (InterruptedException e) {}
c.scanner.close();
smudp.setStop();
smtcp.setStop();
}
}
}

View File

@ -49,7 +49,7 @@ public abstract class ProtocolP2PPacket {
* @throws IOException
* @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
* @return hostItem of the sender

View 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);
}

View 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);
}
}
}

View 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);
}
}
}

View 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);
}
}
}

View File

@ -34,6 +34,8 @@ import protocolP2P.HashRequest;
import protocolP2P.HashResponse;
import protocolP2P.Register;
import protocolP2P.Unregister;
import serverP2P.ServerManagement;
import serverP2P.FileWatcherTCP;
/** Implementation of P2P-JAVA-PROJECT VERSION 1.0 protocol for TCP.
@ -42,17 +44,9 @@ import protocolP2P.Unregister;
* @author JS Auge
* @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 Logger logger;
private HostItem tracker;
private HostItem server;
private FileListWatcher fileListWatcher;
private volatile boolean stop;
/** Constructor for TCP implementation, with baseDirectory and TCPPort parameters.
* @param baseDirectory the root directory where files are stored
@ -62,12 +56,11 @@ public class ServerManagementTCP implements Runnable {
* @param tracker Tracker
*/
public ServerManagementTCP(String baseDirectory, String hostName, int port, Logger logger, HostItem tracker) {
stop = false;
server = new HostItem(hostName, port);
fileList = new String[0];
this.tracker = tracker;
this.logger = logger;
this.baseDirectory = baseDirectory;
super(baseDirectory, new HostItem(hostName, port), tracker, 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";
try {
socket = new ServerSocket(server.getPort(), 10, server.getInetAddress());
} 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.
*/
public void run() {
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();
while(!stop) {
try {
@ -108,15 +89,7 @@ public class ServerManagementTCP implements Runnable {
}
}
fileListWatcher.setStop();
// unregistering from tracker
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);
}
sendUnregisterRequest();
}
/** Private runnable class allowing to serve one client.
@ -193,240 +166,32 @@ public class ServerManagementTCP implements Runnable {
}
}
/** Init sha512 map.
/** Implementation of writeLog
* @param text Text to log
* @param logLevel level of logging
*/
private 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) {
logger.writeTCP("sha512 not supported", LogLevel.Error);
} catch (IOException e) {
logger.writeTCP("cannot read " + f, LogLevel.Warning);
}
}
protected void writeLog(String text, LogLevel logLevel) {
logger.writeTCP(text, logLevel);
}
/** Send an internal error message.
* @param pd ProtocolP2PPacketTCP to respond
/** Implementation of writeLog
* @param e exception to log
* @param logLevel level of logging
*/
private void sendInternalError(ProtocolP2PPacketTCP pd) {
logger.writeTCP("Internal Error", LogLevel.Warning);
try {
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.INTERNAL_ERROR)));
} catch (Exception e) {
logger.writeTCP(e, LogLevel.Error);
}
protected void writeLog(Exception e, LogLevel logLevel) {
logger.writeTCP(e, logLevel);
}
/** Send a NotATracker error message.
* @param pd ProtocolP2PPacketTCP to respond
/** Create packets
* @param payload Payload
*/
private void sendNotATracker(ProtocolP2PPacketTCP pd) {
try {
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.NOT_A_TRACKER)));
} catch (Exception e) {
logger.writeTCP(e, LogLevel.Error);
}
protected < T extends Payload > ProtocolP2PPacket createProtocolP2PPacket(T payload) {
return (ProtocolP2PPacket)new ProtocolP2PPacketTCP((Payload) payload);
}
/** Send response to list request
* @param pd Request received
/** Getter for tracker socket
*/
private void sendListResponse(ProtocolP2PPacketTCP pd) {
try {
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);
}
protected Object getTrackerSocket() {
return (Object)tracker.getTCPSocket();
}
/** 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();
}
}
}
}
}

View File

@ -34,6 +34,7 @@ import tools.HostItem;
import protocolP2P.Register;
import protocolP2P.Unregister;
import java.net.UnknownHostException;
import serverP2P.ServerManagement;
/** Implementation of P2P-JAVA-PROJECT VERSION 1.0 protocol for UDP.
* @author Louis Royer
@ -41,17 +42,9 @@ import java.net.UnknownHostException;
* @author JS Auge
* @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 Logger logger;
private HostItem tracker;
private HostItem server;
private FileListWatcher fileListWatcher;
private volatile boolean stop;
/** Constructor for UDP implementation, with baseDirectory and UDPPort parameters.
* @param baseDirectory the root directory where files are stored
@ -61,11 +54,11 @@ public class ServerManagementUDP implements Runnable {
* @param tracker Tracker
*/
public ServerManagementUDP(String baseDirectory, String hostName, int port, Logger logger, HostItem tracker) {
stop = false;
server = new HostItem(hostName, port);
this.logger = logger;
this.baseDirectory = baseDirectory;
this.tracker = tracker;
super(baseDirectory, new HostItem(hostName, port), tracker, 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";
try {
socket = new DatagramSocket(server.getPort(), server.getInetAddress());
} 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.
*/
public void run() {
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();
while(!stop) {
try {
@ -99,10 +80,11 @@ public class ServerManagementUDP implements Runnable {
Payload p = pd.getPayload();
switch (p.getRequestResponseCode()) {
case LOAD_REQUEST:
loadRequestManagement(pd);
sendLoadResponse(pd);
break;
case LIST_REQUEST:
listRequestManagement(pd);
logger.writeUDP("Received LIST_REQUEST", LogLevel.Action);
sendListResponse(pd);
break;
case HASH_REQUEST:
logger.writeUDP("Received HASH_REQUEST", LogLevel.Action);
@ -127,249 +109,36 @@ public class ServerManagementUDP implements Runnable {
}
}
fileListWatcher.setStop();
// unregistering from tracker
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);
}
sendUnregisterRequest();
}
/** Init sha512 map.
/** Implementation of writeLog
* @param text Text to log
* @param logLevel level of logging
*/
private 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) {
logger.writeUDP("sha512 not supported", LogLevel.Error);
} catch (IOException e) {
logger.writeUDP("cannot read " + f, LogLevel.Warning);
}
}
protected void writeLog(String text, LogLevel logLevel) {
logger.writeUDP(text, logLevel);
}
/** Send an internal error message.
* @param pd ProtocolP2PPacketUDP to respond
/** Implementation of writeLog
* @param e exception to log
* @param logLevel level of logging
*/
private void sendInternalError(ProtocolP2PPacketUDP pd) {
logger.writeUDP("Internal Error", LogLevel.Warning);
try {
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.INTERNAL_ERROR)));
} catch (Exception e) {
logger.writeUDP(e, LogLevel.Error);
}
protected void writeLog(Exception e, LogLevel logLevel) {
logger.writeUDP(e, logLevel);
}
/** Respond to LOAD requests
* @param pd ProtocolP2PPacketUDP
/** Create packets
* @param payload Payload
*/
public void loadRequestManagement(ProtocolP2PPacketUDP pd){
Payload p = pd.getPayload();
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);
}
}
}
protected < T extends Payload > ProtocolP2PPacket createProtocolP2PPacket(T payload) {
return (ProtocolP2PPacket)new ProtocolP2PPacketUDP((Payload) payload);
}
/** Respond to LIST requests
* @param pd ProtocolP2PPacketUDP
/** Getter for tracker socket
*/
public void listRequestManagement(ProtocolP2PPacketUDP pd) {
logger.writeUDP("Received LIST_REQUEST", LogLevel.Action);
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();
}
}
}
protected Object getTrackerSocket() {
return (Object)tracker.getUDPSocket();
}
}