From 15a618ec50323d26555dcab826fa11ce0265fd31 Mon Sep 17 00:00:00 2001 From: Cavailles Kevin Date: Wed, 3 Feb 2021 09:36:28 +0100 Subject: [PATCH] packages communication,observers cleaned and commented --- .../filetransfer/FileTransferClient.java | 37 ++- .../FileTransferReceivingThread.java | 38 ++- .../FileTransferSendingThread.java | 32 ++- .../filetransfer/FileTransferServer.java | 19 +- .../filetransfer/FileTransferUtils.java | 88 ++++--- POO/src/communication/tcp/TCPClient.java | 63 +++-- POO/src/communication/tcp/TCPInputThread.java | 61 ++--- POO/src/communication/tcp/TCPServer.java | 39 +-- .../communication/udp/CommunicationUDP.java | 222 ++++++++++++------ POO/src/communication/udp/UDPClient.java | 60 +++-- POO/src/communication/udp/UDPServer.java | 35 ++- POO/src/observers/ObserverInputMessage.java | 9 +- POO/src/observers/ObserverSocketState.java | 6 + POO/src/observers/ObserverUserList.java | 6 + POO/src/session/ControleurSession.java | 42 ++-- POO/src/standard/ControleurStandard.java | 3 +- 16 files changed, 514 insertions(+), 246 deletions(-) diff --git a/POO/src/communication/filetransfer/FileTransferClient.java b/POO/src/communication/filetransfer/FileTransferClient.java index b0d7d25..5fda49c 100644 --- a/POO/src/communication/filetransfer/FileTransferClient.java +++ b/POO/src/communication/filetransfer/FileTransferClient.java @@ -1,38 +1,49 @@ package communication.filetransfer; - import java.io.File; import java.io.IOException; -import java.net.UnknownHostException; import java.util.ArrayList; import observers.ObserverInputMessage; - - - public class FileTransferClient { - + private int port; - private ArrayList files = null; + private ArrayList files; private ObserverInputMessage obsInput; - - public FileTransferClient(int port, ArrayList filesToSend, ObserverInputMessage obs) throws UnknownHostException, IOException { + /** + * Create a client to transfer one or several files on the specified port of + * localhost. A new Thread is created for each file. The files are sent one by + * one to save bandwidth and avoid issues. + * + * @param port The port of localhost on which to send the files. + * @param filesToSend The file(s) to send. + * @param o The observer to notify each time a file is fully sent. + */ + public FileTransferClient(int port, ArrayList filesToSend, ObserverInputMessage o) { this.port = port; this.files = filesToSend; - this.obsInput = obs; + this.obsInput = o; } + /** + * Try to send every file on localhost on the specified port with a new thread. + * An observer is passed to the thread and it is notified each time a file is + * fully sent. + * + * @throws IOException + * @throws InterruptedException + */ public void sendFiles() throws IOException, InterruptedException { - for(File f: this.files) { - FileTransferSendingThread ftc = new FileTransferSendingThread(this.port, f,this.obsInput); + for (File f : this.files) { + FileTransferSendingThread ftc = new FileTransferSendingThread(this.port, f, this.obsInput); ftc.start(); ftc.join(); } - + } } diff --git a/POO/src/communication/filetransfer/FileTransferReceivingThread.java b/POO/src/communication/filetransfer/FileTransferReceivingThread.java index 75f3062..85cfc71 100644 --- a/POO/src/communication/filetransfer/FileTransferReceivingThread.java +++ b/POO/src/communication/filetransfer/FileTransferReceivingThread.java @@ -1,6 +1,5 @@ package communication.filetransfer; - import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -15,20 +14,31 @@ import messages.MessageFichier; import observers.ObserverInputMessage; public class FileTransferReceivingThread extends Thread { - + private SocketChannel sockTransfert; private ObserverInputMessage obsInput; - - public FileTransferReceivingThread(SocketChannel sock, ObserverInputMessage obs) { + + /** + * Create the thread that will receive one file during a file transfer. This + * allows users to write in the chat while sending/receiving files. + * + * @param sock The SocketChannel returned by ServerSocketChannel.accept(). + * @param o The observer to notify once the file is fully received. + */ + public FileTransferReceivingThread(SocketChannel sock, ObserverInputMessage o) { this.sockTransfert = sock; - this.obsInput = obs; + this.obsInput = o; } public void run() { try { int nbByteRead = 0; + + // Buffer to receive a chunk of the file ByteBuffer fileData = ByteBuffer.allocate(4 * FileTransferUtils.KB_SIZE); + // InputStream to read the first object which is a message containing the name + // and size of the file ObjectInputStream inputFileInformation = new ObjectInputStream( this.sockTransfert.socket().getInputStream()); @@ -41,10 +51,11 @@ public class FileTransferReceivingThread extends Thread { String[] fileInfo = this.processFileInformation(m); String filePath = FileTransferUtils.DOWNLOADS_RELATIVE_PATH + fileInfo[0]; long fileSize = Long.parseLong(fileInfo[1]); - + // OutputStream to create the file if it does not exist FileOutputStream fOutStream = new FileOutputStream(filePath); + // Channel to write the data received in the file FileChannel fileWriter = fOutStream.getChannel(); while (nbTotalBytesRead < fileSize && (nbByteRead = this.sockTransfert.read(fileData)) > 0) { @@ -58,11 +69,13 @@ public class FileTransferReceivingThread extends Thread { fileWriter.close(); fOutStream.close(); - + inputFileInformation.close(); + + // Process the message to display (thumbnails in the case of images) and notify + // the observer Message mUpdate = FileTransferUtils.processMessageToDisplay(new File(filePath)); mUpdate.setSender("other"); - this.obsInput.update(this, mUpdate); - + this.obsInput.updateInput(this, mUpdate); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); @@ -75,6 +88,13 @@ public class FileTransferReceivingThread extends Thread { } } + /** + * Split the content of a message with the separator ";". This function is only + * used to read the name and the size of the file to receive. + * + * @param m message containing the file's information (name and size). + * @return An array with the file's name and the file's size respectively. + */ private String[] processFileInformation(MessageFichier m) { return m.getContenu().split(";"); } diff --git a/POO/src/communication/filetransfer/FileTransferSendingThread.java b/POO/src/communication/filetransfer/FileTransferSendingThread.java index 61aaa2f..60da3e4 100644 --- a/POO/src/communication/filetransfer/FileTransferSendingThread.java +++ b/POO/src/communication/filetransfer/FileTransferSendingThread.java @@ -17,33 +17,46 @@ import messages.MessageFichier; import messages.Message.TypeMessage; import observers.ObserverInputMessage; -public class FileTransferSendingThread extends Thread{ +public class FileTransferSendingThread extends Thread { private SocketChannel sockTransfert; private File file; private ObserverInputMessage obsInput; - public FileTransferSendingThread(int port, File fileToSend, ObserverInputMessage obs) throws IOException { + /** + * Create the thread that will send one file during a file transfer. This allows + * users to write in the chat while sending/receiving files. + * + * @param port The port on localhost to which the SocketChannel will connect. + * @param fileToSend The file to send. + * @param o The observer to notify once the file is fully received. + * @throws IOException if the socket's creation fails. + */ + public FileTransferSendingThread(int port, File fileToSend, ObserverInputMessage o) throws IOException { SocketChannel sock = SocketChannel.open(); SocketAddress addr = new InetSocketAddress(port); sock.connect(addr); this.sockTransfert = sock; this.file = fileToSend; - this.obsInput = obs; + this.obsInput = o; } public void run() { try { + // Buffer to send a chunk of the file ByteBuffer fileData = ByteBuffer.allocate(4 * FileTransferUtils.KB_SIZE); + // OutputStream to write the first object which is a message containing the name + // and size of the file ObjectOutputStream outputFileInformation = new ObjectOutputStream( this.sockTransfert.socket().getOutputStream()); + // Channel to read the data of the file FileChannel fileReader = FileChannel.open(Paths.get(file.getPath())); String str = file.getName() + ";" + file.getTotalSpace(); - // Send file datas (name + size); + // Send file data (name + size); outputFileInformation.writeObject(new MessageFichier(TypeMessage.FICHIER, str, "")); while (fileReader.read(fileData) > 0) { @@ -53,11 +66,14 @@ public class FileTransferSendingThread extends Thread{ } fileReader.close(); - + outputFileInformation.close(); + + // Process the message to display (thumbnails in the case of images) and notify + // the observer Message mUpdate = FileTransferUtils.processMessageToDisplay(this.file); mUpdate.setSender("Moi"); - this.obsInput.update(this, mUpdate); - + this.obsInput.updateInput(this, mUpdate); + } catch (IOException | MauvaisTypeMessageException e) { e.printStackTrace(); } finally { @@ -68,7 +84,5 @@ public class FileTransferSendingThread extends Thread{ } } } - - } diff --git a/POO/src/communication/filetransfer/FileTransferServer.java b/POO/src/communication/filetransfer/FileTransferServer.java index e16db67..3e8530d 100644 --- a/POO/src/communication/filetransfer/FileTransferServer.java +++ b/POO/src/communication/filetransfer/FileTransferServer.java @@ -8,21 +8,32 @@ import java.nio.channels.SocketChannel; import observers.ObserverInputMessage; - public class FileTransferServer extends Thread { private ServerSocketChannel sockFTListen; private int nbFile; private ObserverInputMessage obsInput; - - public FileTransferServer(int nbFile, ObserverInputMessage obs) throws UnknownHostException, IOException { + /** + * Create a server to transfer one or several files. A new socket and thread is + * created for each file to receive. The files are received one by one to save + * bandwidth and avoid issues. + * + * @param nbFile The number of file to receive. + * @param o The observer to notify once a file is fully received. + * @throws UnknownHostException + * @throws IOException + */ + public FileTransferServer(int nbFile, ObserverInputMessage o) throws UnknownHostException, IOException { this.sockFTListen = ServerSocketChannel.open(); this.sockFTListen.socket().bind(new InetSocketAddress(0)); this.nbFile = nbFile; - this.obsInput = obs; + this.obsInput = o; } + /** + * @return The port binded to the ServerSocketChannel. + */ public int getPort() { return this.sockFTListen.socket().getLocalPort(); } diff --git a/POO/src/communication/filetransfer/FileTransferUtils.java b/POO/src/communication/filetransfer/FileTransferUtils.java index cd561ac..734bf08 100644 --- a/POO/src/communication/filetransfer/FileTransferUtils.java +++ b/POO/src/communication/filetransfer/FileTransferUtils.java @@ -23,55 +23,76 @@ import messages.Message.TypeMessage; public class FileTransferUtils { + // Relative path of the folder where are put the received files protected static final String DOWNLOADS_RELATIVE_PATH = "../downloads/"; - protected static final ArrayList IMAGE_EXTENSIONS = new ArrayList(List.of("tif","tiff","bmp","jpg","jpeg","gif", "png", "eps", "svg")); + protected static final ArrayList IMAGE_EXTENSIONS = new ArrayList( + List.of("tif", "tiff", "bmp", "jpg", "jpeg", "gif", "png", "eps", "svg")); protected static final int KB_SIZE = 1024; - - + + /** + * Process what to display on the chat depending on the file sent/received. A + * thumbnail will be created in the case of an image, A String with the file's + * name will be created otherwise. + * + * @param file The file to process. + * @return A message of which content is either a thumbnail or the file's name. + * @throws IOException + */ protected static MessageFichier processMessageToDisplay(File file) throws IOException { String nameFile = file.getName(); - String extension = processFileExtension(nameFile); + String extension = processFileExtension(nameFile); TypeMessage type; String contenu; - - if(IMAGE_EXTENSIONS.contains(extension)) { + + if (IMAGE_EXTENSIONS.contains(extension)) { type = TypeMessage.IMAGE; BufferedImage img = ImageIO.read(file); - contenu = encodeImage(createThumbnail(img), extension) ; - - }else { + contenu = encodeImage(createThumbnail(img), extension); + + } else { type = TypeMessage.FICHIER; contenu = nameFile; } - + try { - //return new MessageFichier(type, contenu, extension); return new MessageFichier(type, contenu, extension); } catch (MauvaisTypeMessageException e) { - System.out.println("Should never go in"); e.printStackTrace(); } return null; } - + + /** + * @param fileName The name of the file (with its extension). + * @return The extension of the file. + */ protected static String processFileExtension(String fileName) { String extension = ""; - + int i = fileName.indexOf('.'); if (i >= 0 || i != -1) { - extension = fileName.substring(i+1).toLowerCase(); + extension = fileName.substring(i + 1).toLowerCase(); } return extension; } - - - private static BufferedImage createThumbnail(BufferedImage image){ + + /** + * @param image A buffered image. + * @return A thumbnail of the image. + */ + private static BufferedImage createThumbnail(BufferedImage image) { float w = image.getWidth(); - float ratio = (w > 150) ? (150F/w) : 1; + float ratio = (w > 150) ? (150F / w) : 1; BufferedImage scaled = scale(image, ratio); return scaled; } - + + /** + * @param img A buffered image. + * @param extension The extension of the image. + * @return The base64 encoded string corresponding to the given image. + * @throws IOException + */ private static String encodeImage(BufferedImage img, String extension) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ImageIO.write(img, extension, bos); @@ -79,16 +100,21 @@ public class FileTransferUtils { bos.close(); return imgString; } - - + + /** + * @param imageString The base64 encoded string of an image. + * @return A buffered image corresponding to the given base64 encoded string. + * @throws IOException + */ public static BufferedImage decodeImage(String imageString) throws IOException { byte[] imgData = Base64.getDecoder().decode(imageString); InputStream is = new ByteArrayInputStream(imgData); - BufferedImage img = ImageIO.read(is); - is.close(); - return img; + BufferedImage img = ImageIO.read(is); + is.close(); + return img; } - + + // Used to scale an image with a given ratio private static BufferedImage scale(BufferedImage source, double ratio) { int w = (int) (source.getWidth() * ratio); int h = (int) (source.getHeight() * ratio); @@ -109,11 +135,15 @@ public class FileTransferUtils { BufferedImage image = gc.createCompatibleImage(w, h); return image; } - + + /** + * Create the folder with the path "DOWNLOADS_RELATIVE_PATH" if it does not + * exist. + */ public static void createDownloads() { File downloads = new File(FileTransferUtils.DOWNLOADS_RELATIVE_PATH); - - if(!downloads.exists()) { + + if (!downloads.exists()) { downloads.mkdir(); } } diff --git a/POO/src/communication/tcp/TCPClient.java b/POO/src/communication/tcp/TCPClient.java index aeaa6c0..c5ca789 100644 --- a/POO/src/communication/tcp/TCPClient.java +++ b/POO/src/communication/tcp/TCPClient.java @@ -5,63 +5,86 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -import java.net.InetAddress; import java.net.Socket; import observers.ObserverInputMessage; import observers.ObserverSocketState; -import messages.MauvaisTypeMessageException; import messages.Message; public class TCPClient { private Socket sockTCP; private ObjectOutputStream output; + private ObjectInputStream input; private TCPInputThread inputThread; - + /** + * Create a TCP client from an existing socket. It will ensure the transmission + * of messages during a session. Two ObjectStream are created in order to + * read/write messages. The ObjectInputStream is given to a thread to read + * continuously the incoming message and allowing the user to write a message + * anytime. + * + * @param sockTCP + * @throws IOException + */ public TCPClient(Socket sockTCP) throws IOException { this.sockTCP = sockTCP; - + this.output = new ObjectOutputStream(sockTCP.getOutputStream()); - ObjectInputStream input = new ObjectInputStream(sockTCP.getInputStream()); - this.inputThread = new TCPInputThread(input); - } - - - public TCPClient(InetAddress addr, int port) throws IOException { - this(new Socket(addr, port)); - + this.input = new ObjectInputStream(sockTCP.getInputStream()); + this.inputThread = new TCPInputThread(this.input); } + /** + * Start the thread that will continuously read from the socket. + */ public void startInputThread() { this.inputThread.start(); } - - public void sendMessage(Message message) throws IOException, MauvaisTypeMessageException { - System.out.println("dans write"); + /** + * Send a message by writing it in the ObjectOutputStream of the socket + * + * @param message + * @throws IOException + */ + public void sendMessage(Message message) throws IOException { this.output.writeObject(message); } - + /** + * Set the observer to notify when a message is received + * + * @param o The observer + */ public void setObserverInputThread(ObserverInputMessage o) { this.inputThread.setObserverInputMessage(o); } - + /** + * Set the observer to notify when the session is closed/the communication is + * broken. + * + * @param o The observer + */ public void setObserverSocketState(ObserverSocketState o) { this.inputThread.setObserverSocketState(o); } - - + + /** + * Method used when the session is over. Set all attribute references to null, + * interrupt the inputThread and close the streams and the socket. + */ public void destroyAll() { try { if (!this.sockTCP.isClosed()) { + this.inputThread.setObserverSocketState(null); + this.inputThread.interrupt(); + this.input.close(); this.output.close(); this.sockTCP.close(); - this.inputThread.setObserverSocketState(null); } this.inputThread = null; this.sockTCP = null; diff --git a/POO/src/communication/tcp/TCPInputThread.java b/POO/src/communication/tcp/TCPInputThread.java index 5287bf5..ff45b4c 100644 --- a/POO/src/communication/tcp/TCPInputThread.java +++ b/POO/src/communication/tcp/TCPInputThread.java @@ -12,7 +12,12 @@ public class TCPInputThread extends Thread { private ObserverInputMessage obsInput; private ObserverSocketState obsState; - public TCPInputThread(ObjectInputStream input) { + /** + * Create the thread used to read the messages + * + * @param input The ObjectInputStream to read data from + */ + protected TCPInputThread(ObjectInputStream input) { this.input = input; this.running = true; } @@ -22,10 +27,9 @@ public class TCPInputThread extends Thread { while (this.running) { try { - - System.out.println("dans read"); - Object o = this.input.readObject(); - this.obsInput.update(this, o); + Object o = this.input.readObject(); + // Notify the observer a message was received + this.obsInput.updateInput(this, o); } catch (IOException | ClassNotFoundException e) { this.interrupt(); @@ -37,34 +41,35 @@ public class TCPInputThread extends Thread { @Override public void interrupt() { - - try { - //Stop the thread - this.running = false; - //Close the stream and the socket - this.input.close(); - - if(this.obsState != null) { - //Send an update to the controller - this.obsState.updateSocketState(this, true); - } - - - //Set every attribute to null so they're collected by the GC - this.obsInput = null; - this.obsState = null; - this.input = null; - - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + // Stop the thread + this.running = false; + // Close the stream and the socket + + if (this.obsState != null) { + // Send an update to the controller + this.obsState.updateSocketState(this, true); } + + // Set every attribute to null so they're collected by the GC + this.obsInput = null; + this.obsState = null; + this.input = null; } - + + /** + * Set the observer to notify when a message is received + * + * @param o The observer + */ protected void setObserverInputMessage(ObserverInputMessage o) { this.obsInput = o; } - + + /** + * Set the observer to notify when the session is cut/closed. + * + * @param o The observer + */ protected void setObserverSocketState(ObserverSocketState o) { this.obsState = o; } diff --git a/POO/src/communication/tcp/TCPServer.java b/POO/src/communication/tcp/TCPServer.java index bdbb16f..6ceb657 100644 --- a/POO/src/communication/tcp/TCPServer.java +++ b/POO/src/communication/tcp/TCPServer.java @@ -8,36 +8,45 @@ import java.net.UnknownHostException; import observers.ObserverInputMessage; - public class TCPServer extends Thread { - - //**** - public static int PORT_SERVER = 7000; private ServerSocket sockListenTCP; - private ObserverInputMessage obs; - + private ObserverInputMessage obsInput; + + /** + * Create a TCP Server on the specified port. It will listen continuously for + * connections in order to create new sessions between users. + * + * @param port The port on which the server will listen + * @throws UnknownHostException + * @throws IOException + */ public TCPServer(int port) throws UnknownHostException, IOException { this.sockListenTCP = new ServerSocket(port, 50, InetAddress.getLocalHost()); } - + @Override public void run() { - System.out.println("TCP running"); Socket sockAccept; - while(true) { + while (true) { try { sockAccept = this.sockListenTCP.accept(); - this.obs.update(this, sockAccept); + + // Notify the observer of the new connexion + this.obsInput.updateInput(this, sockAccept); } catch (IOException e) { - // TODO Auto-generated catch block e.printStackTrace(); } - + } } - - public void addObserver(ObserverInputMessage obs) { - this.obs = obs; + + /** + * Set the observer to notify when a new connection is made. + * + * @param o The observer + */ + public void addObserver(ObserverInputMessage o) { + this.obsInput = o; } } diff --git a/POO/src/communication/udp/CommunicationUDP.java b/POO/src/communication/udp/CommunicationUDP.java index 91eec76..f20cbe2 100644 --- a/POO/src/communication/udp/CommunicationUDP.java +++ b/POO/src/communication/udp/CommunicationUDP.java @@ -2,7 +2,6 @@ package communication.udp; import java.io.IOException; import java.net.InetAddress; -import java.net.SocketException; import java.net.UnknownHostException; import java.util.ArrayList; @@ -13,18 +12,23 @@ import observers.ObserverUserList; public class CommunicationUDP extends Thread { - // **** - protected static int PORT_SERVEUR = 3000; - // **** - protected static int PORT_CLIENT = 2000; - private UDPClient client; private UDPServer server; private int portServer; private ArrayList portOthers; private ArrayList users = new ArrayList(); - private ObserverUserList observer; + private ObserverUserList obsList; + /** + * Create the class that will manage the userlist and contain a UDPClient and a + * UDPServer. Since the applications will run on localhost, it needs to know + * every UDPServer ports used in order to replicate a broadcast behaviour. + * + * @param portClient The port number for the UDPClient + * @param portServer The port number for the UDPServer + * @param portsOther The port numbers for every other application's UDPServer + * @throws IOException + */ public CommunicationUDP(int portClient, int portServer, int[] portsOther) throws IOException { this.portServer = portServer; this.portOthers = this.getArrayListFromArray(portsOther); @@ -33,14 +37,13 @@ public class CommunicationUDP extends Thread { this.client = new UDPClient(portClient); } - // **** - public CommunicationUDP() throws SocketException, UnknownHostException { - this.portServer = PORT_SERVEUR; - this.server = new UDPServer(portServer, this); - this.server.start(); - this.client = new UDPClient(PORT_CLIENT); - } - + /** + * Create an ArrayList from the int[] list of every servers' ports and + * remove the port of this application UDPServer. + * + * @param ports The UDPServer port numbers. + * @return An ArrayList without the port of this UDPServer. + */ private ArrayList getArrayListFromArray(int ports[]) { ArrayList tmp = new ArrayList(); for (int port : ports) { @@ -51,42 +54,83 @@ public class CommunicationUDP extends Thread { return tmp; } - public void setObserver(ObserverUserList obs) { - this.observer = obs; + /** + * Set the observer to notify when the userList is updated. + * + * @param obs The observer + */ + public void setObserver(ObserverUserList o) { + this.obsList = o; } // -------------- USER LIST UPDATE FUNCTION --------------// - protected synchronized void addUser(String idClient, String pseudoClient, InetAddress ipClient, int port) - throws IOException { - users.add(new Utilisateur(idClient, pseudoClient, ipClient, port)); + /** + * Add a new user to the userlist and notify the observer. + * + * @param idClient + * @param pseudoClient + * @param ipClient + * @param port + * @throws UnknownHostException + */ + protected synchronized void addUser(String idUser, String pseudoUser, InetAddress ipUser, int portTCPServer) + throws UnknownHostException { + users.add(new Utilisateur(idUser, pseudoUser, ipUser, portTCPServer)); this.sendUpdate(); } - protected synchronized void changePseudoUser(String idClient, String pseudoClient, InetAddress ipClient, int port) { - int index = getIndexFromID(idClient); - users.get(index).setPseudo(pseudoClient); - this.sendUpdate(); + /** + * Change the pseudo of an user and notify the observer if it exists in the + * userlist. Do nothing otherwise. + * + * @param idClient + * @param pseudoClient + * @param ipClient + * @param port + */ + protected synchronized void changePseudoUser(String idUser, String pseudoUser, InetAddress ipUser, + int portTCPServer) { + int index = getIndexFromID(idUser); + if (index != -1) { + users.get(index).setPseudo(pseudoUser); + this.sendUpdate(); + } + } - protected synchronized void removeUser(String idClient, String pseudoClient, InetAddress ipClient, int port) { - int index = getIndexFromID(idClient); + /** + * Remove an user from the userlist and notify the observer if it exists in the + * userlist. Do nothing otherwise. + * + * @param idUser + * @param pseudoUser + * @param ipUser + * @param portTCPServer + */ + protected synchronized void removeUser(String idUser, String pseudoUser, InetAddress ipUser, int portTCPServer) { + int index = getIndexFromID(idUser); if (index != -1) { users.remove(index); + this.sendUpdate(); } - this.sendUpdate(); + } - public void removeAll() { - int oSize = users.size(); - for (int i = 0; i < oSize; i++) { - users.remove(0); - } + public void removeAllUsers() { + this.users.clear(); } // -------------- CHECKERS --------------// + /** + * Check if there is an user in the list that has the given id. + * + * @param id The user's id. + * @return true if the user is in the list + * false otherwise. + */ protected boolean containsUserFromID(String id) { for (Utilisateur u : users) { if (u.getId().equals(id)) { @@ -96,6 +140,13 @@ public class CommunicationUDP extends Thread { return false; } + /** + * Check if there is an user in the list that has the given pseudo. + * + * @param pseudo The user's pseudo. + * @return true if the user is in the list + * false otherwise. + */ public boolean containsUserFromPseudo(String pseudo) { for (Utilisateur u : users) { if (u.getPseudo().toLowerCase().equals(pseudo)) { @@ -108,6 +159,13 @@ public class CommunicationUDP extends Thread { // -------------- GETTERS --------------// + /** + * Return the user with the given pseudo if it exists in the list. + * + * @param pseudo The user's pseudo. + * @return The user if it exists in the list. + * null otherwise. + */ public Utilisateur getUserFromPseudo(String pseudo) { for (int i = 0; i < users.size(); i++) { if (users.get(i).getPseudo().equals(pseudo)) { @@ -117,6 +175,13 @@ public class CommunicationUDP extends Thread { return null; } + /** + * Return the index of the user with the given id if it exists in the list. + * + * @param id The user's id. + * @return The index if the user exists in the list. + * null otherwise + */ private int getIndexFromID(String id) { for (int i = 0; i < users.size(); i++) { if (users.get(i).getId().equals(id)) { @@ -128,79 +193,100 @@ public class CommunicationUDP extends Thread { // -------------- SEND MESSAGES --------------// + /** + * Send a message indicating this application's user is connected to every + * UDPServer. + * + * @throws UnknownHostException + * @throws IOException + */ public void sendMessageConnecte() throws UnknownHostException, IOException { - for (int port : this.portOthers) { - try { - this.client.sendMessageUDP_local(new MessageSysteme(Message.TypeMessage.JE_SUIS_CONNECTE), port, - InetAddress.getLocalHost()); - } catch (MauvaisTypeMessageException e) { - /* Si ça marche pas essayer là */} + + try { + Message msgOut = new MessageSysteme(Message.TypeMessage.JE_SUIS_CONNECTE); + for (int port : this.portOthers) { + + this.client.sendMessageUDP_local(msgOut, port); + } + } catch (MauvaisTypeMessageException e) { + e.printStackTrace(); } } - // Send the message "add,id,pseudo" to localhost on all the ports in - // "portOthers" - // This allows the receivers' agent (portOthers) to create or modify an entry - // with the - // data of this agent - // Typically used to notify of a name change + /** + * Send a message containing this application's user's data to every UDPServer. + * This method is used to first add this user in the userlist or update this + * user's pseudo. + * + * @throws UnknownHostException + * @throws IOException + */ public void sendMessageInfoPseudo() throws UnknownHostException, IOException { Utilisateur self = Utilisateur.getSelf(); try { - Message msgOut = new MessageSysteme(Message.TypeMessage.INFO_PSEUDO, self.getPseudo(), self.getId(), self.getPort()); + Message msgOut = new MessageSysteme(Message.TypeMessage.INFO_PSEUDO, self.getPseudo(), self.getId(), + self.getPort()); for (int port : this.portOthers) { - this.client.sendMessageUDP_local(msgOut, port, InetAddress.getLocalHost()); + this.client.sendMessageUDP_local(msgOut, port); } - } catch (Exception e) { + } catch (MauvaisTypeMessageException e) { e.printStackTrace(); } } - // Same, but on only one port - // Typically used to give your current name and id to a newly arrived host + /** + * Send a message containing this application's user's data to one user. This + * method is used to answer back when receiving a message with the type + * "JE_SUIS_CONNECTE" + * + * @param portOther The port on which the other user's UDPServer is listening + * @throws UnknownHostException + * @throws IOException + */ public void sendMessageInfoPseudo(int portOther) throws UnknownHostException, IOException { Utilisateur self = Utilisateur.getSelf(); try { Message msgOut = new MessageSysteme(Message.TypeMessage.INFO_PSEUDO, self.getPseudo(), self.getId(), self.getPort()); - this.client.sendMessageUDP_local(msgOut, portOther, InetAddress.getLocalHost()); + this.client.sendMessageUDP_local(msgOut, portOther); } catch (MauvaisTypeMessageException e) { e.printStackTrace(); } } - // Send the message "del,id,pseudo" to localhost on all the ports in - // "portOthers" - // This allows the receivers' agent (portOthers) to delete the entry - // corresponding to this agent + /** + * Send a message indicating this application's user is disconnected to every + * UDPServer. + * + * @throws UnknownHostException + * @throws IOException + */ public void sendMessageDelete() throws UnknownHostException, IOException { Utilisateur self = Utilisateur.getSelf(); try { - - Message msgOut = new MessageSysteme(Message.TypeMessage.JE_SUIS_DECONNECTE, self.getPseudo(), self.getId(), self.getPort()); + + Message msgOut = new MessageSysteme(Message.TypeMessage.JE_SUIS_DECONNECTE, self.getPseudo(), self.getId(), + self.getPort()); for (int port : this.portOthers) { - this.client.sendMessageUDP_local(msgOut, port, InetAddress.getLocalHost()); + this.client.sendMessageUDP_local(msgOut, port); } - + } catch (MauvaisTypeMessageException e) { - + e.printStackTrace(); } } - - + + /** + * Notify the observer with the updated list + */ private void sendUpdate() { - if(this.observer != null) { - this.observer.updateList(this, users); + if (this.obsList != null) { + this.obsList.updateList(this, users); } } - public void destroyAll() { - this.client.destroyAll(); - this.server.interrupt(); - } - } diff --git a/POO/src/communication/udp/UDPClient.java b/POO/src/communication/udp/UDPClient.java index 54839df..44ab75c 100644 --- a/POO/src/communication/udp/UDPClient.java +++ b/POO/src/communication/udp/UDPClient.java @@ -4,40 +4,52 @@ import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; -import java.net.NetworkInterface; import java.net.SocketException; import java.net.UnknownHostException; import messages.*; -public class UDPClient { +class UDPClient { private DatagramSocket sockUDP; - private InetAddress broadcast; - + + /** + * Create a UDP client on the specified port. It will be used to notify the + * other users of this application's user state (Connected/Disconnected/Pseudo + * changed). + * + * @param port + * @throws SocketException + * @throws UnknownHostException + */ public UDPClient(int port) throws SocketException, UnknownHostException { this.sockUDP = new DatagramSocket(port); - - InetAddress localHost = InetAddress.getLocalHost(); - NetworkInterface networkInterface = NetworkInterface.getByInetAddress(localHost); - this.broadcast = networkInterface.getInterfaceAddresses().get(0).getBroadcast(); - System.out.println(this.broadcast); - System.out.println(InetAddress.getLocalHost()); } - - - //Send a message casted as string to the specified port on localhost - protected void sendMessageUDP_local(Message message, int port, InetAddress clientAddress) throws IOException { - String messageString= message.toString(); - DatagramPacket outpacket = new DatagramPacket(messageString.getBytes(), messageString.length(), clientAddress, port); + + /** + * Send a message to the specified port on localhost. + * + * @param message + * @param port + * @throws IOException + */ + protected void sendMessageUDP_local(Message message, int port) throws IOException { + sendMessageUDP(message, port, InetAddress.getLocalHost()); + } + + /** + * Send a message to the given address on the specified port. + * + * @param message + * @param port + * @param clientAddress + * @throws IOException + */ + private void sendMessageUDP(Message message, int port, InetAddress clientAddress) throws IOException { + String messageString = message.toString(); + DatagramPacket outpacket = new DatagramPacket(messageString.getBytes(), messageString.length(), clientAddress, + port); this.sockUDP.send(outpacket); - } - - protected void destroyAll() { - this.sockUDP.close(); - this.sockUDP = null; - this.broadcast = null; - } - + } \ No newline at end of file diff --git a/POO/src/communication/udp/UDPServer.java b/POO/src/communication/udp/UDPServer.java index 529d303..9f3040d 100644 --- a/POO/src/communication/udp/UDPServer.java +++ b/POO/src/communication/udp/UDPServer.java @@ -8,13 +8,22 @@ import java.net.SocketException; import main.Utilisateur; import messages.*; -public class UDPServer extends Thread { +class UDPServer extends Thread { private DatagramSocket sockUDP; private CommunicationUDP commUDP; private byte[] buffer; private boolean running; + + /** + * Create a UDP Server on the specified port. It will be used to read the + * other users states (Connected/Disconnected/Pseudo). + * + * @param port + * @param commUDP + * @throws SocketException + */ public UDPServer(int port, CommunicationUDP commUDP) throws SocketException { this.running = true; this.commUDP = commUDP; @@ -27,12 +36,14 @@ public class UDPServer extends Thread { while (this.running) { try { - + + //When a datagram is received, converts its data in a Message DatagramPacket inPacket = new DatagramPacket(buffer, buffer.length); this.sockUDP.receive(inPacket); String msgString = new String(inPacket.getData(), 0, inPacket.getLength()); Message msg = Message.stringToMessage(msgString); + //Depending on the type of the message switch (msg.getTypeMessage()) { case JE_SUIS_CONNECTE: @@ -40,34 +51,38 @@ public class UDPServer extends Thread { int portClient = inPacket.getPort(); int portServer = portClient+1; + + //Answer back with this application's user data this.commUDP.sendMessageInfoPseudo(portServer); } break; case INFO_PSEUDO: + MessageSysteme m = (MessageSysteme) msg; - + + //Update the userlist with the data received (Add the user or update it) if (this.commUDP.containsUserFromID(m.getId())) { this.commUDP.changePseudoUser(m.getId(), m.getPseudo(), inPacket.getAddress(), m.getPort()); } else { - this.commUDP.addUser(m.getId(), m.getPseudo(), inPacket.getAddress(), m.getPort()); - System.out.println(m.getId() + ", " + m.getPseudo()); } break; case JE_SUIS_DECONNECTE: - this.commUDP.removeUser(((MessageSysteme) msg).getId(), ((MessageSysteme) msg).getPseudo(), - inPacket.getAddress(), inPacket.getPort()); + + MessageSysteme m2 = (MessageSysteme) msg; + //Remove the user from the userlist + this.commUDP.removeUser(m2.getId(), m2.getPseudo(), inPacket.getAddress(), m2.getPort()); break; - default: // Others types of messages are ignored because they are supposed to be - // transmitted by TCP and not UDP + //Do nothing + default: } } catch (IOException e) { - System.out.println("receive exception"); + e.printStackTrace(); } } diff --git a/POO/src/observers/ObserverInputMessage.java b/POO/src/observers/ObserverInputMessage.java index 415c869..57b8c2a 100644 --- a/POO/src/observers/ObserverInputMessage.java +++ b/POO/src/observers/ObserverInputMessage.java @@ -1,5 +1,12 @@ package observers; public interface ObserverInputMessage { - public void update(Object o, Object arg); + + /** + * Method called when data is received from a TCP socket + * + * @param o : The observer to notify + * @param arg : An object + */ + public void updateInput(Object o, Object arg); } diff --git a/POO/src/observers/ObserverSocketState.java b/POO/src/observers/ObserverSocketState.java index 58325df..5aefc77 100644 --- a/POO/src/observers/ObserverSocketState.java +++ b/POO/src/observers/ObserverSocketState.java @@ -2,6 +2,12 @@ package observers; public interface ObserverSocketState { + /** + * Method called when a TCP socket is closed/a communication is broken + * + * @param o : The observer to notify + * @param arg : An object + */ public void updateSocketState(Object o, Object arg); } diff --git a/POO/src/observers/ObserverUserList.java b/POO/src/observers/ObserverUserList.java index 1dc483a..6520e78 100644 --- a/POO/src/observers/ObserverUserList.java +++ b/POO/src/observers/ObserverUserList.java @@ -6,6 +6,12 @@ import main.Utilisateur; public interface ObserverUserList { + /** + * Method called when the userlist is updated + * + * @param o : The observer to notify + * @param userList : The userlist + */ public void updateList(Object o, ArrayList userList); } diff --git a/POO/src/session/ControleurSession.java b/POO/src/session/ControleurSession.java index 3516fa1..2867cb0 100644 --- a/POO/src/session/ControleurSession.java +++ b/POO/src/session/ControleurSession.java @@ -39,6 +39,16 @@ public class ControleurSession implements ActionListener, ObserverInputMessage, private SQLiteManager sqlManager; private ArrayList files; + + /** + * + * @param vue + * @param socketComm + * @param idOther + * @param pseudoOther + * @param sqlManager + * @throws IOException + */ protected ControleurSession(VueSession vue, Socket socketComm, String idOther, String pseudoOther, SQLiteManager sqlManager) throws IOException { this.vue = vue; this.tcpClient = new TCPClient(socketComm); @@ -60,7 +70,7 @@ public class ControleurSession implements ActionListener, ObserverInputMessage, @Override public void actionPerformed(ActionEvent e) { - //Quand le bouton envoyer est presse + //If the button "Envoyer" is pressed if ((JButton) e.getSource() == this.vue.getButtonEnvoyer()) { String messageContent = this.vue.getInputedText(); System.out.println(messageContent); @@ -92,8 +102,7 @@ public class ControleurSession implements ActionListener, ObserverInputMessage, try { this.tcpClient.sendMessage(messageOut); - } catch (MauvaisTypeMessageException | IOException e1) { - // TODO Auto-generated catch block + } catch (IOException e1) { e1.printStackTrace(); } @@ -105,7 +114,9 @@ public class ControleurSession implements ActionListener, ObserverInputMessage, } } + //If the button "Importer" is pressed if((JButton) e.getSource() == this.vue.getButtonImportFile()) { + //Display a file chooser to select one or several files JFileChooser fc = new JFileChooser(); fc.setMultiSelectionEnabled(true); int returVal = fc.showDialog(this.vue, "Importer"); @@ -126,10 +137,7 @@ public class ControleurSession implements ActionListener, ObserverInputMessage, @Override - public void keyTyped(KeyEvent e) { - // TODO Auto-generated method stub - - } + public void keyTyped(KeyEvent e) {} @Override public void keyPressed(KeyEvent e) { @@ -142,10 +150,7 @@ public class ControleurSession implements ActionListener, ObserverInputMessage, } @Override - public void keyReleased(KeyEvent e) { - // TODO Auto-generated method stub - - } + public void keyReleased(KeyEvent e) {} protected ArrayList getHistorique(){ @@ -189,10 +194,11 @@ public class ControleurSession implements ActionListener, ObserverInputMessage, } } - //Methode appelee quand l'inputStream de la socket de communication recoit des donnees + //Method called when a message is received from the TCP socket @Override - public void update(Object o, Object arg) { + public void updateInput(Object o, Object arg) { Message message = (Message) arg; + switch(message.getTypeMessage()) { case TEXTE: System.out.println(message.toString()); @@ -217,6 +223,7 @@ public class ControleurSession implements ActionListener, ObserverInputMessage, this.messagesIn.add(message); } break; + case FICHIER_INIT: try { MessageFichier mFichier = (MessageFichier) arg; @@ -245,16 +252,23 @@ public class ControleurSession implements ActionListener, ObserverInputMessage, } break; + + //Do nothing default: } } + //If the other user closes the session or the communication is broken + //Disable the view (TextArea, Buttons..) and display a message @Override public void updateSocketState(Object o, Object arg) { this.vue.endSession(this.pseudoOther); } + /** + * + */ protected void destroyAll() { String idSelf = Utilisateur.getSelf().getId(); String idOther = this.idOther; @@ -263,11 +277,9 @@ public class ControleurSession implements ActionListener, ObserverInputMessage, this.sqlManager.insertAllMessages(messagesOut, idSelf, idOther); this.sqlManager.insertAllMessages(messagesIn, idOther, idSelf); } catch (SQLException e) { - // TODO Auto-generated catch block e.printStackTrace(); } - this.vue = null; this.tcpClient.destroyAll(); this.tcpClient = null; diff --git a/POO/src/standard/ControleurStandard.java b/POO/src/standard/ControleurStandard.java index 9cab0e9..b0089d9 100644 --- a/POO/src/standard/ControleurStandard.java +++ b/POO/src/standard/ControleurStandard.java @@ -245,7 +245,7 @@ public class ControleurStandard implements ActionListener, ListSelectionListener // ------------OBSERVERS-------------// @Override - public void update(Object o, Object arg) { + public void updateInput(Object o, Object arg) { if (o == this.tcpServ) { @@ -304,6 +304,7 @@ public class ControleurStandard implements ActionListener, ListSelectionListener private void setVueConnexion() throws UnknownHostException, IOException { this.commUDP.sendMessageDelete(); + this.commUDP.removeAllUsers(); this.vue.removeAllUsers(); this.vue.closeAllSession(); this.idsSessionEnCours.clear();