From c5abbbbd2df834eb65a6ec142c1093d85df0a3cb Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Mon, 5 Dec 2022 20:04:50 +0600 Subject: [PATCH 01/11] start 0.15.1 --- CHANGELOG.md | 2 ++ gradle.properties | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd51bd38dfd..d44d2416eff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog +## 0.15.1 + ## 0.15.0 * `Repos`: diff --git a/gradle.properties b/gradle.properties index 19ccba28486..62d5245814b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,5 +14,5 @@ crypto_js_version=4.1.1 # Project data group=dev.inmo -version=0.15.0 -android_code_version=166 +version=0.15.1 +android_code_version=167 From 369ff266279a93710c56471e98a4ec7263f38fdb Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Mon, 5 Dec 2022 21:19:38 +0600 Subject: [PATCH 02/11] initialize startup module --- gradle/libs.versions.toml | 3 ++ mppProjectWithSerialization.gradle | 2 +- mppProjectWithSerializationAndCompose.gradle | 2 +- settings.gradle | 2 + startup/launcher/build.gradle | 17 +++++++++ .../launcher/src/commonMain/kotlin/Config.kt | 9 +++++ .../src/commonMain/kotlin/ServerLauncher.kt | 38 +++++++++++++++++++ .../src/jvmMain/kotlin/ServerLauncher.kt | 24 ++++++++++++ startup/plugin/build.gradle | 17 +++++++++ .../src/commonMain/kotlin/ServerPlugin.kt | 34 +++++++++++++++++ 10 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 startup/launcher/build.gradle create mode 100644 startup/launcher/src/commonMain/kotlin/Config.kt create mode 100644 startup/launcher/src/commonMain/kotlin/ServerLauncher.kt create mode 100644 startup/launcher/src/jvmMain/kotlin/ServerLauncher.kt create mode 100644 startup/plugin/build.gradle create mode 100644 startup/plugin/src/commonMain/kotlin/ServerPlugin.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 493ed2a0e72..b5c564a71a4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,6 +4,8 @@ kt = "1.7.20" kt-serialization = "1.4.1" kt-coroutines = "1.6.4" +kslog = "0.5.4" + jb-compose = "1.2.1" jb-exposed = "0.41.1" jb-dokka = "1.7.20" @@ -60,6 +62,7 @@ ktor-server-websockets = { module = "io.ktor:ktor-server-websockets", version.re ktor-server-statusPages = { module = "io.ktor:ktor-server-status-pages", version.ref = "ktor" } ktor-server-content-negotiation = { module = "io.ktor:ktor-server-content-negotiation", version.ref = "ktor" } +kslog = { module = "dev.inmo:kslog", version.ref = "kslog" } klock = { module = "com.soywiz.korlibs.klock:klock", version.ref = "klock" } uuid = { module = "com.benasher44:uuid", version.ref = "uuid" } diff --git a/mppProjectWithSerialization.gradle b/mppProjectWithSerialization.gradle index 615ec80d132..05177943155 100644 --- a/mppProjectWithSerialization.gradle +++ b/mppProjectWithSerialization.gradle @@ -23,7 +23,7 @@ kotlin { commonMain { dependencies { implementation kotlin('stdlib') - implementation libs.kt.serialization + api libs.kt.serialization } } commonTest { diff --git a/mppProjectWithSerializationAndCompose.gradle b/mppProjectWithSerializationAndCompose.gradle index eff5f7fe0c0..e5fa8355c23 100644 --- a/mppProjectWithSerializationAndCompose.gradle +++ b/mppProjectWithSerializationAndCompose.gradle @@ -23,7 +23,7 @@ kotlin { commonMain { dependencies { implementation kotlin('stdlib') - implementation libs.kt.serialization + api libs.kt.serialization implementation compose.runtime } } diff --git a/settings.gradle b/settings.gradle index ccd2dc06d84..069bd208c00 100644 --- a/settings.gradle +++ b/settings.gradle @@ -32,6 +32,8 @@ String[] includes = [ ":serialization:base64", ":serialization:encapsulator", ":serialization:typed_serializer", + ":startup:plugin", + ":startup:launcher", ":fsm:common", ":fsm:repos:common", diff --git a/startup/launcher/build.gradle b/startup/launcher/build.gradle new file mode 100644 index 00000000000..5b3c3a61734 --- /dev/null +++ b/startup/launcher/build.gradle @@ -0,0 +1,17 @@ +plugins { + id "org.jetbrains.kotlin.multiplatform" + id "org.jetbrains.kotlin.plugin.serialization" +} + +apply from: "$mppJavaProjectPresetPath" + +kotlin { + sourceSets { + commonMain { + dependencies { + api internalProject("micro_utils.startup.plugin") + api libs.kslog + } + } + } +} diff --git a/startup/launcher/src/commonMain/kotlin/Config.kt b/startup/launcher/src/commonMain/kotlin/Config.kt new file mode 100644 index 00000000000..f52ad983aea --- /dev/null +++ b/startup/launcher/src/commonMain/kotlin/Config.kt @@ -0,0 +1,9 @@ +package dev.inmo.micro_utils.startup.launcher + +import dev.inmo.micro_utils.startup.plugin.ServerPlugin +import kotlinx.serialization.Serializable + +@Serializable +data class Config( + val plugins: List +) diff --git a/startup/launcher/src/commonMain/kotlin/ServerLauncher.kt b/startup/launcher/src/commonMain/kotlin/ServerLauncher.kt new file mode 100644 index 00000000000..9f63ef3add7 --- /dev/null +++ b/startup/launcher/src/commonMain/kotlin/ServerLauncher.kt @@ -0,0 +1,38 @@ +package dev.inmo.micro_utils.startup.launcher + +import dev.inmo.micro_utils.startup.plugin.ServerPlugin +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonObject +import org.koin.core.Koin +import org.koin.core.module.Module +import org.koin.dsl.module + +class ServerLauncher : ServerPlugin { + val defaultJson = Json { + ignoreUnknownKeys = true + } + + override fun Module.setupDI(config: JsonObject) { + val pluginsConfig = defaultJson.decodeFromJsonElement(Config.serializer(), config) + + single { pluginsConfig } + + includes( + pluginsConfig.plugins.map { + module { + with(it) { + setupDI(config) + } + } + } + ) + } + + override suspend fun Koin.startPlugin() { + get().plugins.forEach { + with(it) { + startPlugin() + } + } + } +} diff --git a/startup/launcher/src/jvmMain/kotlin/ServerLauncher.kt b/startup/launcher/src/jvmMain/kotlin/ServerLauncher.kt new file mode 100644 index 00000000000..b412f331e92 --- /dev/null +++ b/startup/launcher/src/jvmMain/kotlin/ServerLauncher.kt @@ -0,0 +1,24 @@ +package dev.inmo.micro_utils.startup.launcher + +import dev.inmo.kslog.common.KSLog +import dev.inmo.kslog.common.i +import dev.inmo.micro_utils.startup.plugin.ServerPlugin +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonObject +import org.koin.core.Koin +import org.koin.core.module.Module +import org.koin.dsl.module +import java.io.File + +suspend fun main(args: Array) { + + KSLog.default = KSLog("PlaguBot") + val (configPath) = args + val file = File(configPath) + KSLog.i("Start read config from ${file.absolutePath}") + val json = defaultJsonFormat.parseToJsonElement(file.readText()).jsonObject + val config = defaultJsonFormat.decodeFromJsonElement(Config.serializer(), json) + KSLog.i("Config has been read") + + PlaguBot(json, config).start().join() +} diff --git a/startup/plugin/build.gradle b/startup/plugin/build.gradle new file mode 100644 index 00000000000..f7e590a535b --- /dev/null +++ b/startup/plugin/build.gradle @@ -0,0 +1,17 @@ +plugins { + id "org.jetbrains.kotlin.multiplatform" + id "org.jetbrains.kotlin.plugin.serialization" +} + +apply from: "$mppJavaProjectPresetPath" + +kotlin { + sourceSets { + commonMain { + dependencies { + api libs.koin + api libs.kt.serialization + } + } + } +} diff --git a/startup/plugin/src/commonMain/kotlin/ServerPlugin.kt b/startup/plugin/src/commonMain/kotlin/ServerPlugin.kt new file mode 100644 index 00000000000..91cfc36f0b5 --- /dev/null +++ b/startup/plugin/src/commonMain/kotlin/ServerPlugin.kt @@ -0,0 +1,34 @@ +package dev.inmo.micro_utils.startup.plugin + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable +import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.json.JsonObject +import org.koin.core.Koin +import org.koin.core.module.Module + +@Serializable(ServerPlugin.Companion::class) +interface ServerPlugin { + fun Module.setupDI(config: JsonObject) {} + + suspend fun Koin.startPlugin() {} + + companion object : KSerializer { + override val descriptor: SerialDescriptor + get() = String.serializer().descriptor + + override fun deserialize(decoder: Decoder): ServerPlugin { + val kclass = Class.forName(decoder.decodeString()).kotlin + return (kclass.objectInstance ?: kclass.constructors.first { it.parameters.isEmpty() }.call()) as ServerPlugin + } + + override fun serialize(encoder: Encoder, value: ServerPlugin) { + encoder.encodeString( + value::class.java.canonicalName + ) + } + } +} From 072805efc7aec39d50d45fcad3f7325a19a3bacf Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Mon, 5 Dec 2022 21:45:30 +0600 Subject: [PATCH 03/11] Update ServerLauncher.kt --- startup/launcher/src/commonMain/kotlin/ServerLauncher.kt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/startup/launcher/src/commonMain/kotlin/ServerLauncher.kt b/startup/launcher/src/commonMain/kotlin/ServerLauncher.kt index 9f63ef3add7..a7f871d261a 100644 --- a/startup/launcher/src/commonMain/kotlin/ServerLauncher.kt +++ b/startup/launcher/src/commonMain/kotlin/ServerLauncher.kt @@ -7,11 +7,7 @@ import org.koin.core.Koin import org.koin.core.module.Module import org.koin.dsl.module -class ServerLauncher : ServerPlugin { - val defaultJson = Json { - ignoreUnknownKeys = true - } - +object ServerLauncher : ServerPlugin { override fun Module.setupDI(config: JsonObject) { val pluginsConfig = defaultJson.decodeFromJsonElement(Config.serializer(), config) From c9514d3a6d7b08cfa8c20ec799a1947104658722 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Mon, 5 Dec 2022 21:47:00 +0600 Subject: [PATCH 04/11] Create DefaultJson.kt --- startup/launcher/src/commonMain/kotlin/DefaultJson.kt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 startup/launcher/src/commonMain/kotlin/DefaultJson.kt diff --git a/startup/launcher/src/commonMain/kotlin/DefaultJson.kt b/startup/launcher/src/commonMain/kotlin/DefaultJson.kt new file mode 100644 index 00000000000..4e921791e87 --- /dev/null +++ b/startup/launcher/src/commonMain/kotlin/DefaultJson.kt @@ -0,0 +1,7 @@ +package dev.inmo.micro_utils.startup.launcher + +import kotlinx.serialization.json.Json + +val defaultJson = Json { + ignoreUnknownKeys = true +} From 2a4570eafce07d07e23fdc80ddfd499e8e660745 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Mon, 5 Dec 2022 21:52:36 +0600 Subject: [PATCH 05/11] Update ServerLauncher.kt --- .../launcher/src/jvmMain/kotlin/ServerLauncher.kt | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/startup/launcher/src/jvmMain/kotlin/ServerLauncher.kt b/startup/launcher/src/jvmMain/kotlin/ServerLauncher.kt index b412f331e92..690e1813227 100644 --- a/startup/launcher/src/jvmMain/kotlin/ServerLauncher.kt +++ b/startup/launcher/src/jvmMain/kotlin/ServerLauncher.kt @@ -10,15 +10,23 @@ import org.koin.core.module.Module import org.koin.dsl.module import java.io.File +package dev.inmo.micro_utils.startup.launcher + +import dev.inmo. + +class ServerLauncher : ServerPlugin { + val defaultJson = Json { + ignoreUnknownKeys = true + } + suspend fun main(args: Array) { KSLog.default = KSLog("PlaguBot") val (configPath) = args val file = File(configPath) KSLog.i("Start read config from ${file.absolutePath}") - val json = defaultJsonFormat.parseToJsonElement(file.readText()).jsonObject - val config = defaultJsonFormat.decodeFromJsonElement(Config.serializer(), json) + val json = defaultJson.parseToJsonElement(file.readText()).jsonObject KSLog.i("Config has been read") - PlaguBot(json, config).start().join() + ServerLauncher.start().join() } From b17931e7bd3ac74bc54f46d995cf30c6b4d6edbf Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Mon, 5 Dec 2022 22:31:15 +0600 Subject: [PATCH 06/11] complete startup module --- startup/launcher/build.gradle | 1 - .../src/commonMain/kotlin/ServerLauncher.kt | 34 ----------- .../src/commonMain/kotlin/StartupLauncher.kt | 57 +++++++++++++++++++ .../src/jvmMain/kotlin/ServerLauncher.kt | 36 ++++++------ startup/plugin/build.gradle | 2 + .../src/commonMain/kotlin/ServerPlugin.kt | 2 +- 6 files changed, 80 insertions(+), 52 deletions(-) delete mode 100644 startup/launcher/src/commonMain/kotlin/ServerLauncher.kt create mode 100644 startup/launcher/src/commonMain/kotlin/StartupLauncher.kt diff --git a/startup/launcher/build.gradle b/startup/launcher/build.gradle index 5b3c3a61734..5e762e51950 100644 --- a/startup/launcher/build.gradle +++ b/startup/launcher/build.gradle @@ -10,7 +10,6 @@ kotlin { commonMain { dependencies { api internalProject("micro_utils.startup.plugin") - api libs.kslog } } } diff --git a/startup/launcher/src/commonMain/kotlin/ServerLauncher.kt b/startup/launcher/src/commonMain/kotlin/ServerLauncher.kt deleted file mode 100644 index a7f871d261a..00000000000 --- a/startup/launcher/src/commonMain/kotlin/ServerLauncher.kt +++ /dev/null @@ -1,34 +0,0 @@ -package dev.inmo.micro_utils.startup.launcher - -import dev.inmo.micro_utils.startup.plugin.ServerPlugin -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonObject -import org.koin.core.Koin -import org.koin.core.module.Module -import org.koin.dsl.module - -object ServerLauncher : ServerPlugin { - override fun Module.setupDI(config: JsonObject) { - val pluginsConfig = defaultJson.decodeFromJsonElement(Config.serializer(), config) - - single { pluginsConfig } - - includes( - pluginsConfig.plugins.map { - module { - with(it) { - setupDI(config) - } - } - } - ) - } - - override suspend fun Koin.startPlugin() { - get().plugins.forEach { - with(it) { - startPlugin() - } - } - } -} diff --git a/startup/launcher/src/commonMain/kotlin/StartupLauncher.kt b/startup/launcher/src/commonMain/kotlin/StartupLauncher.kt new file mode 100644 index 00000000000..9640df8f299 --- /dev/null +++ b/startup/launcher/src/commonMain/kotlin/StartupLauncher.kt @@ -0,0 +1,57 @@ +package dev.inmo.micro_utils.startup.launcher + +import dev.inmo.kslog.common.i +import dev.inmo.kslog.common.taggedLogger +import dev.inmo.kslog.common.w +import dev.inmo.micro_utils.coroutines.runCatchingSafely +import dev.inmo.micro_utils.startup.plugin.ServerPlugin +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.joinAll +import kotlinx.coroutines.launch +import kotlinx.serialization.json.JsonObject +import org.koin.core.Koin +import org.koin.core.module.Module +import org.koin.dsl.module + +object StartupLauncher : ServerPlugin { + internal val logger = taggedLogger(this) + override fun Module.setupDI(config: JsonObject) { + val pluginsConfig = defaultJson.decodeFromJsonElement(Config.serializer(), config) + + single { pluginsConfig } + single { CoroutineScope(Dispatchers.Default) } + + includes( + pluginsConfig.plugins.mapNotNull { + runCatching { + module { + with(it) { + setupDI(config) + } + } + }.onFailure { e -> + logger.w("Unable to load DI part of $it", e) + }.getOrNull() + } + ) + } + + override suspend fun startPlugin(koin: Koin) { + val scope = koin.get() + koin.get().plugins.map { plugin -> + scope.launch { + runCatchingSafely { + logger.i("Start loading of $plugin") + with(plugin) { + startPlugin(koin) + } + }.onFailure { e -> + logger.w("Unable to load bot part of $plugin", e) + }.onSuccess { + logger.i("Complete loading of $plugin") + } + } + }.joinAll() + } +} diff --git a/startup/launcher/src/jvmMain/kotlin/ServerLauncher.kt b/startup/launcher/src/jvmMain/kotlin/ServerLauncher.kt index 690e1813227..2a72b7707af 100644 --- a/startup/launcher/src/jvmMain/kotlin/ServerLauncher.kt +++ b/startup/launcher/src/jvmMain/kotlin/ServerLauncher.kt @@ -2,31 +2,35 @@ package dev.inmo.micro_utils.startup.launcher import dev.inmo.kslog.common.KSLog import dev.inmo.kslog.common.i -import dev.inmo.micro_utils.startup.plugin.ServerPlugin -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonObject -import org.koin.core.Koin -import org.koin.core.module.Module +import dev.inmo.micro_utils.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.serialization.json.jsonObject +import org.koin.core.KoinApplication +import org.koin.core.context.GlobalContext import org.koin.dsl.module import java.io.File -package dev.inmo.micro_utils.startup.launcher - -import dev.inmo. - -class ServerLauncher : ServerPlugin { - val defaultJson = Json { - ignoreUnknownKeys = true - } - suspend fun main(args: Array) { - KSLog.default = KSLog("PlaguBot") + KSLog.default = KSLog("ServerLauncher") val (configPath) = args val file = File(configPath) KSLog.i("Start read config from ${file.absolutePath}") val json = defaultJson.parseToJsonElement(file.readText()).jsonObject KSLog.i("Config has been read") - ServerLauncher.start().join() + with(StartupLauncher) { + logger.i("Start initialization") + val koinApp = KoinApplication.init() + koinApp.modules( + module { + setupDI(json) + } + ) + logger.i("Modules loaded") + GlobalContext.startKoin(koinApp) + logger.i("Koin started") + startPlugin(koinApp.koin) + logger.i("Behaviour builder has been setup") + } } diff --git a/startup/plugin/build.gradle b/startup/plugin/build.gradle index f7e590a535b..851613c225e 100644 --- a/startup/plugin/build.gradle +++ b/startup/plugin/build.gradle @@ -11,6 +11,8 @@ kotlin { dependencies { api libs.koin api libs.kt.serialization + api libs.kslog + api project(":micro_utils.coroutines") } } } diff --git a/startup/plugin/src/commonMain/kotlin/ServerPlugin.kt b/startup/plugin/src/commonMain/kotlin/ServerPlugin.kt index 91cfc36f0b5..25019809ad5 100644 --- a/startup/plugin/src/commonMain/kotlin/ServerPlugin.kt +++ b/startup/plugin/src/commonMain/kotlin/ServerPlugin.kt @@ -14,7 +14,7 @@ import org.koin.core.module.Module interface ServerPlugin { fun Module.setupDI(config: JsonObject) {} - suspend fun Koin.startPlugin() {} + suspend fun startPlugin(koin: Koin) {} companion object : KSerializer { override val descriptor: SerialDescriptor From 02dbd493c2819ba5e6fb923a69c64700654d581a Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Tue, 6 Dec 2022 12:38:24 +0600 Subject: [PATCH 07/11] add tests and make several replacements/improvements --- startup/launcher/build.gradle | 10 +++++ .../src/commonMain/kotlin/HelloWorldPlugin.kt | 13 +++++++ .../launcher/src/commonMain/kotlin/Start.kt | 24 ++++++++++++ ...upLauncher.kt => StartupLauncherPlugin.kt} | 2 +- startup/launcher/src/jvmMain/kotlin/Main.kt | 18 +++++++++ .../src/jvmMain/kotlin/ServerLauncher.kt | 36 ----------------- .../jvmTest/kotlin/StartupLaunchingTests.kt | 39 +++++++++++++++++++ startup/plugin/build.gradle | 1 + .../src/commonMain/kotlin/ServerPlugin.kt | 23 +---------- .../kotlin/ServerPluginSerializer.kt | 5 +++ .../jvmMain/kotlin/ServerPluginSerializer.kt | 23 +++++++++++ 11 files changed, 135 insertions(+), 59 deletions(-) create mode 100644 startup/launcher/src/commonMain/kotlin/HelloWorldPlugin.kt create mode 100644 startup/launcher/src/commonMain/kotlin/Start.kt rename startup/launcher/src/commonMain/kotlin/{StartupLauncher.kt => StartupLauncherPlugin.kt} (97%) create mode 100644 startup/launcher/src/jvmMain/kotlin/Main.kt delete mode 100644 startup/launcher/src/jvmMain/kotlin/ServerLauncher.kt create mode 100644 startup/launcher/src/jvmTest/kotlin/StartupLaunchingTests.kt create mode 100644 startup/plugin/src/commonMain/kotlin/ServerPluginSerializer.kt create mode 100644 startup/plugin/src/jvmMain/kotlin/ServerPluginSerializer.kt diff --git a/startup/launcher/build.gradle b/startup/launcher/build.gradle index 5e762e51950..c4905f21051 100644 --- a/startup/launcher/build.gradle +++ b/startup/launcher/build.gradle @@ -1,6 +1,7 @@ plugins { id "org.jetbrains.kotlin.multiplatform" id "org.jetbrains.kotlin.plugin.serialization" + id "application" } apply from: "$mppJavaProjectPresetPath" @@ -14,3 +15,12 @@ kotlin { } } } + +application { + mainClassName = "dev.inmo.micro_utils.startup.launcher.ServerLauncherKt" +} + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} diff --git a/startup/launcher/src/commonMain/kotlin/HelloWorldPlugin.kt b/startup/launcher/src/commonMain/kotlin/HelloWorldPlugin.kt new file mode 100644 index 00000000000..81842523b23 --- /dev/null +++ b/startup/launcher/src/commonMain/kotlin/HelloWorldPlugin.kt @@ -0,0 +1,13 @@ +package dev.inmo.micro_utils.startup.launcher + +import dev.inmo.kslog.common.i +import dev.inmo.kslog.common.logger +import dev.inmo.micro_utils.startup.plugin.ServerPlugin +import org.koin.core.Koin + +object HelloWorldPlugin : ServerPlugin { + override suspend fun startPlugin(koin: Koin) { + super.startPlugin(koin) + logger.i("Hello world") + } +} diff --git a/startup/launcher/src/commonMain/kotlin/Start.kt b/startup/launcher/src/commonMain/kotlin/Start.kt new file mode 100644 index 00000000000..ae17d7a668b --- /dev/null +++ b/startup/launcher/src/commonMain/kotlin/Start.kt @@ -0,0 +1,24 @@ +package dev.inmo.micro_utils.startup.launcher + +import dev.inmo.kslog.common.i +import kotlinx.serialization.json.JsonObject +import org.koin.core.KoinApplication +import org.koin.core.context.GlobalContext +import org.koin.dsl.module + +suspend fun start(rawConfig: JsonObject) { + with(StartupLauncherPlugin) { + logger.i("Start initialization") + val koinApp = KoinApplication.init() + koinApp.modules( + module { + setupDI(rawConfig) + } + ) + logger.i("Modules loaded") + GlobalContext.startKoin(koinApp) + logger.i("Koin started") + startPlugin(koinApp.koin) + logger.i("App has been setup") + } +} diff --git a/startup/launcher/src/commonMain/kotlin/StartupLauncher.kt b/startup/launcher/src/commonMain/kotlin/StartupLauncherPlugin.kt similarity index 97% rename from startup/launcher/src/commonMain/kotlin/StartupLauncher.kt rename to startup/launcher/src/commonMain/kotlin/StartupLauncherPlugin.kt index 9640df8f299..4e98131747c 100644 --- a/startup/launcher/src/commonMain/kotlin/StartupLauncher.kt +++ b/startup/launcher/src/commonMain/kotlin/StartupLauncherPlugin.kt @@ -14,7 +14,7 @@ import org.koin.core.Koin import org.koin.core.module.Module import org.koin.dsl.module -object StartupLauncher : ServerPlugin { +object StartupLauncherPlugin : ServerPlugin { internal val logger = taggedLogger(this) override fun Module.setupDI(config: JsonObject) { val pluginsConfig = defaultJson.decodeFromJsonElement(Config.serializer(), config) diff --git a/startup/launcher/src/jvmMain/kotlin/Main.kt b/startup/launcher/src/jvmMain/kotlin/Main.kt new file mode 100644 index 00000000000..d4f1789f2b4 --- /dev/null +++ b/startup/launcher/src/jvmMain/kotlin/Main.kt @@ -0,0 +1,18 @@ +package dev.inmo.micro_utils.startup.launcher + +import dev.inmo.kslog.common.KSLog +import dev.inmo.kslog.common.i +import kotlinx.serialization.json.jsonObject +import java.io.File + +suspend fun main(args: Array) { + + KSLog.default = KSLog("ServerLauncher") + val (configPath) = args + val file = File(configPath) + KSLog.i("Start read config from ${file.absolutePath}") + val json = defaultJson.parseToJsonElement(file.readText()).jsonObject + KSLog.i("Config has been read") + + start(json) +} diff --git a/startup/launcher/src/jvmMain/kotlin/ServerLauncher.kt b/startup/launcher/src/jvmMain/kotlin/ServerLauncher.kt deleted file mode 100644 index 2a72b7707af..00000000000 --- a/startup/launcher/src/jvmMain/kotlin/ServerLauncher.kt +++ /dev/null @@ -1,36 +0,0 @@ -package dev.inmo.micro_utils.startup.launcher - -import dev.inmo.kslog.common.KSLog -import dev.inmo.kslog.common.i -import dev.inmo.micro_utils.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.serialization.json.jsonObject -import org.koin.core.KoinApplication -import org.koin.core.context.GlobalContext -import org.koin.dsl.module -import java.io.File - -suspend fun main(args: Array) { - - KSLog.default = KSLog("ServerLauncher") - val (configPath) = args - val file = File(configPath) - KSLog.i("Start read config from ${file.absolutePath}") - val json = defaultJson.parseToJsonElement(file.readText()).jsonObject - KSLog.i("Config has been read") - - with(StartupLauncher) { - logger.i("Start initialization") - val koinApp = KoinApplication.init() - koinApp.modules( - module { - setupDI(json) - } - ) - logger.i("Modules loaded") - GlobalContext.startKoin(koinApp) - logger.i("Koin started") - startPlugin(koinApp.koin) - logger.i("Behaviour builder has been setup") - } -} diff --git a/startup/launcher/src/jvmTest/kotlin/StartupLaunchingTests.kt b/startup/launcher/src/jvmTest/kotlin/StartupLaunchingTests.kt new file mode 100644 index 00000000000..7f9f80aea50 --- /dev/null +++ b/startup/launcher/src/jvmTest/kotlin/StartupLaunchingTests.kt @@ -0,0 +1,39 @@ +import dev.inmo.micro_utils.coroutines.launchSynchronously +import dev.inmo.micro_utils.startup.launcher.Config +import dev.inmo.micro_utils.startup.launcher.HelloWorldPlugin +import dev.inmo.micro_utils.startup.launcher.defaultJson +import dev.inmo.micro_utils.startup.launcher.start +import kotlinx.coroutines.launch +import kotlinx.serialization.json.jsonObject +import kotlin.test.Test + +class StartupLaunchingTests { + @Test(timeout = 1000L) + fun CheckThatEmptyPluginsListLeadsToEndOfMain() { + val emptyJson = defaultJson.encodeToJsonElement( + Config.serializer(), + Config(emptyList()) + ).jsonObject + + launchSynchronously { + val job = launch { + start(emptyJson) + } + job.join() + } + } + @Test(timeout = 1000L) + fun CheckThatHelloWorldPluginsListLeadsToEndOfMain() { + val emptyJson = defaultJson.encodeToJsonElement( + Config.serializer(), + Config(listOf(HelloWorldPlugin)) + ).jsonObject + + launchSynchronously { + val job = launch { + start(emptyJson) + } + job.join() + } + } +} diff --git a/startup/plugin/build.gradle b/startup/plugin/build.gradle index 851613c225e..9177a9e7e61 100644 --- a/startup/plugin/build.gradle +++ b/startup/plugin/build.gradle @@ -12,6 +12,7 @@ kotlin { api libs.koin api libs.kt.serialization api libs.kslog + api libs.kt.reflect api project(":micro_utils.coroutines") } } diff --git a/startup/plugin/src/commonMain/kotlin/ServerPlugin.kt b/startup/plugin/src/commonMain/kotlin/ServerPlugin.kt index 25019809ad5..f40ad8c6db5 100644 --- a/startup/plugin/src/commonMain/kotlin/ServerPlugin.kt +++ b/startup/plugin/src/commonMain/kotlin/ServerPlugin.kt @@ -1,34 +1,13 @@ package dev.inmo.micro_utils.startup.plugin -import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable -import kotlinx.serialization.builtins.serializer -import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.json.JsonObject import org.koin.core.Koin import org.koin.core.module.Module -@Serializable(ServerPlugin.Companion::class) +@Serializable(ServerPluginSerializer::class) interface ServerPlugin { fun Module.setupDI(config: JsonObject) {} suspend fun startPlugin(koin: Koin) {} - - companion object : KSerializer { - override val descriptor: SerialDescriptor - get() = String.serializer().descriptor - - override fun deserialize(decoder: Decoder): ServerPlugin { - val kclass = Class.forName(decoder.decodeString()).kotlin - return (kclass.objectInstance ?: kclass.constructors.first { it.parameters.isEmpty() }.call()) as ServerPlugin - } - - override fun serialize(encoder: Encoder, value: ServerPlugin) { - encoder.encodeString( - value::class.java.canonicalName - ) - } - } } diff --git a/startup/plugin/src/commonMain/kotlin/ServerPluginSerializer.kt b/startup/plugin/src/commonMain/kotlin/ServerPluginSerializer.kt new file mode 100644 index 00000000000..72cc877b175 --- /dev/null +++ b/startup/plugin/src/commonMain/kotlin/ServerPluginSerializer.kt @@ -0,0 +1,5 @@ +package dev.inmo.micro_utils.startup.plugin + +import kotlinx.serialization.KSerializer + +expect object ServerPluginSerializer : KSerializer diff --git a/startup/plugin/src/jvmMain/kotlin/ServerPluginSerializer.kt b/startup/plugin/src/jvmMain/kotlin/ServerPluginSerializer.kt new file mode 100644 index 00000000000..3875b65ac53 --- /dev/null +++ b/startup/plugin/src/jvmMain/kotlin/ServerPluginSerializer.kt @@ -0,0 +1,23 @@ +package dev.inmo.micro_utils.startup.plugin + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder + +actual object ServerPluginSerializer : KSerializer { + override val descriptor: SerialDescriptor + get() = String.serializer().descriptor + + override fun deserialize(decoder: Decoder): ServerPlugin { + val kclass = Class.forName(decoder.decodeString()).kotlin + return (kclass.objectInstance ?: kclass.constructors.first { it.parameters.isEmpty() }.call()) as ServerPlugin + } + + override fun serialize(encoder: Encoder, value: ServerPlugin) { + encoder.encodeString( + value::class.java.canonicalName + ) + } +} From 09d1047260e87e45477ecf7582bcc183d415076a Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Tue, 6 Dec 2022 13:06:21 +0600 Subject: [PATCH 08/11] add kdocs to the startup module --- .../launcher/src/commonMain/kotlin/Config.kt | 4 +-- .../src/commonMain/kotlin/HelloWorldPlugin.kt | 4 +-- .../launcher/src/commonMain/kotlin/Start.kt | 9 +++++- ...uncherPlugin.kt => StartLauncherPlugin.kt} | 23 ++++++++++++-- .../src/commonMain/kotlin/ServerPlugin.kt | 13 -------- .../src/commonMain/kotlin/StartPlugin.kt | 31 +++++++++++++++++++ ...Serializer.kt => StartPluginSerializer.kt} | 2 +- ...Serializer.kt => StartPluginSerializer.kt} | 8 ++--- 8 files changed, 69 insertions(+), 25 deletions(-) rename startup/launcher/src/commonMain/kotlin/{StartupLauncherPlugin.kt => StartLauncherPlugin.kt} (64%) delete mode 100644 startup/plugin/src/commonMain/kotlin/ServerPlugin.kt create mode 100644 startup/plugin/src/commonMain/kotlin/StartPlugin.kt rename startup/plugin/src/commonMain/kotlin/{ServerPluginSerializer.kt => StartPluginSerializer.kt} (57%) rename startup/plugin/src/jvmMain/kotlin/{ServerPluginSerializer.kt => StartPluginSerializer.kt} (70%) diff --git a/startup/launcher/src/commonMain/kotlin/Config.kt b/startup/launcher/src/commonMain/kotlin/Config.kt index f52ad983aea..3609170ad13 100644 --- a/startup/launcher/src/commonMain/kotlin/Config.kt +++ b/startup/launcher/src/commonMain/kotlin/Config.kt @@ -1,9 +1,9 @@ package dev.inmo.micro_utils.startup.launcher -import dev.inmo.micro_utils.startup.plugin.ServerPlugin +import dev.inmo.micro_utils.startup.plugin.StartPlugin import kotlinx.serialization.Serializable @Serializable data class Config( - val plugins: List + val plugins: List ) diff --git a/startup/launcher/src/commonMain/kotlin/HelloWorldPlugin.kt b/startup/launcher/src/commonMain/kotlin/HelloWorldPlugin.kt index 81842523b23..4770025e066 100644 --- a/startup/launcher/src/commonMain/kotlin/HelloWorldPlugin.kt +++ b/startup/launcher/src/commonMain/kotlin/HelloWorldPlugin.kt @@ -2,10 +2,10 @@ package dev.inmo.micro_utils.startup.launcher import dev.inmo.kslog.common.i import dev.inmo.kslog.common.logger -import dev.inmo.micro_utils.startup.plugin.ServerPlugin +import dev.inmo.micro_utils.startup.plugin.StartPlugin import org.koin.core.Koin -object HelloWorldPlugin : ServerPlugin { +object HelloWorldPlugin : StartPlugin { override suspend fun startPlugin(koin: Koin) { super.startPlugin(koin) logger.i("Hello world") diff --git a/startup/launcher/src/commonMain/kotlin/Start.kt b/startup/launcher/src/commonMain/kotlin/Start.kt index ae17d7a668b..7526adb8de9 100644 --- a/startup/launcher/src/commonMain/kotlin/Start.kt +++ b/startup/launcher/src/commonMain/kotlin/Start.kt @@ -6,8 +6,15 @@ import org.koin.core.KoinApplication import org.koin.core.context.GlobalContext import org.koin.dsl.module +/** + * Will create [KoinApplication], init, load modules using [StartLauncherPlugin] and start plugins using the same base + * plugin + * + * @param rawConfig It is expected that this [JsonObject] will contain serialized [Config] ([StartLauncherPlugin] will + * deserialize it in its [StartLauncherPlugin.setupDI] + */ suspend fun start(rawConfig: JsonObject) { - with(StartupLauncherPlugin) { + with(StartLauncherPlugin) { logger.i("Start initialization") val koinApp = KoinApplication.init() koinApp.modules( diff --git a/startup/launcher/src/commonMain/kotlin/StartupLauncherPlugin.kt b/startup/launcher/src/commonMain/kotlin/StartLauncherPlugin.kt similarity index 64% rename from startup/launcher/src/commonMain/kotlin/StartupLauncherPlugin.kt rename to startup/launcher/src/commonMain/kotlin/StartLauncherPlugin.kt index 4e98131747c..fb4b21260d0 100644 --- a/startup/launcher/src/commonMain/kotlin/StartupLauncherPlugin.kt +++ b/startup/launcher/src/commonMain/kotlin/StartLauncherPlugin.kt @@ -4,23 +4,38 @@ import dev.inmo.kslog.common.i import dev.inmo.kslog.common.taggedLogger import dev.inmo.kslog.common.w import dev.inmo.micro_utils.coroutines.runCatchingSafely -import dev.inmo.micro_utils.startup.plugin.ServerPlugin +import dev.inmo.micro_utils.startup.plugin.StartPlugin import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.joinAll import kotlinx.coroutines.launch +import kotlinx.serialization.SerialFormat +import kotlinx.serialization.StringFormat import kotlinx.serialization.json.JsonObject import org.koin.core.Koin import org.koin.core.module.Module +import org.koin.dsl.binds import org.koin.dsl.module -object StartupLauncherPlugin : ServerPlugin { +/** + * Default startup plugin. See [setupDI] and [startPlugin] for more info + */ +object StartLauncherPlugin : StartPlugin { internal val logger = taggedLogger(this) + + /** + * Will deserialize [Config] from [config], register it in receiver [Module] (as well as [CoroutineScope] and + * [kotlinx.serialization.json.Json]) + * + * Besides, in this method will be called [StartPlugin.setupDI] on each plugin from [Config.plugins]. In case when + * some plugin will not be loaded correctly it will be reported throw the [logger] + */ override fun Module.setupDI(config: JsonObject) { val pluginsConfig = defaultJson.decodeFromJsonElement(Config.serializer(), config) single { pluginsConfig } single { CoroutineScope(Dispatchers.Default) } + single { defaultJson } binds arrayOf(StringFormat::class, SerialFormat::class) includes( pluginsConfig.plugins.mapNotNull { @@ -37,6 +52,10 @@ object StartupLauncherPlugin : ServerPlugin { ) } + /** + * Takes [CoroutineScope] and [Config] from the [koin], and call starting of each plugin from [Config.plugins] + * ASYNCHRONOUSLY. Just like in [setupDI], in case of fail in some plugin it will be reported using [logger] + */ override suspend fun startPlugin(koin: Koin) { val scope = koin.get() koin.get().plugins.map { plugin -> diff --git a/startup/plugin/src/commonMain/kotlin/ServerPlugin.kt b/startup/plugin/src/commonMain/kotlin/ServerPlugin.kt deleted file mode 100644 index f40ad8c6db5..00000000000 --- a/startup/plugin/src/commonMain/kotlin/ServerPlugin.kt +++ /dev/null @@ -1,13 +0,0 @@ -package dev.inmo.micro_utils.startup.plugin - -import kotlinx.serialization.Serializable -import kotlinx.serialization.json.JsonObject -import org.koin.core.Koin -import org.koin.core.module.Module - -@Serializable(ServerPluginSerializer::class) -interface ServerPlugin { - fun Module.setupDI(config: JsonObject) {} - - suspend fun startPlugin(koin: Koin) {} -} diff --git a/startup/plugin/src/commonMain/kotlin/StartPlugin.kt b/startup/plugin/src/commonMain/kotlin/StartPlugin.kt new file mode 100644 index 00000000000..dfd4963e3f1 --- /dev/null +++ b/startup/plugin/src/commonMain/kotlin/StartPlugin.kt @@ -0,0 +1,31 @@ +package dev.inmo.micro_utils.startup.plugin + +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonObject +import org.koin.core.Koin +import org.koin.core.module.Module + +/** + * Default plugin for start of your app + */ +@Serializable(StartPluginSerializer::class) +interface StartPlugin { + /** + * This method will be called first to configure [Koin] [Module] related to this plugin. You may use + * [org.koin.core.scope.Scope.get] in your koin definitions like [Module.single] to retrieve + * [kotlinx.coroutines.CoroutineScope], [kotlinx.serialization.json.Json] or [dev.inmo.micro_utils.startup.launcher.Config] + */ + fun Module.setupDI(config: JsonObject) {} + + /** + * This method will be called after all other [StartPlugin] will [setupDI] + * + * It is allowed to lock end of this method in case you require to prevent application to end its run (for example, + * you are starting some web server) + * + * @param koin Will contains everything you will register in [setupDI] (as well as other [StartPlugin]s) and + * [kotlinx.coroutines.CoroutineScope], [kotlinx.serialization.json.Json] and [dev.inmo.micro_utils.startup.launcher.Config] + * by their types + */ + suspend fun startPlugin(koin: Koin) {} +} diff --git a/startup/plugin/src/commonMain/kotlin/ServerPluginSerializer.kt b/startup/plugin/src/commonMain/kotlin/StartPluginSerializer.kt similarity index 57% rename from startup/plugin/src/commonMain/kotlin/ServerPluginSerializer.kt rename to startup/plugin/src/commonMain/kotlin/StartPluginSerializer.kt index 72cc877b175..15e4a6be604 100644 --- a/startup/plugin/src/commonMain/kotlin/ServerPluginSerializer.kt +++ b/startup/plugin/src/commonMain/kotlin/StartPluginSerializer.kt @@ -2,4 +2,4 @@ package dev.inmo.micro_utils.startup.plugin import kotlinx.serialization.KSerializer -expect object ServerPluginSerializer : KSerializer +expect object StartPluginSerializer : KSerializer diff --git a/startup/plugin/src/jvmMain/kotlin/ServerPluginSerializer.kt b/startup/plugin/src/jvmMain/kotlin/StartPluginSerializer.kt similarity index 70% rename from startup/plugin/src/jvmMain/kotlin/ServerPluginSerializer.kt rename to startup/plugin/src/jvmMain/kotlin/StartPluginSerializer.kt index 3875b65ac53..0e650c70df5 100644 --- a/startup/plugin/src/jvmMain/kotlin/ServerPluginSerializer.kt +++ b/startup/plugin/src/jvmMain/kotlin/StartPluginSerializer.kt @@ -6,16 +6,16 @@ import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder -actual object ServerPluginSerializer : KSerializer { +actual object StartPluginSerializer : KSerializer { override val descriptor: SerialDescriptor get() = String.serializer().descriptor - override fun deserialize(decoder: Decoder): ServerPlugin { + override fun deserialize(decoder: Decoder): StartPlugin { val kclass = Class.forName(decoder.decodeString()).kotlin - return (kclass.objectInstance ?: kclass.constructors.first { it.parameters.isEmpty() }.call()) as ServerPlugin + return (kclass.objectInstance ?: kclass.constructors.first { it.parameters.isEmpty() }.call()) as StartPlugin } - override fun serialize(encoder: Encoder, value: ServerPlugin) { + override fun serialize(encoder: Encoder, value: StartPlugin) { encoder.encodeString( value::class.java.canonicalName ) From 4971326eca71983a36f88802097be8ca33af8d9a Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Tue, 6 Dec 2022 13:08:37 +0600 Subject: [PATCH 09/11] add kdocs for the config of startup --- startup/launcher/src/commonMain/kotlin/Config.kt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/startup/launcher/src/commonMain/kotlin/Config.kt b/startup/launcher/src/commonMain/kotlin/Config.kt index 3609170ad13..55e36c68857 100644 --- a/startup/launcher/src/commonMain/kotlin/Config.kt +++ b/startup/launcher/src/commonMain/kotlin/Config.kt @@ -3,6 +3,19 @@ package dev.inmo.micro_utils.startup.launcher import dev.inmo.micro_utils.startup.plugin.StartPlugin import kotlinx.serialization.Serializable +/** + * Contains just [List] of [StartPlugin]s. In json this config should look like: + * + * ```json + * { + * "plugins": [ + * "dev.inmo.micro_utils.startup.launcher.HelloWorldPlugin" + * ] + * } + * ``` + * + * In the sample above [HelloWorldPlugin] will be loaded during startup of application + */ @Serializable data class Config( val plugins: List From b9d8528599554f4df5db7114b1a324fae31472d8 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Tue, 6 Dec 2022 13:14:05 +0600 Subject: [PATCH 10/11] complete kdocs for the startup module --- startup/launcher/src/jvmMain/kotlin/Main.kt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/startup/launcher/src/jvmMain/kotlin/Main.kt b/startup/launcher/src/jvmMain/kotlin/Main.kt index d4f1789f2b4..23bb111cefa 100644 --- a/startup/launcher/src/jvmMain/kotlin/Main.kt +++ b/startup/launcher/src/jvmMain/kotlin/Main.kt @@ -5,6 +5,25 @@ import dev.inmo.kslog.common.i import kotlinx.serialization.json.jsonObject import java.io.File +/** + * It is expected, that [args] will contain ONE argument with path to the config json. Sample of launching: + * + * ```bash + * ./gradlew run --args="sample.config.json" + * ``` + * + * Content of `sample.config.json` described in [Config] KDocs. + * + * You may build runnable app using: + * + * ```bash + * ./gradlew assembleDist + * ``` + * + * In that case in `build/distributions` folder you will be able to find zip and tar files with all required + * tools for application running (via their `bin/app_name` binary). In that case yoy will not need to pass + * `--args=...` and launch will look like `./bin/app_name sample.config.json` + */ suspend fun main(args: Array) { KSLog.default = KSLog("ServerLauncher") From acbb8a0c071ba82d86f1515d3b82fab8d8c14440 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Tue, 6 Dec 2022 13:15:08 +0600 Subject: [PATCH 11/11] complete prepare startup module --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d44d2416eff..1f98baaf72b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ ## 0.15.1 +* `Startup`: + * Inited :) + * `Plugin`: + * Inited :) + * `Launcher`: + * Inited :) + ## 0.15.0 * `Repos`: