herlanS_ 4 gadi atpakaļ
vecāks
revīzija
dc8d07da3d

+ 6 - 0
pom.xml

@@ -60,6 +60,12 @@
 			<groupId>org.jetbrains.kotlin</groupId>
 			<artifactId>kotlin-stdlib-jdk8</artifactId>
 		</dependency>
+
+		<dependency>
+			<groupId>org.jetbrains.kotlinx</groupId>
+			<artifactId>kotlinx-coroutines-core</artifactId>
+			<version>1.3.2</version>
+		</dependency>
 		<dependency>
 			<groupId>org.ini4j</groupId>
 			<artifactId>ini4j</artifactId>

+ 72 - 62
src/main/kotlin/co/id/datacomsolusindo/ipphonebridge/BridgeFIlter.kt

@@ -27,6 +27,8 @@ import java.io.File
 import java.io.IOException
 import java.io.Serializable
 import java.net.InetAddress
+import java.time.LocalDateTime
+import java.time.format.DateTimeFormatter
 import java.util.*
 import java.util.concurrent.TimeUnit
 import javax.servlet.*
@@ -66,15 +68,16 @@ class SimpleCORSFilter : Filter {
 class BridgeFilter(private val template: SimpMessagingTemplate, val resourceLoader: ResourceLoader) : Filter {
     override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
         val startTime = System.nanoTime()
-
-
         val req = request as HttpServletRequest
         val res = response as HttpServletResponse
         val splitPath = req.requestURI.split("/")
         val client = splitPath[1]
         val toPath = "/" + splitPath.takeLast(splitPath.size - 1).joinToString("/")
-
-        if (client.startsWith("_") || client.startsWith("resources")) {
+        if (
+                client.startsWith("_")
+                || client.startsWith("resources")
+                || client.startsWith("clientStat")
+        ) {
             chain.doFilter(request, response)
             return
         } else {
@@ -83,62 +86,73 @@ class BridgeFilter(private val template: SimpMessagingTemplate, val resourceLoad
                 res.sendRedirect("/resources/$search")
             } else {
                 if (req.getHeader("User-Agent").contains("(dart:io)") || req.requestURL.toString().endsWith("api/license")) {
-                    val headerMap = req.headerNames.toList().associateBy({ it }, { req.getHeader(it) })
-                    val body = req.reader.lines().toList().joinToString(System.lineSeparator())
-
                     val reqId = UUID.randomUUID().toString()
-                    val rb = RequestBuilder(reqId, toPath, HttpMethod.valueOf(req.method), headerMap.toMutableMap())
-                    rb.body = body
-
-                    rb.queryString = req.parameterMap
-                    Singletons.requestInstance[reqId] = RequestQue(reqId, rb, null)
-
-
-
-                    template.convertAndSend("/topic/$client", RequestTrigger(reqId))
-
+                    try {
+                        val headerMap = req.headerNames.toList().associateBy({ it }, { req.getHeader(it) })
+                        val body = req.reader.lines().toList().joinToString(System.lineSeparator())
+
+                        val rb = RequestBuilder(reqId, toPath, HttpMethod.valueOf(req.method), headerMap.toMutableMap())
+                        rb.body = body
+                        rb.queryString = req.parameterMap
+                        Singletons.requestInstance[reqId] = RequestQue(reqId, rb, null)
+                        template.convertAndSend("/topic/$client", RequestTrigger(reqId))
+//                        template.convertAndSend("/topic/str/$client", rb)
+                        var i = 0
+                        while (Singletons.responseQue[reqId] == null) {
+                            TimeUnit.MILLISECONDS.sleep(100)
+                            i++
+                            if (i >= 600) {
+                                res.status = HttpStatus.REQUEST_TIMEOUT.value()
+                                res.writer.write("{\"message\":\"Request timeout. Client not responding\"}")
+                                res.flushBuffer()
+                                Singletons.requestInstance.remove(reqId)
+                                val endTime = System.nanoTime()
+                                val duration = (endTime - startTime).toDouble() / 1000000 //divide by 1000000 to get milliseconds.
+                                LogManager.getLogger("client.$client").info("Request failed, No Response. Time: $duration  UA: " + req.getHeader("User-Agent") + " From: " + InetAddress.getLocalHost().hostAddress + " To: " + req.requestURL.toString() + "?" + req.queryString)
+
+                                ClientHolder.addFailedRequest(client)
+                                return
+                            }
+                        }
 
-                    var i = 0
+                        val resFromSocket = Singletons.responseQue[reqId]
 
-                    while (Singletons.responseQue[reqId] == null) {
-                        TimeUnit.MILLISECONDS.sleep(100)
-                        i++
-                        if (i >= 600) {
-                            res.status = HttpStatus.REQUEST_TIMEOUT.value()
-                            res.writer.write("{\"message\":\"Request timeout. Client not responding\"}")
-                            res.flushBuffer()
-                            Singletons.requestInstance.remove(reqId)
-                            val endTime = System.nanoTime()
-                            val duration = (endTime - startTime).toDouble() / 1000000 //divide by 1000000 to get milliseconds.
-                            LogManager.getLogger("client.$client").info("Request failed, No Response. Time: $duration  UA: " + req.getHeader("User-Agent") + " From: " + InetAddress.getLocalHost().hostAddress + " To: " + req.requestURL.toString() + "?" + req.queryString)
-                            return
+                        res.status = resFromSocket!!.statusCode
+                        resFromSocket.headers?.let {
+                            it.entries
+                                    .filter { f -> resFromSocket.statusCode < 400 || (resFromSocket.statusCode >= 400 && f.key == "Content-Type") }
+                                    .forEach { en ->
+                                        res.setHeader(en.key, en.value.joinToString(","))
+                                    }
                         }
-                    }
 
-                    val resFromSocket = Singletons.responseQue[reqId]
+                        resFromSocket.body?.let {
+                            res.outputStream.write(it)
+                        }
 
-                    res.status = resFromSocket!!.statusCode
-                    resFromSocket.headers?.let {
-                        it.entries
-                                .filter { f -> resFromSocket.statusCode < 400 || (resFromSocket.statusCode >= 400 && f.key == "Content-Type") }
-                                .forEach { en ->
-                                    res.setHeader(en.key, en.value.joinToString(","))
-                                }
-                    }
+                        res.flushBuffer()
 
-                    resFromSocket.body?.let {
-                        res.outputStream.write(it)
-                    }
+                        Singletons.requestInstance.remove(reqId)
+                        val endTime = System.nanoTime()
 
-                    res.flushBuffer()
+                        val duration = (endTime - startTime).toDouble() / 1000000 //divide by 1000000 to get milliseconds.
+                        LogManager.getLogger("client.$client").info("Request Success. Time: $duration  UA: " + req.getHeader("User-Agent") + " From: " + req.remoteAddr + " To: " + req.requestURL.toString() + "?" + req.queryString)
 
-                    Singletons.requestInstance.remove(reqId)
-                    val endTime = System.nanoTime()
+                        ClientHolder.addSuccessRequest(client,duration)
+                        return
 
-                    val duration = (endTime - startTime).toDouble() / 1000000 //divide by 1000000 to get milliseconds.
-                    LogManager.getLogger("client.$client").info("Request Success. Time: $duration  UA: " + req.getHeader("User-Agent") + " From: " + InetAddress.getLocalHost().hostAddress + " To: " + req.requestURL.toString() + "?" + req.queryString)
+                    } catch (e: Exception) {
+                        res.status = HttpStatus.REQUEST_TIMEOUT.value()
+                        res.writer.write("{\"message\":\"Request timeout. Client not responding\"}")
+                        res.flushBuffer()
+                        Singletons.requestInstance.remove(reqId)
+                        val endTime = System.nanoTime()
+                        val duration = (endTime - startTime).toDouble() / 1000000 //divide by 1000000 to get milliseconds.
+                        LogManager.getLogger("client.$client").info("Request failed, Unknown error. Time: $duration  UA: " + req.getHeader("User-Agent") + " From: " + InetAddress.getLocalHost().hostAddress + " To: " + req.requestURL.toString() + "?" + req.queryString)
+                        ClientHolder.addFailedRequest(client)
+                        return
+                    }
 
-                    return
                 } else {
 
                     response.status = HttpStatus.NOT_FOUND.value()
@@ -146,8 +160,6 @@ class BridgeFilter(private val template: SimpMessagingTemplate, val resourceLoad
                 }
             }
         }
-
-
     }
 
     fun isResourceAvailable(path: String, itr: Int = 1): String? {
@@ -164,17 +176,6 @@ class BridgeFilter(private val template: SimpMessagingTemplate, val resourceLoad
     }
 }
 
-//@Component
-//@Order(1)
-//class RequestFilter : Filter {
-//    override fun doFilter(sreq: ServletRequest?, sres: ServletResponse?, fc: FilterChain?) {
-//        val req = sreq as HttpServletRequest
-////        AppLog.write(this.javaClass).info("Request With " + req.getHeader("User-Agent") + " Address " + InetAddress.getLocalHost().hostAddress + " To " + req.requestURL.toString() + "?" + req.queryString)
-//        //println("Request With " + req.getHeader("User-Agent") + " Address " + InetAddress.getLocalHost().hostAddress + " To " + req.requestURL.toString() + "?" + req.queryString)
-//        fc!!.doFilter(sreq, sres)
-//    }
-//}
-
 class RequestQue(val id: String, val requestBuilder: RequestBuilder, var responseObj: Resp?)
 
 class RequestTrigger(val id: String)
@@ -229,6 +230,15 @@ class BridgeRestController {
         }
         return Singletons.requestInstance[id]!!.requestBuilder
     }
+
+    @GetMapping("/clientStat")
+    fun getClientData(): Map<String, Client> {
+        return ClientHolder.get()
+    }
+    @GetMapping("/clientStat/{id}")
+    fun getClientDataOne(@PathVariable("id") id: String): Client? {
+        return ClientHolder.get()[id]
+    }
 }
 
 @Configuration

+ 68 - 11
src/main/kotlin/co/id/datacomsolusindo/ipphonebridge/IpPhoneBridgeApplication.kt

@@ -1,5 +1,8 @@
 package co.id.datacomsolusindo.ipphonebridge
 
+import kotlinx.coroutines.*
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
 import org.apache.logging.log4j.Level
 import org.apache.logging.log4j.LogManager
 import org.apache.logging.log4j.Logger
@@ -20,7 +23,9 @@ import org.springframework.web.socket.messaging.SessionSubscribeEvent
 import java.io.File
 import java.io.Serializable
 import java.time.LocalDateTime
+import java.time.format.DateTimeFormatter
 import java.util.*
+import kotlin.system.measureTimeMillis
 
 
 @SpringBootApplication
@@ -28,6 +33,7 @@ import java.util.*
 class IpPhoneBridgeApplication
 
 fun main(args: Array<String>) {
+    TimeZone.setDefault(TimeZone.getTimeZone("Asia/Jakarta"))
     val properties = Properties()
     val ini = Wini(File("conf/general.ini"))
     properties["server.port"] = ini.get("server", "port")
@@ -78,16 +84,65 @@ fun main(args: Array<String>) {
 
 class Client(
         val number: String,
-        val lastRequest: LocalDateTime
-        //,val logger: Logger
-) : Serializable
+        var connectAt: String,
+        var lastRequest: String
+) : Serializable {
+    var reqSuccess = 0
+    var reqFailed = 0
+    var avgReqSuccessTime = 0.0
+}
 
 object ClientHolder {
+    private val mutexSucReq = Mutex()
     private val clientMap: MutableMap<String, Client> = mutableMapOf()
-    fun load() = clientMap.toMap()
-    fun add(vararg pairs: Pair<String, Client>): Map<String, Client> {
-        clientMap.putAll(pairs)
-        return clientMap.toMap()
+    private suspend fun massiveRun(action: suspend () -> Unit) {
+        val time = measureTimeMillis {
+            coroutineScope { // sc
+                    launch {
+                            action()
+                    }
+            }
+        }
+    }
+
+    fun put(key: String, client: Client) {
+        clientMap[key] = client
+    }
+
+    fun get() = clientMap.toMap()
+
+    fun addFailedRequest(clNum: String) {
+        runBlocking {
+            withContext(Dispatchers.Default) {
+                massiveRun {
+                    mutexSucReq.withLock {
+                        val cl = clientMap[clNum]
+                        if (cl != null) {
+                            cl.lastRequest = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"))
+                            cl.reqFailed += 1
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    fun addSuccessRequest(clNum: String, duration: Double) {
+        runBlocking {
+            withContext(Dispatchers.Default) {
+                massiveRun {
+                    // protect each increment with lock
+                    mutexSucReq.withLock {
+                        val cl = clientMap[clNum]
+                        if (cl != null) {
+                            cl.lastRequest = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"))
+                            cl.avgReqSuccessTime = ((cl.avgReqSuccessTime * cl.reqSuccess) + duration) / (cl.reqSuccess + 1)
+                            cl.reqSuccess += 1
+                        }
+                    }
+                }
+            }
+        }
     }
 }
 
@@ -100,7 +155,11 @@ class WebSocketEventListener {
         if (!simDestination!!.startsWith("/topic/healthCheck")) {
             // do stuff
             val clNum = simDestination.split("/")[2]
-            ClientHolder.add(Pair(clNum, Client(clNum, LocalDateTime.now())))
+            ClientHolder.put(clNum, Client(
+                    clNum,
+                    LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")),
+                    LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"))
+            ))
 
             LogManager.getLogger(this.javaClass).info("clientConnected $simDestination")
 
@@ -116,9 +175,7 @@ class WebSocketEventListener {
                     Filter.Result.ACCEPT,
                     Filter.Result.DENY)
             flow.addAttribute("marker", "FLOW")
-
             console.add(flow)
-
             val standard = builder.newLayout("PatternLayout")
             standard.addAttribute("pattern", "%d %p [%c{1}] %m%n")
             val policies = builder.newLayout("Policies")
@@ -144,7 +201,7 @@ class WebSocketEventListener {
             logger.addAttribute("additivity", false)
             builder.add(logger)
 
-            ClientHolder.load().entries.forEach {
+            ClientHolder.get().entries.forEach {
                 val clientLog = builder.newAppender("client-${it.key}", "RollingFile")
                 clientLog.addAttribute("fileName", "log/${it.key}/client-${it.key}.log")
                 clientLog.addAttribute("filePattern", "log/${it.key}/\$\${date:yyyy - MM}/app-%d{yyyy-MM-dd}-%i.client-${it.key}.zip")

+ 8 - 0
src/main/kotlin/co/id/datacomsolusindo/ipphonebridge/WebSocketConfig.kt

@@ -5,6 +5,10 @@ import org.springframework.messaging.simp.config.MessageBrokerRegistry
 import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker
 import org.springframework.web.socket.config.annotation.StompEndpointRegistry
 import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer
+import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration
+
+
+
 
 
 @Configuration
@@ -15,6 +19,10 @@ class WebSocketConfig : WebSocketMessageBrokerConfigurer {
         config.setApplicationDestinationPrefixes("/app")
     }
 
+    override fun configureWebSocketTransport(registration: WebSocketTransportRegistration) {
+        registration.setMessageSizeLimit(128 * 1024)
+    }
+
     override fun registerStompEndpoints(registry: StompEndpointRegistry) {
         registry.addEndpoint("/_websocket").setAllowedOrigins("*")
     }