Spring Boot Web Flux with JOOQ for interfacing with DB and Kotlin coroutines to make blocking JDBC calls run asynchronously. Now with rsockets.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

86 lines
2.9 KiB

  1. package com.example.demo.controller
  2. import com.example.demo.model.Message
  3. import com.example.demo.model.User
  4. import kotlinx.coroutines.*
  5. import org.slf4j.LoggerFactory
  6. import org.springframework.messaging.handler.annotation.MessageMapping
  7. import org.springframework.messaging.handler.annotation.Payload
  8. import org.springframework.messaging.rsocket.RSocketRequester
  9. import org.springframework.messaging.rsocket.annotation.ConnectMapping
  10. import org.springframework.stereotype.Controller
  11. import io.vavr.collection.Map
  12. import org.springframework.messaging.handler.annotation.MessageExceptionHandler
  13. @Controller
  14. class RsocketConnectionControllerAsync : CoroutineScope {
  15. private val log = LoggerFactory.getLogger(RsocketConnectionControllerAsync::class.java)
  16. private val job = Job()
  17. override val coroutineContext = Dispatchers.Unconfined + job
  18. @ObsoleteCoroutinesApi
  19. private val myRsocketActor = rsocketActor()
  20. @ObsoleteCoroutinesApi
  21. suspend fun addRequester(rSocketRequester: RSocketRequester, clientId: String) {
  22. log.info("adding requester {}", clientId)
  23. myRsocketActor.send(AddRequester(rSocketRequester, clientId))
  24. }
  25. @ObsoleteCoroutinesApi
  26. suspend fun removeRequester(clientId: String) {
  27. log.info("removing requester {}", clientId)
  28. myRsocketActor.send(RemoveRequester(clientId))
  29. }
  30. @ObsoleteCoroutinesApi
  31. @ConnectMapping(value = ["client-id2"])
  32. fun onConnect(
  33. rSocketRequester: RSocketRequester,
  34. @Payload user: User
  35. ) {
  36. launch(coroutineContext) {
  37. val clientId = user.id.toString()
  38. addRequester(rSocketRequester, clientId)
  39. rSocketRequester
  40. .rsocket()
  41. .onClose()
  42. .subscribe(null, null, {
  43. log.info("{} just disconnected", clientId)
  44. launch(coroutineContext) { removeRequester(clientId) }
  45. })
  46. }
  47. }
  48. @ObsoleteCoroutinesApi
  49. @MessageMapping("private.news.2")
  50. fun privateNews(message: Message) {
  51. launch {
  52. val res = CompletableDeferred<Map<String, RSocketRequester>>()
  53. myRsocketActor.send(GetRequesters(res))
  54. res
  55. .await()
  56. .filterKeys { key -> key == message.toUser || key == message.fromUser }
  57. .values()
  58. .forEach { requester ->
  59. println("Sending message")
  60. sendMessage(requester, message)
  61. }
  62. }
  63. }
  64. private fun sendMessage(requester: RSocketRequester, message: Message) =
  65. requester
  66. .route("user.queue.reply")
  67. .data(message)
  68. .send()
  69. .subscribe()
  70. @MessageExceptionHandler
  71. suspend fun handleException(ex: IllegalArgumentException): String {
  72. delay(10)
  73. return "${ex.message} handled"
  74. }
  75. }