aboutsummaryrefslogtreecommitdiff
path: root/kvision-modules/kvision-server-spring-boot/src/jvmMain/kotlin/pl/treksoft/kvision
diff options
context:
space:
mode:
authorRobert Jaros <rjaros@finn.pl>2020-04-16 17:31:38 +0200
committerRobert Jaros <rjaros@finn.pl>2020-04-16 17:52:54 +0200
commit702c6965a9f7856f59257b55a1927fbc834d26e0 (patch)
tree20e30817d1c077006df12749a7f1198ef72bb40a /kvision-modules/kvision-server-spring-boot/src/jvmMain/kotlin/pl/treksoft/kvision
parent6a4fe0a904af6aa795434f3406f6dd0f5f12b3a5 (diff)
downloadkvision-702c6965a9f7856f59257b55a1927fbc834d26e0.tar.gz
kvision-702c6965a9f7856f59257b55a1927fbc834d26e0.tar.bz2
kvision-702c6965a9f7856f59257b55a1927fbc834d26e0.zip
Deprecate session interfaces in favour of dependency injection
Diffstat (limited to 'kvision-modules/kvision-server-spring-boot/src/jvmMain/kotlin/pl/treksoft/kvision')
-rw-r--r--kvision-modules/kvision-server-spring-boot/src/jvmMain/kotlin/pl/treksoft/kvision/remote/Helpers.kt193
-rw-r--r--kvision-modules/kvision-server-spring-boot/src/jvmMain/kotlin/pl/treksoft/kvision/remote/KVRouterConfiguration.kt21
-rw-r--r--kvision-modules/kvision-server-spring-boot/src/jvmMain/kotlin/pl/treksoft/kvision/remote/KVServiceManager.kt53
-rw-r--r--kvision-modules/kvision-server-spring-boot/src/jvmMain/kotlin/pl/treksoft/kvision/remote/KVWebSocketConfig.kt32
-rw-r--r--kvision-modules/kvision-server-spring-boot/src/jvmMain/kotlin/pl/treksoft/kvision/remote/SessionInterfaces.kt4
5 files changed, 279 insertions, 24 deletions
diff --git a/kvision-modules/kvision-server-spring-boot/src/jvmMain/kotlin/pl/treksoft/kvision/remote/Helpers.kt b/kvision-modules/kvision-server-spring-boot/src/jvmMain/kotlin/pl/treksoft/kvision/remote/Helpers.kt
new file mode 100644
index 00000000..fb128a90
--- /dev/null
+++ b/kvision-modules/kvision-server-spring-boot/src/jvmMain/kotlin/pl/treksoft/kvision/remote/Helpers.kt
@@ -0,0 +1,193 @@
+/*
+ * 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.reactivestreams.Publisher
+import org.springframework.core.ParameterizedTypeReference
+import org.springframework.core.io.buffer.DataBuffer
+import org.springframework.core.io.buffer.DataBufferFactory
+import org.springframework.http.HttpCookie
+import org.springframework.http.codec.HttpMessageReader
+import org.springframework.http.codec.multipart.Part
+import org.springframework.http.server.reactive.ServerHttpRequest
+import org.springframework.util.MultiValueMap
+import org.springframework.web.reactive.function.BodyExtractor
+import org.springframework.web.reactive.function.server.ServerRequest
+import org.springframework.web.reactive.socket.CloseStatus
+import org.springframework.web.reactive.socket.HandshakeInfo
+import org.springframework.web.reactive.socket.WebSocketMessage
+import org.springframework.web.reactive.socket.WebSocketSession
+import org.springframework.web.server.ServerWebExchange
+import org.springframework.web.server.WebSession
+import org.springframework.web.util.UriBuilder
+import reactor.core.publisher.Flux
+import reactor.core.publisher.Mono
+import java.net.InetSocketAddress
+import java.net.URI
+import java.security.Principal
+import java.util.*
+import java.util.function.Function
+
+/**
+ * Empty implementation of the ServerRequest interface
+ */
+internal class KVServerRequest : ServerRequest {
+ override fun <T : Any?> body(extractor: BodyExtractor<T, in ServerHttpRequest>): T {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun <T : Any?> body(extractor: BodyExtractor<T, in ServerHttpRequest>, hints: MutableMap<String, Any>): T {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun remoteAddress(): Optional<InetSocketAddress> {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun attributes(): MutableMap<String, Any> {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun session(): Mono<WebSession> {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun principal(): Mono<out Principal> {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun multipartData(): Mono<MultiValueMap<String, Part>> {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun messageReaders(): MutableList<HttpMessageReader<*>> {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun uri(): URI {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun exchange(): ServerWebExchange {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun <T : Any?> bodyToFlux(elementClass: Class<out T>): Flux<T> {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun <T : Any?> bodyToFlux(typeReference: ParameterizedTypeReference<T>): Flux<T> {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun formData(): Mono<MultiValueMap<String, String>> {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun queryParams(): MultiValueMap<String, String> {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun pathVariables(): MutableMap<String, String> {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun localAddress(): Optional<InetSocketAddress> {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun uriBuilder(): UriBuilder {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun methodName(): String {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun <T : Any?> bodyToMono(elementClass: Class<out T>): Mono<T> {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun <T : Any?> bodyToMono(typeReference: ParameterizedTypeReference<T>): Mono<T> {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun cookies(): MultiValueMap<String, HttpCookie> {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun headers(): ServerRequest.Headers {
+ throw IllegalStateException("Empty implementation")
+ }
+
+}
+
+/**
+ * Empty implementation of the WebSocketSession interface
+ */
+internal class KVWebSocketSession : WebSocketSession {
+ override fun getId(): String {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun binaryMessage(payloadFactory: Function<DataBufferFactory, DataBuffer>): WebSocketMessage {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun pingMessage(payloadFactory: Function<DataBufferFactory, DataBuffer>): WebSocketMessage {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun bufferFactory(): DataBufferFactory {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun getAttributes(): MutableMap<String, Any> {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun receive(): Flux<WebSocketMessage> {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun pongMessage(payloadFactory: Function<DataBufferFactory, DataBuffer>): WebSocketMessage {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun getHandshakeInfo(): HandshakeInfo {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun send(messages: Publisher<WebSocketMessage>): Mono<Void> {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun close(status: CloseStatus): Mono<Void> {
+ throw IllegalStateException("Empty implementation")
+ }
+
+ override fun textMessage(payload: String): WebSocketMessage {
+ throw IllegalStateException("Empty implementation")
+ }
+
+}
diff --git a/kvision-modules/kvision-server-spring-boot/src/jvmMain/kotlin/pl/treksoft/kvision/remote/KVRouterConfiguration.kt b/kvision-modules/kvision-server-spring-boot/src/jvmMain/kotlin/pl/treksoft/kvision/remote/KVRouterConfiguration.kt
index 42c4107d..80ec896b 100644
--- a/kvision-modules/kvision-server-spring-boot/src/jvmMain/kotlin/pl/treksoft/kvision/remote/KVRouterConfiguration.kt
+++ b/kvision-modules/kvision-server-spring-boot/src/jvmMain/kotlin/pl/treksoft/kvision/remote/KVRouterConfiguration.kt
@@ -22,10 +22,11 @@
package pl.treksoft.kvision.remote
import org.springframework.beans.factory.annotation.Value
+import org.springframework.beans.factory.config.BeanDefinition
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.context.annotation.Scope
import org.springframework.core.io.Resource
import org.springframework.http.MediaType.TEXT_HTML
import org.springframework.stereotype.Component
@@ -35,6 +36,9 @@ import org.springframework.web.reactive.function.server.buildAndAwait
import org.springframework.web.reactive.function.server.coRouter
import org.springframework.web.reactive.function.server.router
+/**
+ * Default Spring Boot routes
+ */
@Configuration
open class KVRouterConfiguration {
@Value("classpath:/public/index.html")
@@ -57,8 +61,19 @@ open class KVRouterConfiguration {
}
}
+/**
+ * Default Spring Boot handler
+ */
@Component
-open class KVHandler(var services: List<KVServiceManager<*>>, var applicationContext: ApplicationContext) {
+open class KVHandler(val services: List<KVServiceManager<*>>, val applicationContext: ApplicationContext) {
+
+ private val threadLocalRequest = ThreadLocal<ServerRequest>()
+
+ @Bean
+ @Scope(BeanDefinition.SCOPE_PROTOTYPE)
+ open fun serverRequest(): ServerRequest {
+ return threadLocalRequest.get() ?: KVServerRequest()
+ }
open suspend fun handle(request: ServerRequest): ServerResponse {
val routeUrl = request.path()
@@ -73,7 +88,7 @@ open class KVHandler(var services: List<KVServiceManager<*>>, var applicationCon
}
}.firstOrNull()
return if (handler != null) {
- handler(request, applicationContext as GenericApplicationContext)
+ handler(request, threadLocalRequest, applicationContext)
} else {
ServerResponse.notFound().buildAndAwait()
}
diff --git a/kvision-modules/kvision-server-spring-boot/src/jvmMain/kotlin/pl/treksoft/kvision/remote/KVServiceManager.kt b/kvision-modules/kvision-server-spring-boot/src/jvmMain/kotlin/pl/treksoft/kvision/remote/KVServiceManager.kt
index 1e9bb949..eb602aa1 100644
--- a/kvision-modules/kvision-server-spring-boot/src/jvmMain/kotlin/pl/treksoft/kvision/remote/KVServiceManager.kt
+++ b/kvision-modules/kvision-server-spring-boot/src/jvmMain/kotlin/pl/treksoft/kvision/remote/KVServiceManager.kt
@@ -59,18 +59,18 @@ actual open class KVServiceManager<T : Any> actual constructor(val serviceClass:
val LOG: Logger = LoggerFactory.getLogger(KVServiceManager::class.java.name)
}
- val getRequests: MutableMap<String, suspend (ServerRequest, ApplicationContext) -> ServerResponse> =
+ val getRequests: MutableMap<String, suspend (ServerRequest, ThreadLocal<ServerRequest>, ApplicationContext) -> ServerResponse> =
mutableMapOf()
- val postRequests: MutableMap<String, suspend (ServerRequest, ApplicationContext) -> ServerResponse> =
+ val postRequests: MutableMap<String, suspend (ServerRequest, ThreadLocal<ServerRequest>, ApplicationContext) -> ServerResponse> =
mutableMapOf()
- val putRequests: MutableMap<String, suspend (ServerRequest, ApplicationContext) -> ServerResponse> =
+ val putRequests: MutableMap<String, suspend (ServerRequest, ThreadLocal<ServerRequest>, ApplicationContext) -> ServerResponse> =
mutableMapOf()
- val deleteRequests: MutableMap<String, suspend (ServerRequest, ApplicationContext) -> ServerResponse> =
+ val deleteRequests: MutableMap<String, suspend (ServerRequest, ThreadLocal<ServerRequest>, ApplicationContext) -> ServerResponse> =
mutableMapOf()
- val optionsRequests: MutableMap<String, suspend (ServerRequest, ApplicationContext) -> ServerResponse> =
+ val optionsRequests: MutableMap<String, suspend (ServerRequest, ThreadLocal<ServerRequest>, ApplicationContext) -> ServerResponse> =
mutableMapOf()
val webSocketsRequests: MutableMap<String, suspend (
- WebSocketSession, ApplicationContext, ReceiveChannel<String>, SendChannel<String>
+ WebSocketSession, ThreadLocal<WebSocketSession>, ApplicationContext, ReceiveChannel<String>, SendChannel<String>
) -> Unit> =
mutableMapOf()
@@ -95,6 +95,7 @@ actual open class KVServiceManager<T : Any> actual constructor(val serviceClass:
/**
* @suppress internal function
*/
+ @Suppress("DEPRECATION")
suspend fun initializeService(service: T, req: ServerRequest) {
if (service is WithRequest) {
service.serverRequest = req
@@ -121,8 +122,10 @@ 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, ctx ->
+ addRoute(method, "/kv/$routeDef") { req, tlReq, ctx ->
+ tlReq.set(req)
val service = ctx.getBean(serviceClass.java)
+ tlReq.remove()
initializeService(service, req)
val jsonRpcRequest = if (method == HttpMethod.GET) {
JsonRpcRequest(req.queryParam("id").orElse(null)?.toInt() ?: 0, "", listOf())
@@ -168,8 +171,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, ctx ->
+ addRoute(method, "/kv/$routeDef") { req, tlReq, ctx ->
+ tlReq.set(req)
val service = ctx.getBean(serviceClass.java)
+ tlReq.remove()
initializeService(service, req)
val jsonRpcRequest = req.awaitBody<JsonRpcRequest>()
if (jsonRpcRequest.params.size == 1) {
@@ -223,8 +228,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, ctx ->
+ addRoute(method, "/kv/$routeDef") { req, tlReq, ctx ->
+ tlReq.set(req)
val service = ctx.getBean(serviceClass.java)
+ tlReq.remove()
initializeService(service, req)
val jsonRpcRequest = req.awaitBody<JsonRpcRequest>()
if (jsonRpcRequest.params.size == 2) {
@@ -279,8 +286,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, ctx ->
+ addRoute(method, "/kv/$routeDef") { req, tlReq, ctx ->
+ tlReq.set(req)
val service = ctx.getBean(serviceClass.java)
+ tlReq.remove()
initializeService(service, req)
val jsonRpcRequest = req.awaitBody<JsonRpcRequest>()
@Suppress("MagicNumber")
@@ -337,8 +346,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, ctx ->
+ addRoute(method, "/kv/$routeDef") { req, tlReq, ctx ->
+ tlReq.set(req)
val service = ctx.getBean(serviceClass.java)
+ tlReq.remove()
initializeService(service, req)
val jsonRpcRequest = req.awaitBody<JsonRpcRequest>()
@Suppress("MagicNumber")
@@ -397,8 +408,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, ctx ->
+ addRoute(method, "/kv/$routeDef") { req, tlReq, ctx ->
+ tlReq.set(req)
val service = ctx.getBean(serviceClass.java)
+ tlReq.remove()
initializeService(service, req)
val jsonRpcRequest = req.awaitBody<JsonRpcRequest>()
@Suppress("MagicNumber")
@@ -458,8 +471,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, ctx ->
+ addRoute(method, "/kv/$routeDef") { req, tlReq, ctx ->
+ tlReq.set(req)
val service = ctx.getBean(serviceClass.java)
+ tlReq.remove()
initializeService(service, req)
val jsonRpcRequest = req.awaitBody<JsonRpcRequest>()
@Suppress("MagicNumber")
@@ -516,11 +531,15 @@ actual open class KVServiceManager<T : Any> actual constructor(val serviceClass:
route: String?
) {
val routeDef = "route${this::class.simpleName}${counter++}"
- webSocketsRequests[routeDef] = { webSocketSession, ctx, incoming, outgoing ->
+ webSocketsRequests[routeDef] = { webSocketSession, tlWsSession, ctx, incoming, outgoing ->
+ tlWsSession.set(webSocketSession)
val service = ctx.getBean(serviceClass.java)
+ tlWsSession.remove()
+ @Suppress("DEPRECATION")
if (service is WithWebSocketSession) {
service.webSocketSession = webSocketSession
}
+ @Suppress("DEPRECATION")
if (service is WithPrincipal) {
val principal = webSocketSession.handshakeInfo.principal.awaitSingle()
service.principal = principal
@@ -567,8 +586,10 @@ actual open class KVServiceManager<T : Any> actual constructor(val serviceClass:
noinline function: suspend T.(Int?, Int?, List<RemoteFilter>?, List<RemoteSorter>?, String?) -> RemoteData<RET>
) {
val routeDef = "route${this::class.simpleName}${counter++}"
- addRoute(HttpMethod.POST, "/kv/$routeDef") { req, ctx ->
+ addRoute(HttpMethod.POST, "/kv/$routeDef") { req, tlReq, ctx ->
+ tlReq.set(req)
val service = ctx.getBean(serviceClass.java)
+ tlReq.remove()
initializeService(service, req)
val jsonRpcRequest = req.awaitBody<JsonRpcRequest>()
@Suppress("MagicNumber")
@@ -623,7 +644,7 @@ actual open class KVServiceManager<T : Any> actual constructor(val serviceClass:
fun addRoute(
method: HttpMethod,
path: String,
- handler: suspend (ServerRequest, ApplicationContext) -> ServerResponse
+ handler: suspend (ServerRequest, ThreadLocal<ServerRequest>, ApplicationContext) -> ServerResponse
) {
when (method) {
HttpMethod.GET -> getRequests[path] = handler
diff --git a/kvision-modules/kvision-server-spring-boot/src/jvmMain/kotlin/pl/treksoft/kvision/remote/KVWebSocketConfig.kt b/kvision-modules/kvision-server-spring-boot/src/jvmMain/kotlin/pl/treksoft/kvision/remote/KVWebSocketConfig.kt
index d707f6c4..e0fecb9c 100644
--- a/kvision-modules/kvision-server-spring-boot/src/jvmMain/kotlin/pl/treksoft/kvision/remote/KVWebSocketConfig.kt
+++ b/kvision-modules/kvision-server-spring-boot/src/jvmMain/kotlin/pl/treksoft/kvision/remote/KVWebSocketConfig.kt
@@ -35,9 +35,11 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.reactive.asFlow
import kotlinx.coroutines.reactor.asFlux
import kotlinx.coroutines.reactor.asMono
+import org.springframework.beans.factory.config.BeanDefinition
import org.springframework.context.ApplicationContext
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
+import org.springframework.context.annotation.Scope
import org.springframework.web.reactive.HandlerMapping
import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping
import org.springframework.web.reactive.socket.WebSocketHandler
@@ -46,13 +48,17 @@ import org.springframework.web.reactive.socket.server.support.WebSocketHandlerAd
import reactor.core.publisher.Mono
import kotlin.coroutines.EmptyCoroutineContext
+/**
+ * Spring Boot WebSocket handler
+ */
class KVWebSocketHandler(
private val services: List<KVServiceManager<*>>,
+ private val threadLocalWebSocketSession: ThreadLocal<WebSocketSession>,
private val applicationContext: ApplicationContext
) : WebSocketHandler, CoroutineScope by CoroutineScope(Dispatchers.Default) {
private fun getHandler(session: WebSocketSession): (suspend (
- WebSocketSession, ApplicationContext,
+ WebSocketSession, ThreadLocal<WebSocketSession>, ApplicationContext,
ReceiveChannel<String>, SendChannel<String>
) -> Unit) {
val uri = session.handshakeInfo.uri.toString()
@@ -79,7 +85,13 @@ class KVWebSocketHandler(
requestChannel.close()
}
launch {
- handler.invoke(session, applicationContext, requestChannel, responseChannel)
+ handler.invoke(
+ session,
+ threadLocalWebSocketSession,
+ applicationContext,
+ requestChannel,
+ responseChannel
+ )
if (!responseChannel.isClosedForReceive) responseChannel.close()
session.close()
}
@@ -89,15 +101,25 @@ class KVWebSocketHandler(
}
}
+/**
+ * Spring Boot WebSocket configuration
+ */
@Configuration
open class KVWebSocketConfig(
- private var services: List<KVServiceManager<*>>,
- private var applicationContext: ApplicationContext
+ private val services: List<KVServiceManager<*>>,
+ private val applicationContext: ApplicationContext
) {
+ private val threadLocalWebSocketSession = ThreadLocal<WebSocketSession>()
+
+ @Bean
+ @Scope(BeanDefinition.SCOPE_PROTOTYPE)
+ open fun webSocketSession(): WebSocketSession {
+ return threadLocalWebSocketSession.get() ?: KVWebSocketSession()
+ }
@Bean
open fun handlerMapping(): HandlerMapping {
- val map = mapOf("/kvws/*" to KVWebSocketHandler(services, applicationContext))
+ val map = mapOf("/kvws/*" to KVWebSocketHandler(services, threadLocalWebSocketSession, applicationContext))
val order = -1
return SimpleUrlHandlerMapping(map, order)
}
diff --git a/kvision-modules/kvision-server-spring-boot/src/jvmMain/kotlin/pl/treksoft/kvision/remote/SessionInterfaces.kt b/kvision-modules/kvision-server-spring-boot/src/jvmMain/kotlin/pl/treksoft/kvision/remote/SessionInterfaces.kt
index 63c5a9d1..9080040a 100644
--- a/kvision-modules/kvision-server-spring-boot/src/jvmMain/kotlin/pl/treksoft/kvision/remote/SessionInterfaces.kt
+++ b/kvision-modules/kvision-server-spring-boot/src/jvmMain/kotlin/pl/treksoft/kvision/remote/SessionInterfaces.kt
@@ -26,18 +26,22 @@ import org.springframework.web.reactive.socket.WebSocketSession
import org.springframework.web.server.WebSession
import java.security.Principal
+@Deprecated("Use dependency injection instead.")
interface WithRequest {
var serverRequest: ServerRequest
}
+@Deprecated("Use dependency injection instead.")
interface WithWebSession {
var webSession: WebSession
}
+@Deprecated("Use dependency injection instead.")
interface WithPrincipal {
var principal: Principal
}
+@Deprecated("Use dependency injection instead.")
interface WithWebSocketSession {
var webSocketSession: WebSocketSession
}