|
@@ -1,9 +1,7 @@
|
|
|
package co.id.datacomsolusindo.ipphonebridge
|
|
|
|
|
|
-import com.fasterxml.jackson.core.type.TypeReference
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper
|
|
|
import com.fasterxml.jackson.databind.ObjectWriter
|
|
|
-import org.apache.commons.io.IOUtils
|
|
|
import org.apache.logging.log4j.LogManager
|
|
|
import org.springframework.context.annotation.Configuration
|
|
|
import org.springframework.core.Ordered
|
|
@@ -12,16 +10,8 @@ import org.springframework.core.io.ResourceLoader
|
|
|
import org.springframework.http.HttpHeaders
|
|
|
import org.springframework.http.HttpMethod
|
|
|
import org.springframework.http.HttpStatus
|
|
|
-import org.springframework.messaging.handler.annotation.DestinationVariable
|
|
|
-import org.springframework.messaging.handler.annotation.MessageMapping
|
|
|
-import org.springframework.messaging.handler.annotation.SendTo
|
|
|
import org.springframework.messaging.simp.SimpMessagingTemplate
|
|
|
import org.springframework.stereotype.Component
|
|
|
-import org.springframework.stereotype.Controller
|
|
|
-import org.springframework.web.bind.annotation.GetMapping
|
|
|
-import org.springframework.web.bind.annotation.PathVariable
|
|
|
-import org.springframework.web.bind.annotation.PostMapping
|
|
|
-import org.springframework.web.bind.annotation.RestController
|
|
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc
|
|
|
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry
|
|
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
|
|
@@ -33,6 +23,9 @@ import java.net.InetAddress
|
|
|
import java.time.Duration
|
|
|
import java.time.LocalDateTime
|
|
|
import java.util.*
|
|
|
+import java.util.Collections.synchronizedList
|
|
|
+import java.util.concurrent.ConcurrentHashMap
|
|
|
+import java.util.concurrent.ConcurrentMap
|
|
|
import java.util.concurrent.TimeUnit
|
|
|
import javax.servlet.*
|
|
|
import javax.servlet.http.HttpServletRequest
|
|
@@ -88,6 +81,10 @@ class BridgeFilter(private val template: SimpMessagingTemplate, val resourceLoad
|
|
|
if (search != null && !search.endsWith("assets/bootstrap.js")) {
|
|
|
res.sendRedirect("/resources/$search")
|
|
|
} else {
|
|
|
+
|
|
|
+ LogManager.getLogger("client-$client")
|
|
|
+ .info("Request Start, UA: " + req.getHeader("User-Agent") + " From: " + InetAddress.getLocalHost().hostAddress + " To: " + req.requestURL.toString() + "?" + req.queryString)
|
|
|
+
|
|
|
// if (req.getHeader("User-Agent").contains("(dart:io)") || req.requestURL.toString().endsWith("api/license")) {
|
|
|
val reqId = UUID.randomUUID().toString()
|
|
|
try {
|
|
@@ -112,29 +109,13 @@ class BridgeFilter(private val template: SimpMessagingTemplate, val resourceLoad
|
|
|
rb.body = body
|
|
|
}
|
|
|
|
|
|
- Singletons.requestInstance[reqId] = RequestQue(reqId, rb, null)
|
|
|
+ Singletons.requestInstance.getOrPut(reqId){ RequestQue(reqId, rb, null)}
|
|
|
val ow: ObjectWriter = ObjectMapper().writer().withDefaultPrettyPrinter()
|
|
|
val partStr = ow.writeValueAsString(rb).chunked(1500)
|
|
|
-
|
|
|
-
|
|
|
-// val mid = UUID.randomUUID().toString()
|
|
|
-// partStr.forEachIndexed(
|
|
|
-//
|
|
|
-// )
|
|
|
-// val mList = mutableListOf<Message>()
|
|
|
-// partStr.forEachIndexed { i, s ->
|
|
|
-// mList.add(Message(mid, s, i + 1, partStr.size))
|
|
|
-// }
|
|
|
-// Singletons.sendQueue[mid] = mList.sortedBy { it.part }.toMutableList()
|
|
|
- //send first
|
|
|
- //template.convertAndSend("/topic/request/$client",Singletons.sendQueue[mid]!!.first())
|
|
|
-
|
|
|
-
|
|
|
-// LogManager.getLogger(this.javaClass).info("part size ${partStr.size}")
|
|
|
-// if (partStr.size > 1) {
|
|
|
val partialID = UUID.randomUUID().toString()
|
|
|
val st2 = System.nanoTime()
|
|
|
|
|
|
+// println("req id $reqId")
|
|
|
partStr.forEachIndexed { idx, it ->
|
|
|
template.convertAndSend(
|
|
|
"/topic/partial/$client",
|
|
@@ -144,7 +125,8 @@ class BridgeFilter(private val template: SimpMessagingTemplate, val resourceLoad
|
|
|
|
|
|
|
|
|
var i = 0
|
|
|
- while (Singletons.responseQue[reqId] == null) {
|
|
|
+
|
|
|
+ while (Singletons.responseQue.getOrElse(reqId) { null } == null) {
|
|
|
TimeUnit.MILLISECONDS.sleep(100)
|
|
|
i++
|
|
|
if (i >= 600 * partStr.size) {
|
|
@@ -155,7 +137,7 @@ class BridgeFilter(private val template: SimpMessagingTemplate, val resourceLoad
|
|
|
val endTime = System.nanoTime()
|
|
|
val duration =
|
|
|
(endTime - startTime).toDouble() / 1000000 //divide by 1000000 to get milliseconds.
|
|
|
- LogManager.getLogger("client.$client")
|
|
|
+ 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)
|
|
@@ -163,10 +145,10 @@ class BridgeFilter(private val template: SimpMessagingTemplate, val resourceLoad
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- val resFromSocket = Singletons.responseQue[reqId]
|
|
|
+ val resFromSocket = Singletons.responseQue.getOrElse(reqId) { null }
|
|
|
|
|
|
- res.status = resFromSocket!!.statusCode
|
|
|
- resFromSocket.headers?.let {
|
|
|
+ res.status = resFromSocket?.statusCode ?: 500
|
|
|
+ resFromSocket?.headers?.let {
|
|
|
it.entries
|
|
|
.filter { f -> resFromSocket.statusCode < 400 || (resFromSocket.statusCode >= 400 && f.key == "Content-Type") }
|
|
|
.forEach { en ->
|
|
@@ -174,7 +156,7 @@ class BridgeFilter(private val template: SimpMessagingTemplate, val resourceLoad
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- resFromSocket.body?.let {
|
|
|
+ resFromSocket?.body?.let {
|
|
|
res.outputStream.write(it)
|
|
|
}
|
|
|
|
|
@@ -184,7 +166,7 @@ class BridgeFilter(private val template: SimpMessagingTemplate, val resourceLoad
|
|
|
val endTime = System.nanoTime()
|
|
|
|
|
|
val duration = (endTime - startTime).toDouble() / 1000000 //divide by 1000000 to get milliseconds.
|
|
|
- LogManager.getLogger("client.$client")
|
|
|
+ LogManager.getLogger("client-$client")
|
|
|
.info("Request Success. Time: $duration UA: " + req.getHeader("User-Agent") + " From: " + req.remoteAddr + " To: " + req.requestURL.toString() + "?" + req.queryString)
|
|
|
|
|
|
ClientHolder.addSuccessRequest(client, duration)
|
|
@@ -199,7 +181,7 @@ class BridgeFilter(private val template: SimpMessagingTemplate, val resourceLoad
|
|
|
Singletons.requestInstance.remove(reqId)
|
|
|
val endTime = System.nanoTime()
|
|
|
val duration = (endTime - startTime).toDouble() / 1000000 //divide by 1000000 to get milliseconds.
|
|
|
- LogManager.getLogger("client.$client")
|
|
|
+ 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
|
|
@@ -256,9 +238,9 @@ class Resp(val body: ByteArray?, val statusCode: Int, val headers: Map<String, A
|
|
|
|
|
|
object Singletons {
|
|
|
// val sendQueue: ConcurrentMap<String, MessageHandler> by lazy { ConcurrentHashMap<String, MessageHandler>() }
|
|
|
- val responseQue: MutableMap<String, Resp> by lazy { mutableMapOf<String, Resp>() }
|
|
|
- val requestInstance: MutableMap<String, RequestQue> by lazy { mutableMapOf<String, RequestQue>() }
|
|
|
- val buildSocketChunkData: MutableMap<String, ChunkCollector> by lazy { mutableMapOf<String, ChunkCollector>() }
|
|
|
+ val responseQue: ConcurrentMap<String, Resp> by lazy { ConcurrentHashMap() }
|
|
|
+ val requestInstance: ConcurrentMap<String, RequestQue> by lazy { ConcurrentHashMap() }
|
|
|
+ val buildSocketChunkData: ConcurrentMap<String, ChunkCollector> by lazy { ConcurrentHashMap() }
|
|
|
}
|
|
|
|
|
|
class SocketChunkData(
|
|
@@ -270,117 +252,37 @@ class SocketChunkData(
|
|
|
)
|
|
|
|
|
|
class ChunkCollector(private val id: String) {
|
|
|
- private val listSocketChunk = mutableListOf<SocketChunkData>()
|
|
|
- fun add(dt: SocketChunkData) {
|
|
|
- if (!listSocketChunk.any { it.part == dt.part }) {
|
|
|
- listSocketChunk.add(dt)
|
|
|
- }
|
|
|
-
|
|
|
- if (dt.part == dt.totalPart - 1) {
|
|
|
- val tStart = LocalDateTime.now()
|
|
|
- fixedRateTimer("timer-$id", false, 20, 100) {
|
|
|
- if (listSocketChunk.size == dt.totalPart) {
|
|
|
- val bodyList = listSocketChunk.sortedBy { it.part }.mapNotNull { it.body }
|
|
|
- var bd = ByteArray(0)
|
|
|
- bodyList.forEach { bd += it }
|
|
|
- Singletons.responseQue[id] = Resp(
|
|
|
+ private val mapSocketChunk: ConcurrentMap<Int, SocketChunkData> by lazy { ConcurrentHashMap() }
|
|
|
+
|
|
|
+ init {
|
|
|
+ val tStart = LocalDateTime.now()
|
|
|
+ fixedRateTimer("timer-$id", false, 20, 50) {
|
|
|
+ val listSocketChunk = mapSocketChunk.entries.sortedBy { it.key }.map { it.value }
|
|
|
+ if (listSocketChunk.size > 1 && listSocketChunk[0].totalPart == listSocketChunk.size) {
|
|
|
+ val sortedChunk = listSocketChunk.sortedBy { it.part }
|
|
|
+ val bodyList = sortedChunk.mapNotNull { it.body }
|
|
|
+ var bd = ByteArray(0)
|
|
|
+ bodyList.forEach { bd += it }
|
|
|
+ Singletons.responseQue.getOrPut(id) {
|
|
|
+ Resp(
|
|
|
bd,
|
|
|
- dt.status,
|
|
|
- dt.header?.entries?.associate { Pair(it.key, it.value.toTypedArray()) }
|
|
|
+ sortedChunk.last().status,
|
|
|
+ listSocketChunk.firstOrNull { it.header != null }?.header?.entries?.associate {
|
|
|
+ Pair(it.key, it.value.toTypedArray())
|
|
|
+ }
|
|
|
)
|
|
|
- this.cancel()
|
|
|
- }
|
|
|
- if (Duration.between(tStart, LocalDateTime.now()).toMillis() > 6000) {
|
|
|
- this.cancel()
|
|
|
}
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-@Controller
|
|
|
-class SocketChecker {
|
|
|
-// @MessageMapping("/notification/{id}")
|
|
|
-// @SendTo("/topic/notification/{id}")
|
|
|
-// @Throws(Exception::class)
|
|
|
-// fun notification(@DestinationVariable("id") id: String, message: MutableMap<*, *>): MutableMap<*, *> {
|
|
|
-// return mutableMapOf(Pair("Hi", "I'am Ok"))
|
|
|
-// }
|
|
|
-
|
|
|
- @MessageMapping("/hi")
|
|
|
- @SendTo("/topic/healthCheck")
|
|
|
- @Throws(Exception::class)
|
|
|
- fun greeting(message: MutableMap<*, *>): MutableMap<*, *> {
|
|
|
- return mutableMapOf(Pair("Hi", "I'am Ok"))
|
|
|
- }
|
|
|
|
|
|
- @MessageMapping("/response/{id}")
|
|
|
- @SendTo("/topic/healthCheck")
|
|
|
- @Throws(Exception::class)
|
|
|
- fun responseMsg(message: SocketChunkData, @DestinationVariable("id") id: String): MutableMap<*, *> {
|
|
|
- try {
|
|
|
- LogManager.getLogger(this.javaClass).info("req sent ${message.part} of ${message.totalPart}")
|
|
|
-
|
|
|
- if (message.totalPart == 1) {
|
|
|
- Singletons.responseQue[id] = Resp(
|
|
|
- message.body,
|
|
|
- message.status,
|
|
|
- message.header?.entries?.map { Pair(it.key, it.value.toTypedArray()) }?.toMap()
|
|
|
- )
|
|
|
- } else {
|
|
|
- var sc = Singletons.buildSocketChunkData[id]
|
|
|
-
|
|
|
- if (sc == null) {
|
|
|
- Singletons.buildSocketChunkData[id] = ChunkCollector(id)
|
|
|
- sc = Singletons.buildSocketChunkData[id]
|
|
|
- }
|
|
|
- sc?.add(message)
|
|
|
+ this.cancel()
|
|
|
}
|
|
|
- } catch (e: Exception) {
|
|
|
- e.printStackTrace()
|
|
|
- }
|
|
|
-
|
|
|
- return mutableMapOf(Pair("Hi", "I'am Ok"))
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-@RestController
|
|
|
-class BridgeRestController {
|
|
|
- @PostMapping("/_response/{id}")
|
|
|
- fun responseFromRest(@PathVariable("id") id: String, req: HttpServletRequest): String {
|
|
|
- val objectMapper = ObjectMapper()
|
|
|
- val typeRef: TypeReference<HashMap<String, Array<String>>> =
|
|
|
- object : TypeReference<HashMap<String, Array<String>>>() {}
|
|
|
- val body = IOUtils.toByteArray(req.getPart("body").inputStream)
|
|
|
- Singletons.responseQue[id] = Resp(
|
|
|
- body,
|
|
|
- String(IOUtils.toByteArray(req.getPart("status").inputStream)).toInt(),
|
|
|
- if (req.getPart("header") != null) {
|
|
|
- objectMapper.readValue(String(IOUtils.toByteArray(req.getPart("header").inputStream)), typeRef)
|
|
|
- } else {
|
|
|
- null
|
|
|
+ if (Duration.between(tStart, LocalDateTime.now()).toMillis() > 10 * 60 * 1000) {
|
|
|
+ this.cancel()
|
|
|
}
|
|
|
- )
|
|
|
-
|
|
|
- return "{\"success\":true}"
|
|
|
- }
|
|
|
-
|
|
|
- @GetMapping("/_request/{id}")
|
|
|
- fun getResponseObj(@PathVariable("id") id: String): RequestBuilder {
|
|
|
- if (Singletons.requestInstance[id] == null) {
|
|
|
- throw Exception("not found")
|
|
|
}
|
|
|
- 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]
|
|
|
+ fun add(dt: SocketChunkData) {
|
|
|
+ mapSocketChunk.getOrPut(dt.part) { dt }
|
|
|
}
|
|
|
}
|
|
|
|