123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- 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
- import org.apache.logging.log4j.core.Filter
- import org.apache.logging.log4j.core.config.Configurator
- import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory
- import org.apache.logging.log4j.core.config.builder.api.FilterComponentBuilder
- import org.apache.logging.log4j.core.config.builder.api.LoggerComponentBuilder
- import org.apache.logging.log4j.core.config.builder.api.RootLoggerComponentBuilder
- import org.ini4j.Wini
- import org.springframework.boot.SpringApplication
- import org.springframework.boot.autoconfigure.SpringBootApplication
- import org.springframework.context.event.EventListener
- import org.springframework.messaging.simp.stomp.StompHeaderAccessor
- import org.springframework.messaging.support.GenericMessage
- import org.springframework.scheduling.annotation.EnableScheduling
- import org.springframework.stereotype.Component
- import org.springframework.web.socket.messaging.SessionConnectEvent
- import org.springframework.web.socket.messaging.SessionDisconnectEvent
- 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.exitProcess
- import kotlin.system.measureTimeMillis
- @SpringBootApplication
- @EnableScheduling
- class IpPhoneBridgeApplication
- fun main(args: Array<String>) {
- TimeZone.setDefault(TimeZone.getTimeZone("Asia/Jakarta"))
- val properties = Properties()
- val confFile = File("conf/general.ini")
- if (confFile.exists()) {
- val ini = Wini(File("conf/general.ini"))
- properties["server.port"] = ini.get("server", "port")
- } else {
- properties["server.port"] = System.getenv("PORT")
- }
- if (properties["server.port"] == null) {
- println("undefined port")
- exitProcess(1)
- }
- val sApp = SpringApplication(IpPhoneBridgeApplication::class.java)
- sApp.setDefaultProperties(properties)
- sApp.run(*args)
- LogManager.getLogger("co.id.datacomsolusindo.ipphonebridge.IpPhoneBridgeApplication").info("Bridge Start")
- val hostName = System.getenv("HOSTNAME")
- println("hostname $hostName")
- }
- class Client(
- val number: String,
- 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()
- private suspend fun massiveRun(action: suspend () -> Unit) {
- 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
- }
- }
- }
- }
- }
- }
- }
- @Component
- class WebSocketEventListener {
- fun logBuilder(clNum: String) {
- val builder = ConfigurationBuilderFactory.newConfigurationBuilder()
- val console = builder.newAppender("stdout", "Console")
- val appLog = builder.newAppender("appLog", "RollingFile")
- appLog.addAttribute("fileName", "log/app/app.log")
- appLog.addAttribute("filePattern", "log/app/\$\${date:yyyy - MM}/app-%d{yyyy-MM-dd}-%i.log.zip")
- val flow: FilterComponentBuilder = builder.newFilter(
- "MarkerFilter",
- 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")
- val timeBasedTriggeringPolicy = builder.newLayout("TimeBasedTriggeringPolicy")
- val sizeBasedTriggeringPolicy = builder.newLayout("SizeBasedTriggeringPolicy")
- sizeBasedTriggeringPolicy.addAttribute("size", "10 MB")
- policies.addComponent(timeBasedTriggeringPolicy)
- policies.addComponent(sizeBasedTriggeringPolicy)
- console.add(standard)
- builder.add(console)
- appLog.add(standard)
- appLog.addComponent(policies)
- builder.add(appLog)
- val rootLogger: RootLoggerComponentBuilder = builder.newRootLogger(Level.INFO)
- rootLogger.add(builder.newAppenderRef("stdout"))
- builder.add(rootLogger)
- val logger: LoggerComponentBuilder = builder.newLogger("co.id.datacomsolusindo.ipphonebridge", Level.DEBUG)
- logger.add(builder.newAppenderRef("appLog"))
- logger.addAttribute("additivity", false)
- builder.add(logger)
- rootLogger.add(builder.newAppenderRef("appLog"))
- 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"
- )
- clientLog.add(standard)
- clientLog.addComponent(policies)
- builder.add(clientLog)
- val loggerClient: LoggerComponentBuilder = builder.newLogger("client.${it.key}", Level.DEBUG)
- loggerClient.add(builder.newAppenderRef("client-${it.key}"))
- loggerClient.addAttribute("additivity", false)
- builder.add(loggerClient)
- rootLogger.add(builder.newAppenderRef("client-${it.key}"))
- }
- Configurator.reconfigure(builder.build())
- }
- // @EventListener
- // private void handleSessionConnected(SessionConnectEvent event) {
- // ...
- // }
- //
- // @EventListener
- // private void handleSessionDisconnect(SessionDisconnectEvent event) {
- // ...
- // }
- @EventListener
- fun onConnect(event: SessionConnectEvent) {
- val accessor = StompHeaderAccessor.wrap(event.message)
- val sessionId = accessor.sessionId
- println("connect with session id $sessionId")
- }
- @EventListener
- fun onDisconnect(event: SessionDisconnectEvent) {
- println("disconnect with session id ${event.sessionId}")
- }
- @EventListener
- fun handleSessionSubscribeEvent(event: SessionSubscribeEvent) {
- val message = event.message as GenericMessage<*>
- val simDestination = message.headers["simpDestination"] as String?
- val accessor = StompHeaderAccessor.wrap(event.message)
- val sessionId = accessor.sessionId
- println("subscribe to $simDestination with session id $sessionId")
- if (!(simDestination!!.startsWith("/topic/healthCheck") || simDestination.startsWith("/topic/notification"))) {
- // do stuff
- val clNum = simDestination.split("/")[3]
- 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")
- logBuilder(clNum)
- LogManager.getLogger("client.$clNum").info("Client $clNum Connected")
- }
- }
- }
- object AppLog {
- fun write(cls: Class<*>): Logger {
- return LogManager.getLogger(cls)
- }
- }
|