Add entityStoreFactory and refactor mongo/xodus connections.kt

This commit is contained in:
Atos1337 2021-12-04 02:10:10 +03:00
parent b79ad40a9b
commit e07780e7da
3 changed files with 102 additions and 46 deletions

View File

@ -1,5 +1,6 @@
package ru.mipt.npm.controls.mongo package ru.mipt.npm.controls.mongo
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
@ -16,30 +17,42 @@ import ru.mipt.npm.controls.controllers.DeviceManager
import ru.mipt.npm.controls.controllers.hubMessageFlow import ru.mipt.npm.controls.controllers.hubMessageFlow
import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.Factory import space.kscience.dataforge.context.Factory
import space.kscience.dataforge.context.debug
import space.kscience.dataforge.context.logger
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.string import space.kscience.dataforge.meta.string
public object MongoClientFactory : Factory<CoroutineClient> { internal object DefaultMongoConfig {
public const val connectionString: String = "mongodb://mongoadmin:secret@localhost:27888" const val databaseName = "deviceMessage"
}
override fun invoke(meta: Meta, context: Context): CoroutineClient = meta["connectionString"]?.string?.let { public object MongoClientFactory : Factory<CoroutineClient> {
private const val connectionString: String = "mongodb://mongoadmin:secret@localhost:27888"
override fun invoke(meta: Meta, context: Context): CoroutineClient = meta["mongoConfig"]?.get("connectionString")?.string?.let {
KMongo.createClient(it).coroutine KMongo.createClient(it).coroutine
} ?: KMongo.createClient(connectionString).coroutine } ?: KMongo.createClient(connectionString).coroutine
} }
@OptIn(InternalCoroutinesApi::class)
public fun DeviceManager.connectMongo( public fun DeviceManager.connectMongo(
client: CoroutineClient, factory: Factory<CoroutineClient>,
filterCondition: suspend (DeviceMessage) -> Boolean = { true } filterCondition: suspend (DeviceMessage) -> Boolean = { true }
): Job = hubMessageFlow(context).filter(filterCondition).onEach { message -> ): Job {
context.launch { val client = factory.invoke(meta, context)
client logger.debug { "Mongo client opened" }
.getDatabase("deviceServer") val collection = client
.getCollection<DeviceMessage>() .getDatabase(meta["mongoConfig"]?.get("databaseName")?.string ?: DefaultMongoConfig.databaseName)
.insertOne(Json.encodeToString(message)) .getCollection<DeviceMessage>()
} return hubMessageFlow(context).filter(filterCondition).onEach { message ->
}.launchIn(context).apply { context.launch {
invokeOnCompletion { collection.insertOne(Json.encodeToString(message))
client.close() }
}.launchIn(context).apply {
invokeOnCompletion(onCancelling = true) {
logger.debug { "Mongo client closed" }
client.close()
}
} }
} }

View File

