diff --git a/src/clientP2P/ClientP2P.java b/src/clientP2P/ClientP2P.java index f2ea80f..28d1b67 100644 --- a/src/clientP2P/ClientP2P.java +++ b/src/clientP2P/ClientP2P.java @@ -130,6 +130,9 @@ public class ClientP2P { t.join(); } catch (InterruptedException e) {} c.scanner.close(); + smudp.setStop(); + smtcp.setStop(); + } } } diff --git a/src/protocolP2P/ProtocolP2PPacket.java b/src/protocolP2P/ProtocolP2PPacket.java index 74a1dcd..c0312c4 100644 --- a/src/protocolP2P/ProtocolP2PPacket.java +++ b/src/protocolP2P/ProtocolP2PPacket.java @@ -49,7 +49,7 @@ public abstract class ProtocolP2PPacket { * @throws IOException * @throws SocketClosed */ - public abstract void sendResponse(ProtocolP2PPacket response) throws InternalError, IOException, SocketClosed; + public abstract void sendResponse(T response) throws InternalError, IOException, SocketClosed; /** Get hostItem of the sender * @return hostItem of the sender diff --git a/src/serverP2P/FileWatcher.java b/src/serverP2P/FileWatcher.java new file mode 100644 index 0000000..d6bf632 --- /dev/null +++ b/src/serverP2P/FileWatcher.java @@ -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 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 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 v = new Vector(); + 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); + +} diff --git a/src/serverP2P/FileWatcherTCP.java b/src/serverP2P/FileWatcherTCP.java new file mode 100644 index 0000000..9f3936c --- /dev/null +++ b/src/serverP2P/FileWatcherTCP.java @@ -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); + } + } + +} diff --git a/src/serverP2P/FileWatcherUDP.java b/src/serverP2P/FileWatcherUDP.java new file mode 100644 index 0000000..9cc1021 --- /dev/null +++ b/src/serverP2P/FileWatcherUDP.java @@ -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); + } + } + +} diff --git a/src/serverP2P/ServerManagement.java b/src/serverP2P/ServerManagement.java new file mode 100644 index 0000000..3962b5f --- /dev/null +++ b/src/serverP2P/ServerManagement.java @@ -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 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); + } + } + +} diff --git a/src/serverP2P/ServerManagementTCP.java b/src/serverP2P/ServerManagementTCP.java index 5cbe8af..8fa5428 100644 --- a/src/serverP2P/ServerManagementTCP.java +++ b/src/serverP2P/ServerManagementTCP.java @@ -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 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 - */ - 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); - } - } - - /** Send a NotATracker error message. - * @param pd ProtocolP2PPacketTCP to respond + /** Implementation of writeLog + * @param e exception to log + * @param logLevel level of logging */ - 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); - } - } - - /** Send response to list request - * @param pd Request received - */ - 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 void writeLog(Exception e, LogLevel logLevel) { + logger.writeTCP(e, logLevel); } - /** Send hash response to hash request - * @param pd Request received + /** Create packets + * @param payload Payload */ - 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 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); - } - } - } + protected < T extends Payload > ProtocolP2PPacket createProtocolP2PPacket(T payload) { + return (ProtocolP2PPacket)new ProtocolP2PPacketTCP((Payload) payload); } - - /** Send response to load request - * @param pd Request received + /** Getter for tracker socket */ - 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 v = new Vector(); - 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(); - } - } - } + protected Object getTrackerSocket() { + return (Object)tracker.getTCPSocket(); } - } diff --git a/src/serverP2P/ServerManagementUDP.java b/src/serverP2P/ServerManagementUDP.java index 25a988a..7affa75 100644 --- a/src/serverP2P/ServerManagementUDP.java +++ b/src/serverP2P/ServerManagementUDP.java @@ -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 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); - } - } - - /** Init sha512 map. - */ - 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); - } - } + sendUnregisterRequest(); } - /** Send an internal error message. - * @param pd ProtocolP2PPacketUDP to respond - */ - 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); - } - } - /** Respond to LOAD requests - * @param pd ProtocolP2PPacketUDP + /** Implementation of writeLog + * @param text Text to log + * @param logLevel level of logging */ - 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 void writeLog(String text, LogLevel logLevel) { + logger.writeUDP(text, logLevel); } - /** Respond to LIST requests - * @param pd ProtocolP2PPacketUDP + /** Implementation of writeLog + * @param e exception to log + * @param logLevel level of logging */ - 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); - } + protected void writeLog(Exception e, LogLevel logLevel) { + logger.writeUDP(e, logLevel); } - /** Send a NotATracker error message. - * @param pd ProtocolP2PPacketUDP to respond + /** Create packets + * @param payload Payload */ - 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); - } + protected < T extends Payload > ProtocolP2PPacket createProtocolP2PPacket(T payload) { + return (ProtocolP2PPacket)new ProtocolP2PPacketUDP((Payload) payload); } - /** Send hash response to hash request - * @param pd Request received + /** Getter for tracker socket */ - 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 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 v = new Vector(); - 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(); } }