From c0d3d1d1a5039f14466f2bbf2ab6cc2d08573ce1 Mon Sep 17 00:00:00 2001 From: SeekDaSky Date: Fri, 8 Dec 2017 01:54:01 +0100 Subject: [PATCH] add: Interop pour le channel Event --- src/Channels/Emergency.kt | 12 ++ src/Channels/Event.kt | 266 +++++++++++++++++++++++++++++++++++++ src/Collections/Message.kt | 6 + src/Interop/DelMessage.kt | 7 +- src/Interop/PostMessage.kt | 12 +- src/Interop/UpdMessage.kt | 11 +- src/Main.kt | 9 +- 7 files changed, 317 insertions(+), 6 deletions(-) create mode 100644 src/Channels/Event.kt diff --git a/src/Channels/Emergency.kt b/src/Channels/Emergency.kt index e3e347f..b57704a 100644 --- a/src/Channels/Emergency.kt +++ b/src/Channels/Emergency.kt @@ -250,4 +250,16 @@ class Emergency : AsynchronousListener { } } } + + fun getMessages(chan : String) : MutableList>{ + val channel = this.messages[chan]?.toList(); + val returned = mutableListOf>() + if(channel != null){ + for (m in channel){ + returned.add(Pair(m.second.id,m.second)); + } + } + + return returned; + } } \ No newline at end of file diff --git a/src/Channels/Event.kt b/src/Channels/Event.kt new file mode 100644 index 0000000..f6d22dd --- /dev/null +++ b/src/Channels/Event.kt @@ -0,0 +1,266 @@ +package Channels + +import Collections.Message +import Collections.Stack +import org.json.JSONArray +import org.json.JSONObject +import seekdasky.kWebSocket.Client +import seekdasky.kWebSocket.Event +import seekdasky.kWebSocket.Listeners.AsynchronousListener +import seekdasky.kWebSocket.Message.buildTextMessage +import seekdasky.kWebSocket.Server + +class Event : AsynchronousListener { + + var clients : MutableList; + val messages : MutableMap>>; + val serv : Server; + + constructor(serv : Server){ + this.clients = mutableListOf(); + this.messages = mutableMapOf(); + this.serv = serv; + } + + override fun filter(c: Client): Boolean { + val regex = "\\/event\\/(.*)".toRegex() + val result = regex.findAll(c.URL); + if(result.count() == 1){ + return true; + } + return false; + } + + 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)"); + } + + override fun processConnection(c: Client) { + this.clients.add(c); + val regex = "\\/event\\/(.*)".toRegex(); + val result = regex.findAll(c.URL).elementAt(0).groups[1]?.value ?: ""; + + //create stack if not present + if(this.messages[result] == null){ + this.messages[result] = Stack( 50); + } + + //bind client to channel + if(c.data["emergencyChannel"] == null){ + c.data["emergencyChannel"] = result; + } + c.data["IsLogged"] = false; + } + + fun manuallyDispatchMessage(channel : String, msg : Message, operation : String){ + + val channelStack = this.messages[channel]; + + if(channelStack != null){ + + synchronized(channelStack, { + channelStack.push(Pair(msg.user,msg)); + }) + + this.dispatchMessage(channel,msg,operation); + }else if (channel == ""){ + //broadcast + for(c in this.messages.values){ + synchronized(c,{ + c.push(Pair(msg.user,msg)); + }) + } + + this.dispatchMessage(channel,msg,operation); + + } + } + + 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){ + e.client.data["Username"] = json.getString("name"); + + //if the client correctly identified with the Interop server + if(ConnectChannel.isConnected(e.client)){ + //log + System.out.println("Emergency connection ("+this.clients.count()+" clients connected)"); + + //return OK to the 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 + val array = JSONObject(); + + val chanMessages = this.messages[e.client.data["emergencyChannel"]]; + if(chanMessages != null){ + synchronized(chanMessages,{ + chanMessages.toList().forEach { + array.put(it.second.id,it.second.toJSON()) + } + }) + } + + //build JSON + val jsonMessage = JSONObject(); + jsonMessage.put("error",false); + jsonMessage.put("add",array); + + e.client.send(buildTextMessage(jsonMessage.toString())); + }else{ + val jsonError = JSONObject(); + jsonError.put("error","Invalid credentials"); + + e.client.send(buildTextMessage(jsonError.toString())); + } + + }else{ + //tried to access chat without logging in + val jsonError = JSONObject(); + jsonError.put("error","You must send your credential before sending anything else"); + + e.client.send(buildTextMessage(jsonError.toString())); + + } + + return; + }else if(json.has("message")){ + val chanMessages = this.messages[e.client.data["emergencyChannel"]]; + val message = Message(); + if(chanMessages != null){ + message.user = e.client.data["Username"].toString(); + message.message = json.getString("message"); + message.id = "-1"; + message.type = json.getInt("type"); + + var lat : Float; + var lng : Float; + + //javascript may send float as integer so we double check that + try { + lat = (json.getJSONArray("location")[0] as Double).toFloat(); + }catch(e : Exception){ + lat = (json.getJSONArray("location")[0] as Integer).toFloat(); + } + + try { + lng = (json.getJSONArray("location")[1] as Double).toFloat(); + }catch(e : Exception){ + lng = (json.getJSONArray("location")[1] as Integer).toFloat(); + } + + message.location = mutableListOf(lat,lng); + + 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(); + } + + for(c in this.clients){ + this.dispatchMessage(chanName,message,"add"); + } + }else{ + System.out.println("unknown JSON: "+json.toString()); + } + }catch (e : Exception){ + //System.out.println("Something went wrong (probably JSON parsing error"); + e.printStackTrace(); + } + } + + fun dispatchMessage(channel: String, message: Message, operation : String){ + + val authorizedOperations = listOf("add","upd","del"); + + if(!authorizedOperations.contains(operation)){ + throw Exception("Unauthorized operation"); + } + + 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 + for (op in authorizedOperations){ + if(op == operation){ + jsonMessage.put(op,array); + }else{ + jsonMessage.put(op, JSONArray()); + } + } + + 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 == "")){ + c.send(buildTextMessage(jsonMessage.toString())); + } + } + } + + fun deleteMessage(id : String){ + this.messages.keys.forEach { + val key = it; + val array = this.messages[it]; + array?.toList()?.forEach{ + if(it.second.id == id){ + array.remove(it); + val msg = Message() + msg.id = id; + this.dispatchMessage(key,msg,"del"); + } + } + } + } + + fun updateMessage(msg : Message){ + this.messages.keys.forEach { + val key = it; + val array = this.messages[it]; + var index = 0; + array?.toList()?.forEach{ + if(it.second.id == msg.id){ + msg.user = it.second.user; + msg.location = it.second.location; + msg.timestamp = it.second.timestamp; + array.set(index,Pair(it.first,msg)); + this.dispatchMessage(key,msg,"upd"); + } + index++; + } + } + } + + fun getMessages(chan : String) : MutableList>{ + val channel = this.messages[chan]?.toList(); + val returned = mutableListOf>() + if(channel != null){ + for (m in channel){ + returned.add(Pair(m.second.id,m.second)); + } + } + + return returned; + } +} \ No newline at end of file diff --git a/src/Collections/Message.kt b/src/Collections/Message.kt index 337a2c3..906e8ec 100644 --- a/src/Collections/Message.kt +++ b/src/Collections/Message.kt @@ -11,6 +11,8 @@ class Message { var location = mutableListOf(); var id = ""; + var type = -1; + constructor(){ this.timestamp = System.currentTimeMillis() / 1000; @@ -23,6 +25,10 @@ class Message { json.put("timestamp",timestamp); json.put("location",location); + if(this.type >= 0){ + json.put("type",type); + } + return json; } } \ No newline at end of file diff --git a/src/Interop/DelMessage.kt b/src/Interop/DelMessage.kt index c873b1d..b7ede14 100644 --- a/src/Interop/DelMessage.kt +++ b/src/Interop/DelMessage.kt @@ -1,6 +1,7 @@ package Interop import Channels.Emergency +import Channels.Event import org.json.JSONObject import seekdasky.kWebSocket.InteropEvent import seekdasky.kWebSocket.Listeners.InteropListener @@ -8,9 +9,11 @@ import seekdasky.kWebSocket.Listeners.InteropListener class DelMessage : InteropListener { val emergencyController : Emergency; + val eventController : Event; - constructor(emergency : Emergency){ + constructor(emergency : Emergency, event : Event){ this.emergencyController = emergency; + this.eventController = event; } override fun filter(e: InteropEvent): Boolean { @@ -29,6 +32,8 @@ class DelMessage : InteropListener { if(json.getString("channelType") == "Emergency"){ this.emergencyController.deleteMessage(json.getString("id")); + }else if(json.getString("channelType") == "Event"){ + this.eventController.deleteMessage(json.getString("id")); } diff --git a/src/Interop/PostMessage.kt b/src/Interop/PostMessage.kt index bebed30..c66855c 100644 --- a/src/Interop/PostMessage.kt +++ b/src/Interop/PostMessage.kt @@ -1,6 +1,7 @@ package Interop import Channels.Emergency +import Channels.Event import Collections.Message import org.json.JSONObject import seekdasky.kWebSocket.InteropEvent @@ -9,9 +10,11 @@ import seekdasky.kWebSocket.Listeners.InteropListener class PostMessage : InteropListener { val emergencyController : Emergency; + val eventController : Event; - constructor(emergency : Emergency){ + constructor(emergency : Emergency, event : Event){ this.emergencyController = emergency; + this.eventController = event; } override fun filter(e: InteropEvent): Boolean { @@ -58,6 +61,13 @@ class PostMessage : InteropListener { message, "add" ); + }else if(json.getString("channelType") == "Event"){ + message.type = json.getInt("type"); + this.eventController.manuallyDispatchMessage( + json.getString("channelName"), + message, + "add" + ); } diff --git a/src/Interop/UpdMessage.kt b/src/Interop/UpdMessage.kt index b65fea8..5d80481 100644 --- a/src/Interop/UpdMessage.kt +++ b/src/Interop/UpdMessage.kt @@ -1,6 +1,7 @@ package Interop import Channels.Emergency +import Channels.Event import Collections.Message import org.json.JSONObject import seekdasky.kWebSocket.InteropEvent @@ -9,9 +10,11 @@ import seekdasky.kWebSocket.Listeners.InteropListener class UpdMessage : InteropListener { val emergencyController : Emergency; + val eventController : Event; - constructor(emergency : Emergency){ + constructor(emergency : Emergency, event : Event){ this.emergencyController = emergency; + this.eventController = event; } override fun filter(e: InteropEvent): Boolean { @@ -35,6 +38,12 @@ class UpdMessage : InteropListener { message.id = json.getString("id"); this.emergencyController.updateMessage(message); + }else if(json.getString("channelType") == "Event"){ + val message = Message(); + message.message = json.getString("message"); + message.id = json.getString("id"); + + this.eventController.updateMessage(message); } }catch(e : Exception){ diff --git a/src/Main.kt b/src/Main.kt index bff7226..c38247c 100644 --- a/src/Main.kt +++ b/src/Main.kt @@ -1,4 +1,5 @@ import Channels.Emergency +import Channels.Event import Interop.DelMessage import Interop.PostMessage import Interop.UpdMessage @@ -10,12 +11,14 @@ fun main(args: Array){ server.startInteropServer("localhost",9998); val emergencyChannel = Emergency(server); + val eventChannel = Event(server); server.addListener(Channel(server,"/chat")); server.addListener(emergencyChannel); - server.addInteropListener(PostMessage(emergencyChannel)); - server.addInteropListener(DelMessage(emergencyChannel)); - server.addInteropListener(UpdMessage(emergencyChannel)); + server.addListener(eventChannel); + server.addInteropListener(PostMessage(emergencyChannel,eventChannel)); + server.addInteropListener(DelMessage(emergencyChannel,eventChannel)); + server.addInteropListener(UpdMessage(emergencyChannel,eventChannel)); server.addInteropListener(ConnectChannel); server.startServer();