@ -1,26 +1,57 @@
package ru.mipt.npm.controls.xodus package ru.mipt.npm.controls.xodus
import io.ktor.application.*
import jetbrains.exodus.entitystore.PersistentEntityStore import jetbrains.exodus.entitystore.PersistentEntityStore
import jetbrains.exodus.entitystore.PersistentEntityStores
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.job
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import ru.mipt.npm.controls.api.DeviceMessage import ru.mipt.npm.controls.api.DeviceMessage
import ru.mipt.npm.controls.api.PropertyChangedMessage import ru.mipt.npm.controls.api.PropertyChangedMessage
import ru.mipt.npm.controls.controllers.DeviceManager import ru.mipt.npm.controls.controllers.DeviceManager
import ru.mipt.npm.controls.controllers.hubMessageFlow import ru.mipt.npm.controls.controllers.hubMessageFlow
import ru.mipt.npm.magix.server.GenericMagixMessage import ru.mipt.npm.magix.server.GenericMagixMessage
import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.Factory
import space.kscience.dataforge.context.debug
import space.kscience.dataforge.context.logger
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.string
import java.nio.file.Paths
internal object DefaultXodusConfig {
val entityStorePath = Paths.get(".messages")
}
public fun DeviceManager.connectXodus( public object EntityStoreFactory : Factory<PersistentEntityStore> {
entityStore: PersistentEntityStore, override fun invoke(meta: Meta, context: Context): PersistentEntityStore {
filterCondition: suspend (DeviceMessage) -> Boolean = { it is PropertyChangedMessage } return PersistentEntityStores.newInstance(
): Job = hubMessageFlow(context).filter(filterCondition).onEach { message -> meta["xodusConfig"]?.get("entityStorePath")?.string ?: DefaultXodusConfig.entityStorePath.toString()
entityStore.executeInTransaction { )
(message as PropertyChangedMessage).toEntity(it)
} }
}.launchIn(context) }
@OptIn(InternalCoroutinesApi::class)
public fun DeviceManager.connectXodus(
factory: Factory<PersistentEntityStore>,
filterCondition: suspend (DeviceMessage) -> Boolean = { it is PropertyChangedMessage }
): Job {
val entityStore = factory.invoke(meta, context)
logger.debug { "Device entity store opened" }
return hubMessageFlow(context).filter(filterCondition).onEach { message ->
entityStore.executeInTransaction {
(message as PropertyChangedMessage).toEntity(it)
}
}.launchIn(context).apply {
invokeOnCompletion(onCancelling = true) {
entityStore.close()
logger.debug { "Device entity store closed" }
}
}
}
//public fun CoroutineScope.startMagixServer( //public fun CoroutineScope.startMagixServer(
// entityStore: PersistentEntityStore, // entityStore: PersistentEntityStore,
@ -53,3 +84,17 @@ public fun SharedFlow<GenericMagixMessage>.storeInXodus(
} }
} }
} }
@OptIn(InternalCoroutinesApi::class)
public fun Application.storeInXodus(
factory: Factory<PersistentEntityStore>,
flow: MutableSharedFlow<GenericMagixMessage>,
meta: Meta = Meta.EMPTY
) {
val entityStore = factory.invoke(meta)
flow.storeInXodus(entityStore)
coroutineContext.job.invokeOnCompletion(onCancelling = true) {
entityStore.close()
}
}

View File

