diff --git a/src/clientP2P/ClientP2P.java b/src/clientP2P/ClientP2P.java index 1d1b224..f2ea80f 100644 --- a/src/clientP2P/ClientP2P.java +++ b/src/clientP2P/ClientP2P.java @@ -19,8 +19,8 @@ import tools.HostItem; */ public class ClientP2P { - private String subdir = "seeded/"; - private String parts = ".parts"; + private String logDir = "logs/"; + private String partsDir = ".parts/"; private Logger loggerServer; private Logger loggerClient; private String host; @@ -34,11 +34,13 @@ public class ClientP2P { /** Initialize loggers if directories and logger are null, * else fail silently. */ - public void initLogger() { + public void initDirectoriesAndLoggers() { if (directories == null && loggerServer == null && loggerClient == null) { directories = new Directories("P2P_JAVA_PROJECT_" + port); - loggerServer = new Logger(directories.getDataHomeDirectory() + "server.log"); - loggerClient = new Logger(directories.getDataHomeDirectory() + "client.log"); + directories.createSubdir(logDir); + loggerServer = new Logger(directories.getDataHomeDirectory() + logDir + "server.log"); + loggerClient = new Logger(directories.getDataHomeDirectory() + logDir + "client.log"); + directories.createSubdir(partsDir); } } @@ -53,16 +55,14 @@ public class ClientP2P { } catch (NumberFormatException e){ int oldPort = port; port = defaultPort; - initLogger(); + initDirectoriesAndLoggers(); System.err.println("Error incorrect port " + oldPort + " using default port " + defaultPort); loggerServer.write("incorrect port " + oldPort + " using default port " + defaultPort, LogLevel.Info); } - initLogger(); - directories.createSubdir(subdir); - directories.createSubdir(parts); + initDirectoriesAndLoggers(); host = "localhost"; - System.out.println("Server will listen on port " + port + " and serve files from " + directories.getDataHomeDirectory() + subdir); - directories.askOpenDataHomeDirectory(subdir, scanner); + System.out.println("Server will listen on port " + port + " and serve files from " + directories.getDataHomeDirectory()); + directories.askOpenDataHomeDirectory(null, scanner); System.out.println("Please enter list of servers to use; first one will be used to ask list of files"); } @@ -84,8 +84,8 @@ public class ClientP2P { } // Server threads - ServerManagementUDP smudp = new ServerManagementUDP(c.directories.getDataHomeDirectory() + c.subdir, "localhost", c.port, c.loggerServer, c.tracker); - ServerManagementTCP smtcp = new ServerManagementTCP(c.directories.getDataHomeDirectory() + c.subdir, "localhost", c.port, c.loggerServer, c.tracker); + ServerManagementUDP smudp = new ServerManagementUDP(c.directories.getDataHomeDirectory(), "localhost", c.port, c.loggerServer, c.tracker); + ServerManagementTCP smtcp = new ServerManagementTCP(c.directories.getDataHomeDirectory(), "localhost", c.port, c.loggerServer, c.tracker); Thread tudp = new Thread(smudp); tudp.setName("server UDP P2P-JAVA-PROJECT (port: " + c.port + ")"); tudp.start(); @@ -96,7 +96,7 @@ public class ClientP2P { // Wait a bit before printing client interface // This is not required, but allow to have a cleaner interface try { - Thread.sleep(100); + Thread.sleep(200); } catch(InterruptedException e) { Thread.currentThread().interrupt(); } @@ -112,7 +112,7 @@ public class ClientP2P { case "upd": // alias typo case "2" : System.out.println("Starting with UDP"); - ClientManagementUDP cmudp = new ClientManagementUDP(c.directories.getDataHomeDirectory(), c.tracker, c.directories.getDataHomeDirectory() + c.parts + "/", c.loggerClient, c.scanner); + ClientManagementUDP cmudp = new ClientManagementUDP(c.directories.getDataHomeDirectory(), c.tracker, c.directories.getDataHomeDirectory() + c.partsDir, c.loggerClient, c.scanner); t = new Thread(cmudp); break; case "TCP": @@ -120,7 +120,7 @@ public class ClientP2P { case "1": default: System.out.println("Starting with TCP"); - ClientManagementTCP cmtcp = new ClientManagementTCP(c.directories.getDataHomeDirectory(), c.tracker, c.directories.getDataHomeDirectory() + c.parts + "/", c.loggerClient, c.scanner); + ClientManagementTCP cmtcp = new ClientManagementTCP(c.directories.getDataHomeDirectory(), c.tracker, c.directories.getDataHomeDirectory() + c.partsDir, c.loggerClient, c.scanner); t = new Thread(cmtcp); break; } diff --git a/src/exception/LocalException.java b/src/exception/LocalException.java index 5c115d5..2fbc0f2 100644 --- a/src/exception/LocalException.java +++ b/src/exception/LocalException.java @@ -1,5 +1,5 @@ package exception; -public class LocalException extends Exception { +public abstract class LocalException extends Exception { private static final long serialVersionUID = 12L; } diff --git a/src/exception/RemoteException.java b/src/exception/RemoteException.java index cfb2cd7..968c8db 100644 --- a/src/exception/RemoteException.java +++ b/src/exception/RemoteException.java @@ -1,5 +1,5 @@ package exception; -public class RemoteException extends Exception { +public abstract class RemoteException extends Exception { private static final long serialVersionUID = 12L; } diff --git a/src/serverP2P/ServerManagementTCP.java b/src/serverP2P/ServerManagementTCP.java index 359d616..5cbe8af 100644 --- a/src/serverP2P/ServerManagementTCP.java +++ b/src/serverP2P/ServerManagementTCP.java @@ -51,6 +51,8 @@ public class ServerManagementTCP implements Runnable { 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 @@ -60,12 +62,12 @@ 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; - initFileList(); - initSha512(); try { socket = new ServerSocket(server.getPort(), 10, server.getInetAddress()); } catch (SocketException e) { @@ -77,12 +79,26 @@ 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); - (new Thread(new TrackerRegisterer())).start(); - do { + fileListWatcher = new FileListWatcher(10000); // checking every 10 seconds + (new Thread(fileListWatcher)).start(); + while(!stop) { try { Socket s = socket.accept(); ClientHandler c = new ClientHandler(s); @@ -90,7 +106,17 @@ public class ServerManagementTCP implements Runnable { } catch (IOException e) { logger.writeTCP("Error while accepting new connection", LogLevel.Warning); } - } while(true); + } + 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); + } } /** Private runnable class allowing to serve one client. @@ -167,22 +193,6 @@ public class ServerManagementTCP implements Runnable { } } - /** Initialize local list of all files allowed to be shared. - */ - private void initFileList() { - 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()); - } - } - fileList = new String[v.size()]; - v.toArray(fileList); - Arrays.sort(fileList); - } /** Init sha512 map. */ @@ -333,39 +343,89 @@ public class ServerManagementTCP implements Runnable { } } - /** Private runnable class allowing to initialize tracker while initializing server. */ - private class TrackerRegisterer implements Runnable { - - /** Runnable implementation */ - public void run() { + /** 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 { - registerTracker(); + 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) { - logger.writeTCP(e, LogLevel.Error); - System.exit(-4); + // error, trying again at next iteration + force = true; + logger.writeTCP("Cannot contact tracker, trying again at next iteration (" + time + " milliseconds).", LogLevel.Error); } } - /** Register server on tracker - * @throws InternalError - * @throws IOException - * @throws SocketClosed + + /** Update fileList and returns true if different than old list. + * @return true if changed */ - private void registerTracker() throws InternalError, IOException, SocketClosed { - logger.writeTCP("Unregistering from tracker", LogLevel.Info); - ProtocolP2PPacket p = (ProtocolP2PPacket)new ProtocolP2PPacketTCP((Payload)new Unregister(server)); - p.sendRequest((Object)tracker.getTCPSocket()); - // FIXME: this is a hack - // ProtocolP2PPacketTCP reads 1024 bytes but if 2 request comes at the same time InputStream is cleared fully - // and we keep waiting forever on the other side - // a fix could be to read only the header first, and read the required size after - try { - Thread.sleep(100); - } catch (InterruptedException e) {} - logger.writeTCP("Registering into tracker", LogLevel.Info); - p = (ProtocolP2PPacket)new ProtocolP2PPacketTCP((Payload)new Register(server)); - p.sendRequest((Object)tracker.getTCPSocket()); - logger.writeTCP("Registering completed", LogLevel.Debug); - tracker.closeTCPSocket(); + 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(); + } + } } } diff --git a/src/serverP2P/ServerManagementUDP.java b/src/serverP2P/ServerManagementUDP.java index fbc0795..25a988a 100644 --- a/src/serverP2P/ServerManagementUDP.java +++ b/src/serverP2P/ServerManagementUDP.java @@ -43,13 +43,15 @@ import java.net.UnknownHostException; */ public class ServerManagementUDP implements Runnable { - private String[] fileList; + 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 @@ -59,12 +61,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; - initFileList(); - initSha512(); try { socket = new DatagramSocket(server.getPort(), server.getInetAddress()); } catch (SocketException e) { @@ -73,12 +74,26 @@ 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); - (new Thread(new TrackerRegisterer())).start(); - while(true) { + fileListWatcher = new FileListWatcher(10000); // checking every 10 seconds + (new Thread(fileListWatcher)).start(); + while(!stop) { try { ProtocolP2PPacketUDP pd = new ProtocolP2PPacketUDP((Object)socket); Payload p = pd.getPayload(); @@ -111,23 +126,15 @@ public class ServerManagementUDP implements Runnable { } catch (SizeError e) { } } - } - - /** Initialize local list of all files allowed to be shared. - */ - private void initFileList() { - 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()); - } + 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); } - fileList = new String[v.size()]; - v.toArray(fileList); - Arrays.sort(fileList); } /** Init sha512 map. @@ -280,32 +287,89 @@ public class ServerManagementUDP implements Runnable { } } -/** Private runnable class allowing to initialize tracker while initializing server. */ - private class TrackerRegisterer implements Runnable { - - /** Runnable implementation */ - public void run() { + /** 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 { - registerTracker(); + 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) { - logger.writeUDP(e, LogLevel.Error); - System.exit(-4); + force = true; + logger.writeUDP("Cannot contact tracker, trying again at next iteration (" + time + " milliseconds).", LogLevel.Error); } } - /** Register server on tracker - * @throws InternalError - * @throws IOException - * @throws SocketClosed + + /** Update fileList and returns true if different than old list. + * @return true if changed */ - private void registerTracker() throws InternalError, IOException, SocketClosed { - logger.writeUDP("Unregistering from tracker", LogLevel.Info); - ProtocolP2PPacket p = (ProtocolP2PPacket)new ProtocolP2PPacketUDP((Payload)new Unregister(server)); - p.sendRequest((Object)tracker.getUDPSocket()); - logger.writeUDP("Registering into tracker", LogLevel.Info); - p = (ProtocolP2PPacket)new ProtocolP2PPacketUDP((Payload)new Register(server)); - p.sendRequest((Object)tracker.getUDPSocket()); - logger.writeUDP("Registering completed", LogLevel.Debug); - tracker.closeUDPSocket(); + 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(); + } + } } } } diff --git a/src/tools/HostItem.java b/src/tools/HostItem.java index 231e6b7..95960af 100644 --- a/src/tools/HostItem.java +++ b/src/tools/HostItem.java @@ -49,6 +49,19 @@ public class HostItem { return tcpSocket; } + /** Get TCP Socket. + * @return TCP Socket + * @throws SocketException + * @throws UnknownHostException + * @throws IOException + */ + public Socket tryGetTCPSocket() throws SocketException, UnknownHostException, IOException { + if (tcpSocket == null) { + tcpSocket = new Socket(InetAddress.getByName(hostname), port); + } + return tcpSocket; + } + /** Closes tcp socket */ public void closeTCPSocket() { diff --git a/src/tracker/TrackerManagementUDP.java b/src/tracker/TrackerManagementUDP.java index 4d31c2f..42d6445 100644 --- a/src/tracker/TrackerManagementUDP.java +++ b/src/tracker/TrackerManagementUDP.java @@ -75,9 +75,11 @@ public class TrackerManagementUDP implements Runnable { 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: