aboutsummaryrefslogtreecommitdiff
path: root/kvision-modules/kvision-server-spring-boot/src
diff options
context:
space:
mode:
authorRobert Jaros <rjaros@finn.pl>2019-10-12 18:22:43 +0200
committerRobert Jaros <rjaros@finn.pl>2019-10-12 18:22:43 +0200
commitbcf7504392baccf1568e740c1d453eac7080fb5b (patch)
tree519489751032814bfc0d5426c48de257b788329f /kvision-modules/kvision-server-spring-boot/src
parent28b6e9d646123d774660ebdf0124eeb8a22fe087 (diff)
downloadkvision-bcf7504392baccf1568e740c1d453eac7080fb5b.tar.gz
kvision-bcf7504392baccf1568e740c1d453eac7080fb5b.tar.bz2
kvision-bcf7504392baccf1568e740c1d453eac7080fb5b.zip
Redesign spring-boot server module to use Spring WebFlux instead of Spring MVC.
Diffstat (limited to 'kvision-modules/kvision-server-spring-boot/src')
-rw-r--r--kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/Annotations.kt35
-rw-r--r--kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/KVController.kt66
-rw-r--r--kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/KVRouterConfiguration.kt84
-rw-r--r--kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/KVServiceManager.kt369
-rw-r--r--kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/KVWebSocketConfig.kt263
-rw-r--r--kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/Profile.kt137
-rw-r--r--kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/Security.kt30
-rw-r--r--kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/SessionInterfaces.kt47
-rw-r--r--kvision-modules/kvision-server-spring-boot/src/main/resources/META-INF/spring.factories2
9 files changed, 569 insertions, 464 deletions
diff --git a/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/Annotations.kt b/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/Annotations.kt
new file mode 100644
index 00000000..7655ae70
--- /dev/null
+++ b/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/Annotations.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2017-present Robert Jaros
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package pl.treksoft.kvision.remote
+
+import org.springframework.data.annotation.Id
+import org.springframework.data.annotation.Transient
+import org.springframework.data.relational.core.mapping.Column
+import org.springframework.data.relational.core.mapping.Table
+
+actual typealias Id = Id
+
+actual typealias Transient = Transient
+
+actual typealias Table = Table
+
+actual typealias Column = Column
diff --git a/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/KVController.kt b/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/KVController.kt
deleted file mode 100644
index 23e00284..00000000
--- a/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/KVController.kt
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (c) 2017-present Robert Jaros
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package pl.treksoft.kvision.remote
-
-import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.context.ApplicationContext
-import org.springframework.stereotype.Controller
-import org.springframework.web.bind.annotation.RequestMapping
-import org.springframework.web.bind.annotation.RequestMethod
-import javax.servlet.http.HttpServletRequest
-import javax.servlet.http.HttpServletResponse
-
-/**
- * Controller for handling automatic routes.
- */
-@Controller
-open class KVController {
-
- @Autowired
- lateinit var services: List<KVServiceManager<*>>
-
- @Autowired
- lateinit var applicationContext: ApplicationContext
-
- @RequestMapping(
- "/kv/**",
- method = [RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE, RequestMethod.OPTIONS]
- )
- open fun kVMapping(req: HttpServletRequest, res: HttpServletResponse) {
- val routeUrl = req.requestURI
- val handler = services.mapNotNull {
- when (req.method) {
- "GET" -> it.getRequests[routeUrl]
- "POST" -> it.postRequests[routeUrl]
- "PUT" -> it.putRequests[routeUrl]
- "DELETE" -> it.deleteRequests[routeUrl]
- "OPTIONS" -> it.optionsRequests[routeUrl]
- else -> null
- }
- }.firstOrNull()
- if (handler != null) {
- handler.invoke(req, res, applicationContext)
- } else {
- res.status = HttpServletResponse.SC_NOT_FOUND
- }
- }
-}
diff --git a/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/KVRouterConfiguration.kt b/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/KVRouterConfiguration.kt
new file mode 100644
index 00000000..215848a7
--- /dev/null
+++ b/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/KVRouterConfiguration.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2017-present Robert Jaros
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package pl.treksoft.kvision.remote
+
+import org.springframework.context.ApplicationContext
+import org.springframework.context.annotation.Bean
+import org.springframework.context.annotation.Configuration
+import org.springframework.context.support.GenericApplicationContext
+import org.springframework.stereotype.Component
+import org.springframework.web.reactive.function.server.RouterFunction
+import org.springframework.web.reactive.function.server.ServerRequest
+import org.springframework.web.reactive.function.server.ServerResponse
+import org.springframework.web.reactive.function.server.buildAndAwait
+import org.springframework.web.reactive.function.server.coRouter
+import org.springframework.web.reactive.function.server.router
+import java.net.URI
+
+@Configuration
+open class KVRouterConfiguration {
+ @Bean
+ open fun kvRoutes(kvHandler: KVHandler) = coRouter {
+ GET("/kv/**", kvHandler::handle)
+ POST("/kv/**", kvHandler::handle)
+ PUT("/kv/**", kvHandler::handle)
+ DELETE("/kv/**", kvHandler::handle)
+ OPTIONS("/kv/**", kvHandler::handle)
+ }
+
+ @Bean
+ open fun indexRouter(): RouterFunction<ServerResponse> {
+ val redirectToIndex =
+ ServerResponse
+ .temporaryRedirect(URI("/index.html"))
+ .build()
+
+ return router {
+ GET("/") {
+ redirectToIndex
+ }
+ }
+ }
+}
+
+@Component
+open class KVHandler(var services: List<KVServiceManager<*>>, var applicationContext: ApplicationContext) {
+
+ open suspend fun handle(request: ServerRequest): ServerResponse {
+ val routeUrl = request.path()
+ val handler = services.mapNotNull {
+ when (request.method()?.name) {
+ "GET" -> it.getRequests[routeUrl]
+ "POST" -> it.postRequests[routeUrl]
+ "PUT" -> it.putRequests[routeUrl]
+ "DELETE" -> it.deleteRequests[routeUrl]
+ "OPTIONS" -> it.optionsRequests[routeUrl]
+ else -> null
+ }
+ }.firstOrNull()
+ return if (handler != null) {
+ handler(request, applicationContext as GenericApplicationContext)
+ } else {
+ ServerResponse.notFound().buildAndAwait()
+ }
+ }
+}
diff --git a/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/KVServiceManager.kt b/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/KVServiceManager.kt
index 185356da..14360abc 100644
--- a/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/KVServiceManager.kt
+++ b/kvision-modules/kvision-server-spring-boot/src/main/kotlin/pl/treksoft/kvision/remote/KVServiceManager.kt
@@ -21,24 +21,35 @@
*/
package pl.treksoft.kvision.remote
+import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.ReceiveChannel
import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
+import kotlinx.coroutines.reactive.awaitSingle
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.context.ApplicationContext
-import org.springframework.web.context.support.GenericWebApplicationContext
-import org.springframework.web.socket.WebSocketSession
-import javax.servlet.http.HttpServletRequest
-import javax.servlet.http.HttpServletResponse
+import org.springframework.security.core.Authentication
+import org.springframework.web.reactive.function.server.ServerRequest
+import org.springframework.web.reactive.function.server.ServerResponse
+import org.springframework.web.reactive.function.server.awaitBody
+import org.springframework.web.reactive.function.server.bodyValueAndAwait
+import org.springframework.web.reactive.function.server.json
+import org.springframework.web.reactive.socket.WebSocketSession
+import pl.treksoft.kvision.types.*
+import java.time.LocalDate
+import java.time.LocalDateTime
+import java.time.LocalTime
+import java.time.OffsetDateTime
+import java.time.OffsetTime
import kotlin.reflect.KClass
+
/**
* Multiplatform service manager for Spring Boot.
*/
@@ -50,25 +61,61 @@ actual open class KVServiceManager<T : Any> actual constructor(val serviceClass:
val LOG: Logger = LoggerFactory.getLogger(KVServiceManager::class.java.name)
}
- val getRequests: MutableMap<String, (HttpServletRequest, HttpServletResponse, ApplicationContext) -> Unit> =
+ val getRequests: MutableMap<String, suspend (ServerRequest, ApplicationContext) -> ServerResponse> =
mutableMapOf()
- val postRequests: MutableMap<String, (HttpServletRequest, HttpServletResponse, ApplicationContext) -> Unit> =
+ val postRequests: MutableMap<String, suspend (ServerRequest, ApplicationContext) -> ServerResponse> =
mutableMapOf()
- val putRequests: MutableMap<String, (HttpServletRequest, HttpServletResponse, ApplicationContext) -> Unit> =
+ val putRequests: MutableMap<String, suspend (ServerRequest, ApplicationContext) -> ServerResponse> =
mutableMapOf()
- val deleteRequests: MutableMap<String, (HttpServletRequest, HttpServletResponse, ApplicationContext) -> Unit> =
+ val deleteRequests: MutableMap<String, suspend (ServerRequest, ApplicationContext) -> ServerResponse> =
mutableMapOf()
- val optionsRequests: MutableMap<String, (HttpServletRequest, HttpServletResponse, ApplicationContext) -> Unit> =
+ val optionsRequests: MutableMap<String, suspend (ServerRequest, ApplicationContext) -> ServerResponse> =
mutableMapOf()
val webSocketsRequests: MutableMap<String, suspend (
- WebSocketSession, GenericWebApplicationContext, ReceiveChannel<String>, SendChannel<String>
+ WebSocketSession, ApplicationContext, ReceiveChannel<String>, SendChannel<String>
) -> Unit> =
mutableMapOf()
- val mapper = jacksonObjectMapper()
+ val mapper = jacksonObjectMapper().apply {
+ val module = SimpleModule()
+ module.addSerializer(LocalDateTime::class.java, LocalDateTimeSerializer())
+ module.addSerializer(LocalDate::class.java, LocalDateSerializer())
+ module.addSerializer(LocalTime::class.java, LocalTimeSerializer())
+ module.addSerializer(OffsetDateTime::class.java, OffsetDateTimeSerializer())
+ module.addSerializer(OffsetTime::class.java, OffsetTimeSerializer())
+ module.addDeserializer(LocalDateTime::class.java, LocalDateTimeDeserializer())
+ module.addDeserializer(LocalDate::class.java, LocalDateDeserializer())
+ module.addDeserializer(LocalTime::class.java, LocalTimeDeserializer())
+ module.addDeserializer(OffsetDateTime::class.java, OffsetDateTimeDeserializer())
+ module.addDeserializer(OffsetTime::class.java, OffsetTimeDeserializer())
+ this.registerModule(module)
+ }
var counter: Int = 0
/**
+ * @suppress internal function
+ */
+ suspend fun initializeService(service: T, req: ServerRequest) {
+ if (service is WithRequest) {
+ service.serverRequest = req
+ }
+ if (service is WithWebSession) {
+ val session = req.session().awaitSingle()
+ service.webSession = session
+ }
+ if (service is WithPrincipal) {
+ val principal = req.principal().awaitSingle()
+ service.principal = principal
+ }
+ if (service is WithProfile) {
+ val profile = req.principal().ofType(Authentication::class.java).map {
+ it.principal as Profile
+ }.awaitSingle()
+ service.profile = profile
+ }
+ }
+
+ /**
* Binds a given route with a function of the receiver.
* @param function a function of the receiver
* @param method a HTTP method
@@ -80,35 +127,34 @@ actual open class KVServiceManager<T : Any> actual constructor(val serviceClass:
method: HttpMethod, route: String?
) {
val routeDef = route ?: "route${this::class.simpleName}${counter++}"
- addRoute(method, "/kv/$routeDef") { req, res, ctx ->
+ addRoute(method, "/kv/$routeDef") { req, ctx ->
val service = ctx.getBean(serviceClass.java)
+ initializeService(service, req)
val jsonRpcRequest = if (method == HttpMethod.GET) {
- JsonRpcRequest(req.getParameter("id")?.toInt() ?: 0, "", listOf())
+ JsonRpcRequest(req.queryParam("id").orElse(null)?.toInt() ?: 0, "", listOf())
} else {
- mapper.readValue(req.inputStream, JsonRpcRequest::class.java)
+ req.awaitBody()
}
- GlobalScope.launch(start = CoroutineStart.UNDISPATCHED) {
- try {
- val result = function.invoke(service)
- res.writeJSON(
- mapper.writeValueAsString(
- JsonRpcResponse(
- id = jsonRpcRequest.id,
- result = mapper.writeValueAsString(result)
- )
+ try {
+ val result = function.invoke(service)
+ ServerResponse.ok().json().bodyValueAndAwait(
+ mapper.writeValueAsString(
+ JsonRpcResponse(
+ id = jsonRpcRequest.id,
+ result = mapper.writeValueAsString(result)
)
)
- } catch (e: Exception) {
- LOG.error(e.message, e)
- res.writeJSON(
- mapper.writeValueAsString(
- JsonRpcResponse(
- id = jsonRpcRequest.id,
- error = e.message ?: "Error"
- )
+ )
+ } catch (e: Exception) {
+ LOG.error(e.message, e)
+ ServerResponse.ok().json().bodyValueAndAwait(
+ mapper.writeValueAsString(
+ JsonRpcResponse(
+ id = jsonRpcRequest.id,
+ error = e.message ?: "Error"
)
)
- }
+ )
}
}
}
@@ -127,36 +173,35 @@ actual open class KVServiceManager<T : Any> actual constructor(val serviceClass:
if (method == HttpMethod.GET)
throw UnsupportedOperationException("GET method is only supported for methods without parameters")
val routeDef = route ?: "route${this::class.simpleName}${counter++}"
- addRoute(method, "/kv/$routeDef") { req, res, ctx ->
+ addRoute(method, "/kv/$routeDef") { req, ctx ->
val service = ctx.getBean(serviceClass.java)
- val jsonRpcRequest = mapper.readValue(req.inputStream, JsonRpcRequest::class.java)
+ initializeService(service, req)
+ val jsonRpcRequest = req.awaitBody<JsonRpcRequest>()
if (jsonRpcRequest.params.size == 1) {
val param = getParameter<PAR>(jsonRpcRequest.params[0])
- GlobalScope.launch(start = CoroutineStart.UNDISPATCHED) {
- try {
- val result = function.invoke(service, param)
- res.writeJSON(
- mapper.writeValueAsString(
- JsonRpcResponse(
- id = jsonRpcRequest.id,
- result = mapper.writeValueAsString(result)
- )
+ try {
+ val result = function.invoke(service, param)
+ ServerResponse.ok().json().bodyValueAndAwait(
+ mapper.writeValueAsString(
+ JsonRpcResponse(
+ id = jsonRpcRequest.id,
+ result = mapper.writeValueAsString(result)
)
)
- } catch (e: Exception) {
- LOG.error(e.message, e)
- res.writeJSON(
- mapper.writeValueAsString(
- JsonRpcResponse(
- id = jsonRpcRequest.id,
- error = e.message ?: "Error"
- )
+ )
+ } catch (e: Exception) {
+ LOG.error(e.message, e)
+ ServerResponse.ok().json().bodyValueAndAwait(
+ mapper.writeValueAsString(
+ JsonRpcResponse(
+ id = jsonRpcRequest.id,
+ error = e.message ?: "Error"
)
)
- }
+ )
}
} else {
- res.writeJSON(
+ ServerResponse.ok().json().bodyValueAndAwait(
mapper.writeValueAsString(
JsonRpcResponse(
id = jsonRpcRequest.id,
@@ -182,37 +227,36 @@ actual open class KVServiceManager<T : Any> actual constructor(val serviceClass:
if (method == HttpMethod.GET)
throw UnsupportedOperationException("GET method is only supported for methods without parameters")
val routeDef = route ?: "route${this::class.simpleName}${counter++}"
- addRoute(method, "/kv/$routeDef") { req, res, ctx ->
+ addRoute(method, "/kv/$routeDef") { req, ctx ->
val service = ctx.getBean(serviceClass.java)
- val jsonRpcRequest = mapper.readValue(req.inputStream, JsonRpcRequest::class.java)
+ initializeService(service, req)
+ val jsonRpcRequest = req.awaitBody<JsonRpcRequest>()
if (jsonRpcRequest.params.size == 2) {
val param1 = getParameter<PAR1>(jsonRpcRequest.params[0])
val param2 = getParameter<PAR2>(jsonRpcRequest.params[1])
- GlobalScope.launch(start = CoroutineStart.UNDISPATCHED) {
- try {
- val result = function.invoke(service, param1, param2)
- res.writeJSON(
- mapper.writeValueAsString(
- JsonRpcResponse(
- id = jsonRpcRequest.id,
- result = mapper.writeValueAsString(result)
- )
+ try {
+ val result = function.invoke(service, param1, param2)
+ ServerResponse.ok().json().bodyValueAndAwait(
+ mapper.writeValueAsString(
+ JsonRpcResponse(
+ id = jsonRpcRequest.id,
+ result = mapper.writeValueAsString(result)
)
)
- } catch (e: Exception) {
- LOG.error(e.message, e)
- res.writeJSON(
- mapper.writeValueAsString(
- JsonRpcResponse(
- id = jsonRpcRequest.id,
- error = e.message ?: "Error"
- )
+ )
+ } catch (e: Exception) {
+ LOG.error(e.message, e)
+ ServerResponse.ok().json().bodyValueAndAwait(
+ mapper.writeValueAsString(
+ JsonRpcResponse(
+ id = jsonRpcRequest.id,
+ error = e.message ?: "Error"
)
)
- }
+ )
}
} else {
- res.writeJSON(
+ ServerResponse.ok().json().bodyValueAndAwait(
mapper.writeValueAsString(
JsonRpcResponse(
id = jsonRpcRequest.id,
@@ -238,39 +282,38 @@ actual open class KVServiceManager<T : Any> actual constructor(val serviceClass:
if (method == HttpMethod.GET)
throw UnsupportedOperationException("GET method is only supported for methods without parameters")
val routeDef = route ?: "route${this::class.simpleName}${counter++}"
- addRoute(method, "/kv/$routeDef") { req, res, ctx ->
+ addRoute(method, "/kv/$routeDef") { req, ctx ->
val service = ctx.getBean(serviceClass.java)
- val jsonRpcRequest = mapper.readValue(req.inputStream, JsonRpcRequest::class.java)
+ initializeService(service, req)
+ val jsonRpcRequest = req.awaitBody<JsonRpcRequest>()
@Suppress("MagicNumber")
if (jsonRpcRequest.params.size == 3) {
val param1 = getParameter<PAR1>(jsonRpcRequest.params[0])
val param2 = getParameter<PAR2>(jsonRpcRequest.params[1])
val param3 = getParameter<PAR3>(jsonRpcRequest.params[2])
- GlobalScope.launch(start = CoroutineStart.UNDISPATCHED) {
- try {
- val result = function.invoke(service, param1, param2, param3)
- res.writeJSON(
- mapper.writeValueAsString(
- JsonRpcResponse(
- id = jsonRpcRequest.id,
- result = mapper.writeValueAsString(result)
- )
+ try {
+ val result = function.invoke(service, param1, param2, param3)
+ ServerResponse.ok().json().bodyValueAndAwait(
+ mapper.writeValueAsString(
+ JsonRpcResponse(
+ id = jsonRpcRequest.id,
+ result = mapper.writeValueAsString(result)
)
)
- } catch (e: Exception) {
- LOG.error(e.message, e)
- res.writeJSON(
- mapper.writeValueAsString(
- JsonRpcResponse(
- id = jsonRpcRequest.id,
- error = e.message ?: "Error"
- )
+ )
+ } catch (e: Exception) {
+ LOG.error(e.message, e)
+ ServerResponse.ok().json().bodyValueAndAwait(
+ mapper.writeValueAsString(
+ JsonRpcResponse(
+ id = jsonRpcRequest.id,
+ error = e.message ?: "Error"
)
)
- }
+ )
}
} else {
- res.writeJSON(
+ ServerResponse.ok().json().bodyValueAndAwait(
mapper.writeValueAsString(
JsonRpcResponse(
id = jsonRpcRequest.id,
@@ -296,40 +339,39 @@ actual open class KVServiceManager<T : Any> actual constructor(val serviceClass:
if (method == HttpMethod.GET)
throw UnsupportedOperationException("GET method is only supported for methods without parameters")
val routeDef = route ?: "route${this::class.simpleName}${counter++}"
- addRoute(method, "/kv/$routeDef") { req, res, ctx ->
+ addRoute(method, "/kv/$routeDef") { req, ctx ->
val service = ctx.getBean(serviceClass.java)
- val jsonRpcRequest = mapper.readValue(req.inputStream, JsonRpcRequest::class.java)
+ initializeService(service, req)
+ val jsonRpcRequest = req.awaitBody<JsonRpcRequest>()
@Suppress("MagicNumber")
if (jsonRpcRequest.params.size == 4) {
val param1 = getParameter<PAR1>(jsonRpcRequest.params[0])
val param2 = getParameter<PAR2>(jsonRpcRequest.params[1])
val param3 = getParameter<PAR3>(jsonRpcRequest.params[2])
val param4 = getParameter<PAR4>(jsonRpcRequest.params[3])
- GlobalScope.launch(start = CoroutineStart.UNDISPATCHED) {
- try {
- val result = function.invoke(service, param1, param2, param3, param4)
- res.writeJSON(
- mapper.writeValueAsString(
- JsonRpcResponse(
- id = jsonRpcRequest.id,
- result = mapper.writeValueAsString(result)
- )
+ try {
+ val result = function.invoke(service, param1, param2, param3, param4)
+ ServerResponse.ok().json().bodyValueAndAwait(
+ mapper.writeValueAsString(
+ JsonRpcResponse(
+ id = jsonRpcRequest.id,
+ result = mapper.writeValueAsString(result)
)
)
- } catch (e: Exception) {
- LOG.error(e.message, e)
- res.writeJSON(
- mapper.writeValueAsString(
- JsonRpcResponse(
- id = jsonRpcRequest.id,
- error = e.message ?: "Error"
- )
+ )
+ } catch (e: Exception) {
+ LOG.error(e.message, e)
+ ServerResponse.ok().json().bodyValueAndAwait(
+ mapper.writeValueAsString(
+ JsonRpcResponse(
+ id = jsonRpcRequest.id,
+ error = e.message ?: "Error"
)
)
- }
+ )
}
} else {
- res.writeJSON(
+ ServerResponse.ok().json().bodyValueAndAwait(
mapper.writeValueAsString(
JsonRpcResponse(
id = jsonRpcRequest.id,
@@ -356,9 +398,10 @@ actual open class KVServiceManager<T : Any> actual constructor(val serviceClass:
if (method == HttpMethod.GET)
throw UnsupportedOperationException("GET method is only supported for methods without parameters")
val routeDef = route ?: "route${this::class.simpleName}${counter++}"
- addRoute(method, "/kv/$routeDef") { req, res, ctx ->
+ addRoute(method, "/kv/$routeDef") { req, ctx ->
val service = ctx.getBean(serviceClass.java)
- val jsonRpcRequest = mapper.readValue(req.inputStream, JsonRpcRequest::class.java)
+ initializeService(service, req)
+ val jsonRpcRequest = req.awaitBody<JsonRpcRequest>()
@Suppress("MagicNumber")
if (jsonRpcRequest.params.size == 5) {
val param1 = getParameter<PAR1>(jsonRpcRequest.params[0])
@@ -366,31 +409,29 @@ actual open class KVServiceManager<T : Any> actual constructor(val serviceClass:
val param3 = getParameter<PAR3>(jsonRpcRequest.params[2])
val param4 = getParameter<PAR4>(jsonRpcRequest.params[3])
val param5 = getParameter<PAR5>(jsonRpcRequest.params[4])
- GlobalScope.launch(start = CoroutineStart.UNDISPATCHED) {
- try {
- val result = function.invoke(service, param1, param2, param3, param4, param5)
- res.writeJSON(
- mapper.writeValueAsString(
- JsonRpcResponse(
- id = jsonRpcRequest.id,
- result = mapper.writeValueAsString(result)
- )
+ try {
+ val result = function.invoke(service, param1, param2, param3, param4, param5)
+ ServerResponse.ok().json().bodyValueAndAwait(
+ mapper.writeValueAsString(
+ JsonRpcResponse(
+ id = jsonRpcRequest.id,
+ result = mapper.writeValueAsString(result)
)
)
- } catch (e: Exception) {
- LOG.error(e.message, e)
- res.writeJSON(
- mapper.writeValueAsString(
- JsonRpcResponse(
- id = jsonRpcRequest.id,
- error = e.message ?: "Error"
- )
+ )
+ } catch (e: Exception) {
+ LOG.error(e.message, e)
+ ServerResponse.ok().json().bodyValueAndAwait(
+ mapper.writeValueAsString(
+ JsonRpcResponse(
+ id = jsonRpcRequest.id,
+ error = e.message ?: "Error"
)
)
- }
+ )
}
} else {
- res.writeJSON(
+ ServerResponse.ok().json().bodyValueAndAwait(
mapper.writeValueAsString(
JsonRpcResponse(
id = jsonRpcRequest.id,
@@ -413,9 +454,19 @@ actual open class KVServiceManager<T : Any> actual constructor(val serviceClass:
) {
val routeDef = "route${this::class.simpleName}${counter++}"
webSocketsRequests[routeDef] = { webSocketSession, ctx, incoming, outgoing ->
- val service = synchronized(this) {
- WebSocketSessionHolder.webSocketSession = webSocketSession
- ctx.getBean(serviceClass.java)
+ val service = ctx.getBean(serviceClass.java)
+ if (service is WithWebSocketSession) {
+ service.webSocketSession = webSocketSession
+ }
+ if (service is WithPrincipal) {
+ val principal = webSocketSession.handshakeInfo.principal.awaitSingle()
+ service.principal = principal
+ }
+ if (service is WithProfile) {
+ val profile = webSocketSession.handshakeInfo.principal.ofType(Authentication::class.java).map {
+ it.principal as Profile
+ }.awaitSingle()
+ service.profile = profile
}
val requestChannel = Channel<PAR1>()
val responseChannel = Channel<PAR2>()
@@ -440,6 +491,7 @@ actual open class KVServiceManager<T : Any> actual constructor(val serviceClass:
)
outgoing.send(text)
}
+ if (!incoming.isClosedForReceive) incoming.cancel()
}
launch(start = CoroutineStart.UNDISPATCHED) {
function.invoke(service, requestChannel, responseChannel)
@@ -458,15 +510,16 @@ actual open class KVServiceManager<T : Any> actual constructor(val serviceClass:
function: T.(String?, String?) -> List<RemoteOption>
) {
val routeDef = "route${this::class.simpleName}${counter++}"
- addRoute(HttpMethod.POST, "/kv/$routeDef") { req, res, ctx ->
+ addRoute(HttpMethod.POST, "/kv/$routeDef") { req, ctx ->
val service = ctx.getBean(serviceClass.java)
- val jsonRpcRequest = mapper.readValue(req.inputStream, JsonRpcRequest::class.java)
+ initializeService(service, req)
+ val jsonRpcRequest = req.awaitBody<JsonRpcRequest>()
if (jsonRpcRequest.params.size == 2) {
val param1 = getParameter<String?>(jsonRpcRequest.params[0])
val param2 = getParameter<String?>(jsonRpcRequest.params[1])
try {
val result = function.invoke(service, param1, param2)
- res.writeJSON(
+ ServerResponse.ok().json().bodyValueAndAwait(
mapper.writeValueAsString(
JsonRpcResponse(
id = jsonRpcRequest.id,
@@ -476,7 +529,7 @@ actual open class KVServic