package serverP2P; import serverP2P.FileWatcher; import serverP2P.RatioWatcher; import tools.Logger; import tools.LogLevel; import tools.HostItem; import tools.ServeErrors; import protocolP2P.ProtocolP2PPacket; import protocolP2P.Payload; import protocolP2P.RequestResponseCode; import protocolP2P.FileList; import protocolP2P.FilePart; import protocolP2P.LoadRequest; import protocolP2P.HashRequest; import protocolP2P.HashResponse; import protocolP2P.HashAlgorithm; import protocolP2P.Unregister; import protocolP2P.SizeRequest; import protocolP2P.SizeResponse; import protocolP2P.Denied; import java.nio.file.Paths; import java.nio.file.Files; import java.io.File; import java.util.Arrays; import java.util.Map; import java.util.HashMap; import java.util.Random; import java.io.IOException; import exception.LocalException; import localException.InternalError; import remoteException.UnknownHost; /** Implementation of P2P-JAVA-PROJECT VERSION 1.0 protocol. * @author Louis Royer * @author Flavien Haas * @author JS Auge * @version 1.0 */ public abstract class ServerManagement extends ServeErrors implements Runnable { protected volatile boolean stop; protected FileWatcher fileListWatcher; protected Logger logger; protected String baseDirectory; protected HostItem server; protected HostItem tracker; protected Random punisher = new Random(); protected RatioWatcher ratioWatcher; /** Constructor */ public ServerManagement(String baseDirectory, HostItem server, HostItem tracker, Logger logger) { super(); assert baseDirectory != null : "baseDirectory is null"; assert server != null : "server is null"; assert tracker != null : "tracker is null"; assert logger != null : "logger is null"; stop = false; this.baseDirectory = baseDirectory; this.server = server; this.tracker = tracker; this.logger = logger; } /** Stop the thread */ public void setStop() { stop = true; fileListWatcher.setStop(); ratioWatcher.setStop(); sendUnregisterRequest(); closeSocket(); } /** Closes socket */ protected abstract void closeSocket(); /** Trigger a manual check of the file list */ public void updateFileList() { if (fileListWatcher != null) { fileListWatcher.trigger(); } } /** Send response to list request * @param pd Request received */ protected < T extends ProtocolP2PPacket > void sendListResponse(T pd) { try { String[] fileList = fileListWatcher.getFileList(); if (fileList.length == 0) { writeLog("Sending EMPTY_DIRECTORY to host " + pd.getHostItem(), LogLevel.Action); sendEmptyDirectory(pd); } else { writeLog("Sending LIST_RESPONSE to host " + pd.getHostItem(), LogLevel.Action); pd.sendResponse(createProtocolP2PPacket((Payload)(new FileList(fileList)))); } } catch (Exception e2) { writeLog(e2, LogLevel.Error); } } /** Send hash response to hash request * @param pd Request received */ protected < T extends ProtocolP2PPacket > void sendHashResponse(T pd) { Payload p = pd.getPayload(); assert p instanceof HashRequest : "payload must be an instance of HashRequest"; if (!(p instanceof HashRequest)) { sendInternalError(pd); } else { String filename = ((HashRequest)p).getFilename(); if (Arrays.binarySearch(fileListWatcher.getFileList(), filename) >= 0) { Map hashes = new HashMap<>(); for (HashAlgorithm h : ((HashRequest)p).getAlgoList()) { switch (h) { case SHA512: hashes.put(h, fileListWatcher.getSha512Map().get(filename)); break; case MD5: default: hashes.put(h, new byte[0]); break; } } try { pd.sendResponse(createProtocolP2PPacket((Payload)(new HashResponse(filename, hashes)))); } catch (Exception e) { writeLog(e, LogLevel.Error); } } else { // file not found try { sendNotFound(pd); } catch (Exception e) { writeLog(e, LogLevel.Error); } } } } /** Send response to load request * @param pd Request received */ protected < T extends ProtocolP2PPacket > void sendLoadResponse(T pd) { Payload p = pd.getPayload(); assert p instanceof LoadRequest : "payload must be an instance of LoadRequest"; if (!(p instanceof LoadRequest)) { sendInternalError(pd); } else { String filename = ((LoadRequest)p).getFilename(); long offset = ((LoadRequest)p).getOffset(); long maxSizePartialContent = ((LoadRequest)p).getMaxSizePartialContent(); try { String[] fileList = fileListWatcher.getFileList(); if (Arrays.binarySearch(fileList, filename) >= 0) { try { double proba = ratioWatcher.getPunishmentProbability(((LoadRequest)p).getHostItem()); if (punisher.nextDouble() <= proba) { writeLog("Sending punishment", LogLevel.Debug); pd.sendResponse(createProtocolP2PPacket(new Denied(filename, offset))); } else { 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)); try { if (load.length == 0) { sendEmptyFile(pd); } else { pd.sendResponse(createProtocolP2PPacket((Payload)(new FilePart(filename, offset, load)))); } } catch (Exception e2) { writeLog(e2, LogLevel.Error); } } } catch (InternalError e) { writeLog("InternalError", LogLevel.Debug); writeLog(e, LogLevel.Debug); sendInternalError(pd); return; } catch (UnknownHost e) { writeLog("Unknown host: " + ((LoadRequest)p).getHostItem(), LogLevel.Debug); writeLog(e, LogLevel.Debug); sendInternalError(pd); return; } catch(LocalException e) { sendInternalError(pd); return; } } else { writeLog("File requested not found: `" + filename + "` " + Arrays.binarySearch(fileList, filename), LogLevel.Debug); writeLog("File list:", LogLevel.Debug); for (String f: fileList) { writeLog("- " + f, LogLevel.Debug); } throw new IOException(); // to send a NOT_FOUND in the catch block } } catch (IOException e) { try { sendNotFound(pd); } catch (Exception e2) { writeLog(e2, LogLevel.Debug); } } } } /** Send response to size request * @param pd Request received */ protected < T extends ProtocolP2PPacket > void sendSizeResponse(T pd) { Payload p = pd.getPayload(); assert p instanceof SizeRequest : "payload must be an instance of SizeRequest"; if (!(p instanceof SizeRequest)) { sendInternalError(pd); } else { String filename = ((SizeRequest)p).getFilename(); try { long size = (new File(baseDirectory + filename)).length(); String[] fileList = fileListWatcher.getFileList(); if (Arrays.binarySearch(fileList, filename) >= 0) { try { if (size == 0) { sendEmptyFile(pd); } else { pd.sendResponse(createProtocolP2PPacket((new SizeResponse(filename, size)))); } } catch (Exception e2) { writeLog(e2, LogLevel.Error); } } else { throw new IOException(); // to send a NOT_FOUND in the catch block } } catch (IOException e) { try { sendNotFound(pd); } catch (Exception e2) { writeLog(e2, LogLevel.Debug); } } } } /** Getter for tracker socket */ protected abstract Object getTrackerSocket(); /** Send unregister request to tracker */ protected void sendUnregisterRequest() { // unregistering from tracker try { writeLog("Unregistering from tracker", LogLevel.Info); createProtocolP2PPacket(new Unregister(server)).sendRequest(getTrackerSocket()); } catch (Exception e) { writeLog("Cannot unregister from tracker", LogLevel.Error); writeLog(e, LogLevel.Error); } } /** Handle request. * @throws LocalException */ protected < T extends ProtocolP2PPacket > void handleRequest(T pd) throws LocalException { Payload p = pd.getPayload(); switch (p.getRequestResponseCode()) { case LOAD_REQUEST: writeLog("Received LOAD_REQUEST from host " + pd.getHostItem(), LogLevel.Action); sendLoadResponse(pd); break; case SIZE_REQUEST: writeLog("Received SIZE_REQUEST from host " + pd.getHostItem(), LogLevel.Action); sendSizeResponse(pd); break; case LIST_REQUEST: writeLog("Received LIST_REQUEST from host " + pd.getHostItem(), LogLevel.Action); sendListResponse(pd); break; case HASH_REQUEST: writeLog("Received HASH_REQUEST from host " + pd.getHostItem(), LogLevel.Action); sendHashResponse(pd); break; case DISCOVER_REQUEST: writeLog("Received DISCOVER_REQUEST from host " + pd.getHostItem(), LogLevel.Action); sendNotATracker(pd); break; case UNREGISTER: writeLog("Received UNREGISTER from host " + pd.getHostItem(), LogLevel.Action); sendNotATracker(pd); break; case REGISTER: writeLog("Received REGISTER from host " + pd.getHostItem(), LogLevel.Action); sendNotATracker(pd); break; default: writeLog("Received grabbage from host " + pd.getHostItem(), LogLevel.Action); sendInternalError(pd); } } }