package tracker; import tools.Logger; import tools.LogLevel; import java.net.DatagramSocket; import protocolP2P.ProtocolP2PPacketUDP; import protocolP2P.ProtocolP2PPacket; import protocolP2P.RequestResponseCode; import protocolP2P.Payload; import protocolP2P.Register; import protocolP2P.Unregister; import tools.HostItem; import java.util.ArrayList; import java.util.List; import java.io.IOException; import java.net.SocketException; import exception.LocalException; import java.util.Map; import java.util.HashMap; import protocolP2P.DiscoverRequest; import protocolP2P.DiscoverResponse; import protocolP2P.FileList; import localException.InternalError; import remoteException.EmptyDirectory; import java.net.InetAddress; import java.net.UnknownHostException; /** Tracker management implementation with udp * @author Louis Royer * @author Flavien Haas * @author JS Auge * @version 1.0 */ public class TrackerManagementUDP implements Runnable { private HostItem tracker; private Logger logger; private DatagramSocket socket; private List hostList = new ArrayList<>(); private Map> fileList = new HashMap<>(); /** Constructor with port and logger. * @param port Port used to listen. * @param logger Logger object */ public TrackerManagementUDP(int port, Logger logger) { tracker = new HostItem("localhost", port); this.logger = logger; try { socket = new DatagramSocket(tracker.getPort(), tracker.getInetAddress()); } catch (SocketException e) { logger.writeUDP("Error: cannot listen on port " + port, LogLevel.Error); System.exit(-1); } } /** Implementation of runnable. This methods allows to run the tracker. */ public void run() { logger.writeUDP("Tracker successfully started", LogLevel.Info); while(true) { try { ProtocolP2PPacketUDP pd = new ProtocolP2PPacketUDP((Object)socket); Payload p = pd.getPayload(); switch (p.getRequestResponseCode()) { case LOAD_REQUEST: logger.writeUDP("Received LOAD_REQUEST from host " + pd.getHostItem() + ", sending NOT_FOUND", LogLevel.Action); sendNotFound(pd); break; case LIST_REQUEST: logger.writeUDP("Received LIST_REQUEST from host " + pd.getHostItem() + ", sending EMPTY_DIRECTORY", LogLevel.Action); sendEmptyDirectory(pd); break; case HASH_REQUEST: logger.writeUDP("Received HASH_REQUEST from host " + pd.getHostItem() + ", sending NOT_FOUND", LogLevel.Action); sendNotFound(pd); break; case REGISTER: logger.writeUDP("Received REGISTER from host " + pd.getHostItem(), LogLevel.Debug); handleRegister(pd); break; case UNREGISTER: logger.writeUDP("Received UNREGISTER from host " + pd.getHostItem(), LogLevel.Debug); handleUnregister(pd); break; case DISCOVER_REQUEST: handleDiscover(pd); break; default: logger.writeUDP("Received grabbage from host " + pd.getHostItem(), LogLevel.Action); sendInternalError(pd); break; } } catch (IOException e) { logger.writeUDP(e, LogLevel.Warning); } catch (LocalException e) { logger.writeUDP(e, LogLevel.Warning); } } } /** Send a not found message. * @param pd ProtocolP2PPacketUDP to respond */ private void sendNotFound(ProtocolP2PPacketUDP pd) { try { pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.NOT_FOUND))); } catch (Exception e) { logger.writeUDP(e, LogLevel.Error); } } /** Send an empty directory message. * @param pd ProtocolP2PPacketUDP to respond */ private void sendEmptyDirectory(ProtocolP2PPacketUDP pd) { try { pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.EMPTY_DIRECTORY))); } catch (Exception e) { logger.writeUDP(e, LogLevel.Error); } } /** 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); } } /** Handle Registering * @param pd ProtocolP2PPacketUDP to respond * @throws InternalError */ private void handleRegister(ProtocolP2PPacketUDP pd) throws InternalError { Payload p = pd.getPayload(); assert p instanceof Register : "payload must be an instance of Register"; if (!(p instanceof Register)) { sendInternalError(pd); throw new InternalError(); } // add host to known host list HostItem host = ((Register)p).getHostItem(); if (!hostList.contains(host)) { hostList.add(host); } // send a list request try { ProtocolP2PPacket pLReq = (ProtocolP2PPacket)new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.LIST_REQUEST)); pLReq.sendRequest((Object)host.getUDPSocket()); logger.writeUDP("Received REGISTER from host " + pd.getHostItem() + ". Adding host " + host + " to list. Sending List request", LogLevel.Action); ProtocolP2PPacket resp = pLReq.receiveResponse(); handleListResponse((ProtocolP2PPacketUDP)resp, host); logger.writeUDP("Received LIST RESPONSE from host " + pd.getHostItem(), LogLevel.Action); host.closeUDPSocket(); } catch (EmptyDirectory e) { logger.writeUDP("Empty Directory", LogLevel.Debug); hostList.remove(host); logger.writeUDP("Received EMPTY DIRECTORY from host " + pd.getHostItem() + ". Aborting.", LogLevel.Action); } catch (Exception e) { // remove from list because list request could not be send hostList.remove(host); logger.writeUDP("Aborting the add of host " + host, LogLevel.Action); logger.writeUDP(e, LogLevel.Error); } } /** Handle Unregistering * @param pd ProtocolP2PPacketUDP to respond * @throws InternalError */ private void handleUnregister(ProtocolP2PPacketUDP pd) throws InternalError { Payload p = pd.getPayload(); assert p instanceof Unregister : "payload must be an instance of Unregister"; if (!(p instanceof Unregister)) { sendInternalError(pd); throw new InternalError(); } HostItem host = ((Unregister)p).getHostItem(); logger.writeUDP("Received UNREGISTER from host " + pd.getHostItem() + ". Removing host " + host, LogLevel.Action); hostList.remove(host); for(String f: fileList.keySet()) { fileList.get(f).remove(host); if(fileList.get(f).isEmpty()) { fileList.remove(f); } } } /** Handle Discover request * @param pd ProtocolP2PPacketUDP to respond * @throws InternalError */ private void handleDiscover(ProtocolP2PPacketUDP pd) throws InternalError { logger.writeUDP("Received DISCOVER REQUEST from host " + pd.getHostItem(), LogLevel.Action); Payload p = pd.getPayload(); assert p instanceof DiscoverRequest : "payload must be an instance of DiscoverRequest"; if (!(p instanceof DiscoverRequest)) { sendInternalError(pd); } else { String filename = ((DiscoverRequest)p).getFilename(); try { pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP((Payload)new DiscoverResponse(filename, fileList.getOrDefault(filename, hostList)))); } catch (Exception e) { logger.writeUDP(e, LogLevel.Error); } } } /** Handle List Responses * @param pd ProtocolP2PPacketUDP response * @throws InternalError */ private void handleListResponse(ProtocolP2PPacketUDP pd, HostItem host) throws InternalError { Payload p = pd.getPayload(); assert p instanceof FileList: "payload must be an instance of FileList"; if (!(p instanceof FileList)) { throw new InternalError(); } else { String[] f = ((FileList)p).getFileList(); for (String file: f) { List h = fileList.get(file); if (h != null) { if (!h.contains(host)) { h.add(host); } } else { List emptyH = new ArrayList<>(); emptyH.add(host); fileList.put(file, emptyH); } } } } }