diff --git a/src/Channel.kt b/src/Channel.kt index 3709819..aadcb3f 100644 --- a/src/Channel.kt +++ b/src/Channel.kt @@ -7,11 +7,18 @@ import seekdasky.kWebSocket.Listeners.AsynchronousListener import seekdasky.kWebSocket.Message.*; import seekdasky.kWebSocket.Server +/** + * Classe s'occupant de la partie chat simple du site, ancienne version que l'on a pas eu le temps de refactorer, pour un meilleur exemple regardez les classe contenues dans le package Channels + */ class Channel : AsynchronousListener { + //nom du channel, utilisé pour définir l'URL d'écoute val channelName : String; + //list des clients var clients : MutableList; + //liste des messages du chat val messages : Stack>; + //instance du serveur val serv : Server; constructor(serv : Server , chanName : String){ @@ -21,43 +28,56 @@ class Channel : AsynchronousListener { this.serv = serv; } + /** + * comme toute les autres méthodes filter + */ override fun filter(c: Client): Boolean { return this.channelName == c.URL; } + /** + * méthode executée quand le client ferme son socket + */ override fun processClosed(e: Event) { this.clients.remove(e.client); ConnectChannel.notifyDisconnect(e.client); System.out.println("A client disconnected ("+this.clients.count()+" clients connected)"); } + /** + * les clients sont ajouté a la liste lors de leur première connection mais ne sont pas loggués + */ override fun processConnection(c: Client) { this.clients.add(c); c.data["IsLogged"] = false; } + /** + * méthode gérant la connexion et l'envoi de message + */ override fun processEvent(e: Event) { try{ val json = JSONObject(e.message.getString()) - if(json.has("close")){ - return - } - if(e.client.data["IsLogged"] != true){ //json format : {username:xxx} if(json.has("name") && json.getString("name") != null){ e.client.data["Username"] = json.getString("name"); + //on demande si le client est bien connecté if(ConnectChannel.isConnected(e.client)){ System.out.println("A client connected ("+this.clients.count()+" clients connected)"); + + //on loggue le client et on lui notifie le succès e.client.data["IsLogged"] = true; val jsonLogin = JSONObject(); jsonLogin.put("error",false); e.client.send(buildTextMessage(jsonLogin.toString())); + //on lui envoie la liste des messages val array = JSONArray(); + //gare a la concurrence synchronized(this.messages,{ this.messages.toList().forEach { array.put(JSONArray(listOf(it.first.data["Username"],it.second))) @@ -87,6 +107,7 @@ class Channel : AsynchronousListener { return; }else if(json.has("message")){ + //gare a la concurrence synchronized(this.messages,{ this.messages.push(Pair(e.client,json.getString("message"))) }); diff --git a/src/ChannelDispatcher.kt b/src/ChannelDispatcher.kt deleted file mode 100644 index 667729b..0000000 --- a/src/ChannelDispatcher.kt +++ /dev/null @@ -1,43 +0,0 @@ -import seekdasky.kWebSocket.Client -import seekdasky.kWebSocket.Event -import seekdasky.kWebSocket.Listeners.AsynchronousListener -import seekdasky.kWebSocket.Server - -class ChannelDispatcher : AsynchronousListener{ - - val serv : Server; - private var channels : MutableList; - - constructor(serv : Server){ - this.serv = serv; - this.channels = mutableListOf(); - } - - override fun filter(c: Client): Boolean { - for(chan in this.channels){ - if(chan.channelName == c.URL){ - return false; - } - } - - return true; - } - - override fun processClosed(e: Event) { - //no client should arrive here - } - - override fun processConnection(c: Client) { - System.out.println("new channel created: "+c.URL); - //if a client is handled here it means we have no channel at this address, let's create it - var newChan = Channel(this.serv,c.URL); - this.serv.addListener(newChan); - //we transfer the client to the new channel - newChan.processConnection(c); - this.channels.add(newChan); - } - - override fun processEvent(e: Event) { - //no client should arrive here - } -} \ No newline at end of file diff --git a/src/Channels/AbstractChannel.kt b/src/Channels/AbstractChannel.kt index ab0bc2e..9cdc327 100644 --- a/src/Channels/AbstractChannel.kt +++ b/src/Channels/AbstractChannel.kt @@ -10,20 +10,43 @@ import seekdasky.kWebSocket.Listeners.AsynchronousListener import seekdasky.kWebSocket.Message.buildTextMessage import seekdasky.kWebSocket.Server +/** + * La classe abstraite AbstractChannel fournis toutes les méthodes nécéssaire a la gestion du channel (authentification, méthode de gestion des messages + * utilisés par les classes Interop, dispatch des messages a tout les clients, la seule partie abstraite est le nom du channel (propriété ChanName) + * qui sert a définir l'URL d'écoute. + */ abstract class AbstractChannel : AsynchronousListener { + //liste de tout les clients du channel var clients : MutableList; + + //map ayant en clé le nom du channel et en valeur la stack de message, la stack est composé de paire nom d'utilisateur/message val messages : MutableMap>>; + + //instance du serveur pour dispatcher les évenements val serv : Server; + + //propriété abstraite servant a définir l'addresse d'écoute abstract val chanName : String; + /** + * Constructeur de la classe + * + * @param serv Server instance du serveur + */ constructor(serv : Server){ this.clients = mutableListOf(); this.messages = mutableMapOf(); this.serv = serv; } - override fun filter(c: Client): Boolean { + /** + * Methode permettant de filtrer les évenements passés a la méthode ProcessEvent() + * + * @param c Client client a filtrer + */ + override fun filter(c: Client): Boolean{ + //on compare l'addresse liée au client a l'adresse d'écoute du channel val regex = "\\/$chanName\\/(.*)".toRegex() val result = regex.findAll(c.URL); if(result.count() == 1){ @@ -32,40 +55,62 @@ abstract class AbstractChannel : AsynchronousListener { return false; } + /** + * Lorsqu'un client quitte le channel, on notifie la classe d'authentification et on retire le client de notre liste + * + * @param e Event évenement lancé par le serveur + */ override fun processClosed(e: Event) { this.clients.remove(e.client); ConnectChannel.notifyDisconnect(e.client); System.out.println("A client disconnected ("+this.clients.count()+" clients connected)"); } + /** + * Méthode executée lors de la première connexion du client + */ override fun processConnection(c: Client) { + //on rajoute le client a notre liste this.clients.add(c); val regex = "\\/$chanName\\/(.*)".toRegex(); val result = regex.findAll(c.URL).elementAt(0).groups[1]?.value ?: ""; - //create stack if not present + //si le sous-channel auquel le client esaie d'accéder n'existe pas, on le créé if(this.messages[result] == null){ this.messages[result] = Stack( 50); } - //bind client to channel - if(c.data["emergencyChannel"] == null){ - c.data["emergencyChannel"] = result; + //on rajoute au client une donnée permettant de savoir a quel sous-channel il est lié + if(c.data["channel"] == null){ + c.data["channel"] = result; } + + //le client est conecté mais pas logué, il devra envoyer un message spécial pour l'être c.data["IsLogged"] = false; } - fun manuallyDispatchMessage(channel : String, msg : Message, operation : String){ + /** + * Méthode appelée par les classes Interop afin de dispatcher les mesages envoyé par l'API REST du site + * + * @param channel String nom du sous-channel auquel il faut dispatch, un nom vide signifie un broadcast a tout les sous channels + * @param msg Messag message a dispatcher + */ + fun manuallyDispatchMessage(channel : String, msg : Message){ + //on récupère la stach du sous-channel val channelStack = this.messages[channel]; + //si il existe, on dispatch if(channelStack != null){ + //gare aux modifications concurrentes synchronized(channelStack, { channelStack.push(Pair(msg.user,msg)); }) - this.dispatchMessage(channel,msg,operation); + //dispatch + this.dispatchMessage(channel,msg,"add"); + //sinon si la chaine est vide on broadcast }else if (channel == ""){ //broadcast for(c in this.messages.values){ @@ -74,42 +119,43 @@ abstract class AbstractChannel : AsynchronousListener { }) } - this.dispatchMessage(channel,msg,operation); + //dispatch + this.dispatchMessage(channel,msg,"add"); } } + /** + * Méthode principale du listener, elle prend en charge tout les messages de tout les clients + */ override fun processEvent(e: Event) { try{ val json = JSONObject(e.message.getString()) - //dead code, should remove it later - if(json.has("close")){ - return - } - //si l'utilisateur n'est pas log, on le check if(e.client.data["IsLogged"] != true){ //json format : {username:xxx} if(json.has("name") && json.getString("name") != null){ + //on set le nom d'utilisateur e.client.data["Username"] = json.getString("name"); - //if the client correctly identified with the Interop server + //On vérifie qu'il est bien connecté a l'aide de la classe ConnectChannel if(ConnectChannel.isConnected(e.client)){ //log System.out.println("Emergency connection ("+this.clients.count()+" clients connected)"); - //return OK to the client + //on retourne une réponse de succès au client e.client.data["IsLogged"] = true; val jsonLogin = JSONObject(); jsonLogin.put("error",false); e.client.send(buildTextMessage(jsonLogin.toString())); - //give the client the list of the most recent messages + //on envoie au client la liste des messages récent val array = JSONObject(); - val chanMessages = this.messages[e.client.data["emergencyChannel"]]; + val chanMessages = this.messages[e.client.data["channel"]]; if(chanMessages != null){ + //gare a la concurrence synchronized(chanMessages,{ chanMessages.toList().forEach { array.put(it.second.id,it.second.toJSON()) @@ -117,11 +163,11 @@ abstract class AbstractChannel : AsynchronousListener { }) } - //build JSON + //on construit le JSON val jsonMessage = JSONObject(); jsonMessage.put("error",false); jsonMessage.put("add",array); - + //on envoie la sauce e.client.send(buildTextMessage(jsonMessage.toString())); }else{ val jsonError = JSONObject(); @@ -131,27 +177,32 @@ abstract class AbstractChannel : AsynchronousListener { } }else{ - //tried to access chat without logging in + //Tentative d'accès au chat sans loggin val jsonError = JSONObject(); jsonError.put("error","You must send your credential before sending anything else"); e.client.send(buildTextMessage(jsonError.toString())); } - return; + //si le client envoie un message sur le canal websocket (pas utilisé sur le site car tout passe par l'API REST et les Interops mais utilisable en serveur standalone }else if(json.has("message")){ - val chanMessages = this.messages[e.client.data["emergencyChannel"]]; + //on récupère le channel + val chanMessages = this.messages[e.client.data["channel"]]; val message = Message(); + //toujours vrai mais il est important de faire la vérification pour l'analyseur de code kotlin if(chanMessages != null){ + //on peuple l'objet message message.user = e.client.data["Username"].toString(); message.message = json.getString("message"); - message.id = "-1"; + //ligne pourrie, on ne peut pas donner d'id vu que c'est le serveur de l'API qui le fait, par défaut on utilise le timestamp (qui plus est un id est toujours une string + message.id = System.currentTimeMillis().toString(); + //on check les coordonnés GPS var lat : Float; var lng : Float; - //javascript may send float as integer so we double check that + //étant doné que javascript fait un peu ce qu'il veut avec les nombres on peut recevoir des int ou des double, il faut tout cast en float try { lat = (json.getJSONArray("location")[0] as Double).toFloat(); }catch(e : Exception){ @@ -166,20 +217,24 @@ abstract class AbstractChannel : AsynchronousListener { message.location = mutableListOf(lat,lng); + //utilisé que par le channel Event si tout va bien if(json.has("type")){ message.type = json.getInt("type"); } + //concurrence encore synchronized(chanMessages,{ chanMessages.push(Pair(e.client.data["Username"].toString(),message)) }); } var chanName = ""; - if(e.client.data["emergencyChannel"] != null){ - chanName = e.client.data["emergencyChannel"].toString(); + //obligatoire pour l'analyseur de code kotlin + if(e.client.data["channel"] != null){ + chanName = e.client.data["channel"].toString(); } + //dispatch.....SPATCH!!!! for(c in this.clients){ this.dispatchMessage(chanName,message,"add"); } @@ -188,25 +243,35 @@ abstract class AbstractChannel : AsynchronousListener { } }catch (e : Exception){ //System.out.println("Something went wrong (probably JSON parsing error"); + //utile pour debug e.printStackTrace(); } } + /** + * SPATCH!! methode envoyant aux bon clients les opérations demandées + * @param channel String sous-channel + * @param message Message message a dispatch + * @param operation String operation a effectuer (add, upd, del) + */ fun dispatchMessage(channel: String, message: Message, operation : String){ + //whitelist val authorizedOperations = listOf("add","upd","del"); + //wow much security, very reliable, wow if(!authorizedOperations.contains(operation)){ throw Exception("Unauthorized operation"); } + //on construit le JSON val array = JSONObject(); array.put(message.id,message.toJSON()) val jsonMessage = JSONObject(); jsonMessage.put("error",false); - //all operations must be present but can be empty + //toutes les opréations doivent être présente mais peuvent être vide for (op in authorizedOperations){ if(op == operation){ jsonMessage.put(op,array); @@ -215,40 +280,59 @@ abstract class AbstractChannel : AsynchronousListener { } } + //SPATCH!! for(c in this.clients){ - //dispatch message to logged client in the right channel or in any channel if channel is an empty string (broadcast) - if(c.data["IsLogged"] == true && ((channel != "" && c.data["emergencyChannel"] == channel) || channel == "")){ + //on envoie a tout les clients loggés du bon channel, ou en broadcast si aucun channel n'est précisé + if(c.data["IsLogged"] == true && ((channel != "" && c.data["channel"] == channel) || channel == "")){ c.send(buildTextMessage(jsonMessage.toString())); } } } + /** + * Methode utilisée par les classes Interop pour que l'API puisse agir sur les channels + * @param id String id du message a supprimer + */ fun deleteMessage(id : String){ + //comme tout les ids sont unique, on doit parcourir tout les channels a la recherche du message this.messages.keys.forEach { + //comme j'utilise la version fonctionnelle des boucle, la variable it (qui contiens l'item) se reécrit dans la deuxieme boucle val key = it; val array = this.messages[it]; array?.toList()?.forEach{ if(it.second.id == id){ + //on enleve de la stack array.remove(it); + //on créer un message vide, seul l'id est utile val msg = Message() msg.id = id; + //on envoie au client this.dispatchMessage(key,msg,"del"); + //petite optimisation pour éviter de continuer a parcourir les channels + return; } } } } + /** + * Methode utilisée par les classes Interop pour que l'API puisse agir sur les channels + * @param msg Message message avec le contenu modifié + */ fun updateMessage(msg : Message){ + //comme pour le delete, on doit tout parcourir this.messages.keys.forEach { val key = it; val array = this.messages[it]; var index = 0; array?.toList()?.forEach{ if(it.second.id == msg.id){ + //on peuple le nouveau message des anciennes valeurs msg.user = it.second.user; msg.location = it.second.location; msg.timestamp = it.second.timestamp; array.set(index,Pair(it.first,msg)); + //on envoie this.dispatchMessage(key,msg,"upd"); } index++; @@ -256,6 +340,10 @@ abstract class AbstractChannel : AsynchronousListener { } } + /** + * Methode implémentée mais pas utilisée suite a une décision durant la nuit + * aucune idée de si ça marche du coup + */ fun getMessages(chan : String) : MutableList>{ val channel = this.messages[chan]?.toList(); val returned = mutableListOf>() diff --git a/src/Channels/Emergency.kt b/src/Channels/Emergency.kt index 146fb39..1ce3377 100644 --- a/src/Channels/Emergency.kt +++ b/src/Channels/Emergency.kt @@ -2,6 +2,9 @@ package Channels import seekdasky.kWebSocket.Server +/** + * Classe gérant le channel emergency + */ class Emergency : AbstractChannel { override val chanName = "emergency"; diff --git a/src/Channels/Event.kt b/src/Channels/Event.kt index 54631e8..eb30564 100644 --- a/src/Channels/Event.kt +++ b/src/Channels/Event.kt @@ -2,6 +2,9 @@ package Channels import seekdasky.kWebSocket.Server +/** + * classe gérant le channel Event + */ class Event : AbstractChannel { override val chanName = "event"; diff --git a/src/Collections/Message.kt b/src/Collections/Message.kt index 906e8ec..0329107 100644 --- a/src/Collections/Message.kt +++ b/src/Collections/Message.kt @@ -3,6 +3,9 @@ package Collections import org.json.JSONObject import java.lang.System; +/** + * Modèle de donnée standard pour tout les messages de channel + */ class Message { var user = ""; @@ -14,10 +17,13 @@ class Message { var type = -1; + //par défaut la date de message est celle actuelle constructor(){ + //on converti le timestamp de milliseconde en seconde (UNIX timestamp) this.timestamp = System.currentTimeMillis() / 1000; } + //permet de facilement serialiser le message fun toJSON() : JSONObject{ val json = JSONObject(); json.put("user",user); @@ -25,6 +31,7 @@ class Message { json.put("timestamp",timestamp); json.put("location",location); + //le type n'est pas présent sur tout les channels if(this.type >= 0){ json.put("type",type); } diff --git a/src/Collections/Stack.kt b/src/Collections/Stack.kt index a7c9719..7dbb852 100644 --- a/src/Collections/Stack.kt +++ b/src/Collections/Stack.kt @@ -1,25 +1,39 @@ package Collections +/** + * Stack FIFO codée pour m'adier a avoir un historique de conversation limité et facile a utiliser + * la classe est générique et réutilisable ailleur au besoin + */ class Stack(size : Int){ - var itCounter = 0; + //taille max de la stack val size = size; + //collection utilisée pour stocker les données var items:MutableList = mutableListOf() + //vérifie si la stack est vide fun isEmpty():Boolean = this.items.isEmpty() + //retourne le nombre d'élément de la stack fun count():Int = this.items.count() override fun toString() = this.items.toString() + /** + * Rajoute un élément en haut de la stack + * @param element T item a ajouter + */ fun push(element: T){ - //BEWARE this stack is programmed to empty itself automatically if you push an item when the stack is full + //ATTENTION cette stack est faite pour évacuer les éléments les plus anciens toute seule en cas de saturation if(this.items.count() == this.size ){ this.pull(); } this.items.add(element) } + /** + * retire un élément en pas de la stack + */ fun pull():T?{ if (this.isEmpty()){ return null @@ -28,14 +42,22 @@ class Stack(size : Int){ } } + /** + * permet de mettre a jour des élements dans la stack + */ fun set(index : Int, item : T){ this.items.set(index,item); } + //enlève un élément de la stack fun remove(item : T){ this.items.remove(item); } + /** + * Plutot que d'implémenter l'interface Iterable moi même (risque de bug en cas d'itération concurrente), lorsque l'on a besoin d'itérer sur la stack + * on peut apppeller cette méthode et itérer sur la collection Kotlin qui est thread safe + */ fun toList() : MutableList{ return this.items.toMutableList(); } diff --git a/src/ConnectChannel.kt b/src/ConnectChannel.kt index 8e507d3..b4cca91 100644 --- a/src/ConnectChannel.kt +++ b/src/ConnectChannel.kt @@ -9,13 +9,22 @@ import seekdasky.kWebSocket.Listeners.InteropListener import seekdasky.kWebSocket.Message.InteropMessage import seekdasky.kWebSocket.Message.buildTextMessage +/** + * Singleton permettant de gérer les conecion par Interop et par WebSocket, Singleton utilisé par les classes lsiteners pour vérifier l'identité des clients + */ object ConnectChannel : InteropListener, AsynchronousListener() { + //utilisé pour que chaque gues ai un id unique (dans la limite de 500) private var lastGuestIndex = 0; + //clints s'étant loggué mais ne s'étant pas encore connecté a un channel (si un client se loggue sans jamais se connecter a un socket il sera éjecté de la satck private val notBoundYet = Stack>(500); + //client connecté a un channel WebSocket private val connected = mutableMapOf(); + /** + * méthode de filtre pour le WebSOcket + */ override fun filter(c: Client): Boolean { return c.URL == "/connect"; } @@ -28,13 +37,20 @@ object ConnectChannel : InteropListener, AsynchronousListener() { return } + /** + * méthode de login par WebSocket + */ override fun processEvent(e: Event) { try { val json = JSONObject(e.message.toString()); + //si l'utilisateur est un guest if(json.getString("type") == "guest"){ + //on lui donne un id unique (dans la limtite de 500) this.lastGuestIndex = ++this.lastGuestIndex % 500; val guestName = "Guest"+this.lastGuestIndex; + + //on retourne au client son id de guest val jsonReturn = JSONObject(); jsonReturn.put("error", false); jsonReturn.put("name", guestName); @@ -44,6 +60,7 @@ object ConnectChannel : InteropListener, AsynchronousListener() { e.client.send(buildTextMessage(jsonReturn.toString())); }else{ + //on n'autorise pas la connection en tant qu'admin ou utilisateur (car aucun lien possible avec la base de donnée pour vérifier les tokens val jsonReturn = JSONObject(); jsonReturn.put("error", "Admin or User login is disabled when using the websocket API, please use the REST API to access you account"); @@ -56,6 +73,9 @@ object ConnectChannel : InteropListener, AsynchronousListener() { } } + /** + * méthode de filtre pour le socket Interop + */ override fun filter(e: InteropEvent): Boolean { try{ val json = JSONObject(e.message.string); @@ -68,13 +88,18 @@ object ConnectChannel : InteropListener, AsynchronousListener() { } } + /** + * méthode de loggin par Interop + */ override fun processEvent(e: InteropEvent) { try { val json = JSONObject(e.message.string); if(json.getString("type") == "guest"){ + //Login de guest this.lastGuestIndex = ++this.lastGuestIndex % 500; val guestName = "Guest"+this.lastGuestIndex; + //on retourne l'id de l'utilisateur val jsonReturn = JSONObject(); jsonReturn.put("error", false); jsonReturn.put("name", guestName); @@ -84,6 +109,7 @@ object ConnectChannel : InteropListener, AsynchronousListener() { e.client.send(InteropMessage(encode(jsonReturn.toString()))); }else{ + //la vérification de token étant faite par l'API REST, on peut logg les utilisateurs et admin val jsonReturn = JSONObject(); jsonReturn.put("error", false); jsonReturn.put("name", json.getString("name")); @@ -98,15 +124,21 @@ object ConnectChannel : InteropListener, AsynchronousListener() { } } + /** + * méthode appelée par les Listeners Interop et WebSocket pour vérifier l'identitée d'un client + */ fun isConnected(client : Client) : Boolean{ + //est ce que l'utilisateur est deja log sur un autre channel for(c in this.connected.keys){ if(c.data["Username"] == client.data["Username"]){ return true; } } + //est ce qu'il ne s'est pas encore log sur un channel for(p in this.notBoundYet.toList()) { if (p.second == client.data["Username"]) { + //on rajoute le client au clients utilisant un channel this.connected.put(client, p.first); this.notBoundYet.remove(p); return true; @@ -116,6 +148,9 @@ object ConnectChannel : InteropListener, AsynchronousListener() { return false; } + /** + * lorsqu'un client se déconnecte, on remet son ID dans la catégorie des clients pas encore connecté afin de ne pas avoir a su re loguer a chaque changement de channel + */ fun notifyDisconnect(client : Client){ val clientType = this.connected[client]; if(clientType != null){ diff --git a/src/Interop/DelMessage.kt b/src/Interop/DelMessage.kt index b7ede14..106e1f3 100644 --- a/src/Interop/DelMessage.kt +++ b/src/Interop/DelMessage.kt @@ -6,9 +6,14 @@ import org.json.JSONObject import seekdasky.kWebSocket.InteropEvent import seekdasky.kWebSocket.Listeners.InteropListener +/** + * Classe Interop permettant de gérer toutes les suppréssions de message + */ class DelMessage : InteropListener { + //channel emergency val emergencyController : Emergency; + //channel event val eventController : Event; constructor(emergency : Emergency, event : Event){ @@ -16,6 +21,9 @@ class DelMessage : InteropListener { this.eventController = event; } + /** + * Comme pour les Listeners WebSocket, cette méthode filtre les évenements entrant + */ override fun filter(e: InteropEvent): Boolean { val json = JSONObject(e.message.string); @@ -24,12 +32,15 @@ class DelMessage : InteropListener { json.getString("operation") == "DelMessage"; } + /** + * méthode gérant la suppression de message + */ override fun processEvent(e: InteropEvent) { try { val json = JSONObject(e.message.string); - + //on dispatch au bon channel if(json.getString("channelType") == "Emergency"){ this.emergencyController.deleteMessage(json.getString("id")); }else if(json.getString("channelType") == "Event"){ diff --git a/src/Interop/PostMessage.kt b/src/Interop/PostMessage.kt index c66855c..de4fa94 100644 --- a/src/Interop/PostMessage.kt +++ b/src/Interop/PostMessage.kt @@ -7,16 +7,26 @@ import org.json.JSONObject import seekdasky.kWebSocket.InteropEvent import seekdasky.kWebSocket.Listeners.InteropListener + +/** + * Classe Interop permettant de gérer tous les ajouts de message + */ class PostMessage : InteropListener { + //channel emergency val emergencyController : Emergency; + //channel event val eventController : Event; + constructor(emergency : Emergency, event : Event){ this.emergencyController = emergency; this.eventController = event; } + /** + * cf: DelMessage + */ override fun filter(e: InteropEvent): Boolean { val json = JSONObject(e.message.string); @@ -25,12 +35,15 @@ class PostMessage : InteropListener { json.getString("operation") == "PostMessage"; } + /** + * méthode traitant l'ajout de message + */ override fun processEvent(e: InteropEvent) { try { val json = JSONObject(e.message.string); - //javascript may send float as integer so we double check that + //javascript fait un peu ce qu'il veut au niveau des nombres et peut envoyer des doubles ou des int, on doit tout cast en float var lat : Float; var lng : Float try { @@ -47,26 +60,24 @@ class PostMessage : InteropListener { val location = mutableListOf(lat, lng); + //on peuple le modèle val message = Message(); message.location = location; message.message = json.getString("message"); message.user = json.getString("username"); message.id = json.getString("id"); - message.timestamp = System.currentTimeMillis() / 1000; if(json.getString("channelType") == "Emergency"){ this.emergencyController.manuallyDispatchMessage( json.getString("channelName"), - message, - "add" + message ); }else if(json.getString("channelType") == "Event"){ message.type = json.getInt("type"); this.eventController.manuallyDispatchMessage( json.getString("channelName"), - message, - "add" + message ); } diff --git a/src/Interop/UpdMessage.kt b/src/Interop/UpdMessage.kt index 5d80481..32f2a3c 100644 --- a/src/Interop/UpdMessage.kt +++ b/src/Interop/UpdMessage.kt @@ -7,6 +7,9 @@ import org.json.JSONObject import seekdasky.kWebSocket.InteropEvent import seekdasky.kWebSocket.Listeners.InteropListener +/** + * Classe Interop permettant de gérer toutes les mise a jour de message + */ class UpdMessage : InteropListener { val emergencyController : Emergency; diff --git a/src/Main.kt b/src/Main.kt index 28b15ee..b90a51c 100644 --- a/src/Main.kt +++ b/src/Main.kt @@ -7,22 +7,32 @@ import seekdasky.kWebSocket.Server fun main(args: Array){ - val server = Server("0.0.0.0",9999,null); + //on démarre le serveur WebSOcket + val server = Server("0.0.0.0",9999,null) + //on démarre le serveur Interop server.startInteropServer("localhost",9998); + //channels WebSocket pilotés par Interop val emergencyChannel = Emergency(server); val eventChannel = Event(server); - + //channel WebSocket server.addListener(Channel(server,"/chat")); + //on bind les listeners server.addListener(emergencyChannel); - server.addListener(eventChannel) + server.addListener(eventChannel); + //service de login par WebSocket server.addListener(ConnectChannel); + //Listeners Interops server.addInteropListener(PostMessage(emergencyChannel,eventChannel)); server.addInteropListener(DelMessage(emergencyChannel,eventChannel)); server.addInteropListener(UpdMessage(emergencyChannel,eventChannel)); + //login Interop server.addInteropListener(ConnectChannel); + + //et c'est partiiiiiiii server.startServer(); + //méthode bloquant le thread principal tant que le serveur est en vie server.block(); } \ No newline at end of file