@ -6,11 +6,8 @@ import javafx.scene.Parent
import javafx.scene.control.TextField import javafx.scene.control.TextField
import javafx.scene.layout.Priority import javafx.scene.layout.Priority
import javafx.stage.Stage import javafx.stage.Stage
import jetbrains.exodus.entitystore.PersistentEntityStore
import jetbrains.exodus.entitystore.PersistentEntityStores
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.litote.kmongo.coroutine.CoroutineClient
import ru.mipt.npm.controls.api.DeviceMessage import ru.mipt.npm.controls.api.DeviceMessage
import ru.mipt.npm.controls.client.connectToMagix import ru.mipt.npm.controls.client.connectToMagix
import ru.mipt.npm.controls.controllers.DeviceManager import ru.mipt.npm.controls.controllers.DeviceManager
@ -18,6 +15,7 @@ import ru.mipt.npm.controls.controllers.install
import ru.mipt.npm.controls.demo.car.IVirtualCar.Companion.acceleration import ru.mipt.npm.controls.demo.car.IVirtualCar.Companion.acceleration
import ru.mipt.npm.controls.mongo.MongoClientFactory import ru.mipt.npm.controls.mongo.MongoClientFactory
import ru.mipt.npm.controls.mongo.connectMongo import ru.mipt.npm.controls.mongo.connectMongo
import ru.mipt.npm.controls.xodus.EntityStoreFactory
import ru.mipt.npm.controls.xodus.connectXodus import ru.mipt.npm.controls.xodus.connectXodus
import ru.mipt.npm.controls.xodus.storeInXodus import ru.mipt.npm.controls.xodus.storeInXodus
import ru.mipt.npm.magix.api.MagixEndpoint import ru.mipt.npm.magix.api.MagixEndpoint
@ -28,14 +26,16 @@ import space.kscience.dataforge.meta.Meta
import tornadofx.* import tornadofx.*
import java.nio.file.Paths import java.nio.file.Paths
internal object VirtualCarControllerConfig {
val deviceEntityStorePath = Paths.get(".messages")
val magixEntityStorePath = Paths.get(".server_messages")
}
class VirtualCarController : Controller(), ContextAware { class VirtualCarController : Controller(), ContextAware {
var virtualCar: VirtualCar? = null var virtualCar: VirtualCar? = null
var magixVirtualCar: MagixVirtualCar? = null var magixVirtualCar: MagixVirtualCar? = null
var magixServer: ApplicationEngine? = null var magixServer: ApplicationEngine? = null
var deviceEntityStore: PersistentEntityStore? = null
var magixEntityStore: PersistentEntityStore? = null
var mongoClient: CoroutineClient? = null
var xodusStorageJob: Job? = null var xodusStorageJob: Job? = null
var mongoStorageJob: Job? = null var mongoStorageJob: Job? = null
@ -43,25 +43,29 @@ class VirtualCarController : Controller(), ContextAware {
plugin(DeviceManager) plugin(DeviceManager)
} }
private val deviceManager = context.fetch(DeviceManager) private val deviceManager = context.fetch(DeviceManager, Meta {
"xodusConfig" put {
"entityStorePath" put VirtualCarControllerConfig.deviceEntityStorePath.toString()
}
})
fun init() { fun init() {
context.launch { context.launch {
virtualCar = deviceManager.install("virtual-car", VirtualCar) virtualCar = deviceManager.install("virtual-car", VirtualCar)
//starting magix event loop and connect it to entity store //starting magix event loop and connect it to entity store
magixEntityStore = PersistentEntityStores.newInstance(Paths.get(".server_messages").toFile())
magixServer = startMagixServer(enableRawRSocket = true, enableZmq = true) { flow -> magixServer = startMagixServer(enableRawRSocket = true, enableZmq = true) { flow ->
flow.storeInXodus(magixEntityStore as PersistentEntityStore) storeInXodus(EntityStoreFactory, flow, Meta {
"xodusConfig" put {
"entityStorePath" put VirtualCarControllerConfig.magixEntityStorePath.toString()
}
})
} }
magixVirtualCar = deviceManager.install("magix-virtual-car", MagixVirtualCar) magixVirtualCar = deviceManager.install("magix-virtual-car", MagixVirtualCar)
deviceEntityStore = PersistentEntityStores.newInstance(Paths.get(".messages").toFile())
//connect to device entity store //connect to device entity store
xodusStorageJob = deviceManager.connectXodus(deviceEntityStore as PersistentEntityStore) xodusStorageJob = deviceManager.connectXodus(EntityStoreFactory)
//Create mongo client and connect to MongoDB //Create mongo client and connect to MongoDB
val mongoClient = MongoClientFactory.invoke(meta = Meta.EMPTY, context) mongoStorageJob = deviceManager.connectMongo(MongoClientFactory)
mongoStorageJob = deviceManager.connectMongo(mongoClient)
this@VirtualCarController.mongoClient = mongoClient
//Launch device client and connect it to the server //Launch device client and connect it to the server
val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost", DeviceMessage.serializer()) val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost", DeviceMessage.serializer())
deviceManager.connectToMagix(deviceEndpoint) deviceManager.connectToMagix(deviceEndpoint)
@ -76,12 +80,6 @@ class VirtualCarController : Controller(), ContextAware {
logger.info { "Magix virtual car server stopped" } logger.info { "Magix virtual car server stopped" }
virtualCar?.close() virtualCar?.close()
logger.info { "Virtual car server stopped" } logger.info { "Virtual car server stopped" }
deviceEntityStore?.close()
logger.info { "Device entity store closed" }
magixEntityStore?.close()
logger.info { "Magix entity store closed" }
mongoClient?.close()
logger.info { "MongoClient closed" }
context.close() context.close()
} }
} }