From 37329b19fef958e09eff74f5f9d4213ba8414331 Mon Sep 17 00:00:00 2001 From: tiagohm Date: Thu, 21 Nov 2024 23:58:42 -0300 Subject: [PATCH] [api]: Drop Preference table; Fallback on failed to download IERS --- api/src/main/kotlin/nebulosa/api/Nebulosa.kt | 46 +++++++----- .../nebulosa/api/atlas/IERSUpdateTask.kt | 53 +++++++++----- .../nebulosa/api/atlas/LibWCSDownloadTask.kt | 7 +- .../nebulosa/api/atlas/SatelliteUpdateTask.kt | 9 +-- .../nebulosa/api/atlas/SkyAtlasUpdateTask.kt | 8 ++- .../nebulosa/api/guiding/GuidingService.kt | 7 +- .../main/kotlin/nebulosa/api/inject/Inject.kt | 49 +++++-------- .../api/preference/PreferenceEntity.kt | 23 ------ .../api/preference/PreferenceRepository.kt | 53 -------------- .../api/preference/PreferenceService.kt | 71 ++++++++----------- .../api/preference/PreferenceTable.kt | 10 --- api/src/test/kotlin/PreferenceServiceTest.kt | 2 - 12 files changed, 134 insertions(+), 204 deletions(-) delete mode 100644 api/src/main/kotlin/nebulosa/api/preference/PreferenceEntity.kt delete mode 100644 api/src/main/kotlin/nebulosa/api/preference/PreferenceRepository.kt delete mode 100644 api/src/main/kotlin/nebulosa/api/preference/PreferenceTable.kt diff --git a/api/src/main/kotlin/nebulosa/api/Nebulosa.kt b/api/src/main/kotlin/nebulosa/api/Nebulosa.kt index d6eca7fdb..4d7c81185 100644 --- a/api/src/main/kotlin/nebulosa/api/Nebulosa.kt +++ b/api/src/main/kotlin/nebulosa/api/Nebulosa.kt @@ -16,8 +16,14 @@ import nebulosa.api.core.FileLocker import nebulosa.api.database.migration.MainDatabaseMigrator import nebulosa.api.database.migration.SkyDatabaseMigrator import nebulosa.api.inject.controllersModule -import nebulosa.api.inject.koinApp +import nebulosa.api.inject.coreModule +import nebulosa.api.inject.databaseModule +import nebulosa.api.inject.eventBusModule +import nebulosa.api.inject.httpModule import nebulosa.api.inject.objectMapperModule +import nebulosa.api.inject.pathModule +import nebulosa.api.inject.phd2Module +import nebulosa.api.inject.repositoriesModule import nebulosa.api.inject.serverModule import nebulosa.api.inject.servicesModule import nebulosa.api.ktor.configureHTTP @@ -25,6 +31,7 @@ import nebulosa.api.ktor.configureMonitoring import nebulosa.api.ktor.configureRouting import nebulosa.api.ktor.configureSerialization import nebulosa.api.ktor.configureSockets +import nebulosa.api.preference.PreferenceService import nebulosa.json.PathModule import nebulosa.log.d import nebulosa.log.loggerFor @@ -38,32 +45,32 @@ import java.util.concurrent.ExecutorService import kotlin.io.path.Path import kotlin.io.path.exists import kotlin.io.path.fileSize -import kotlin.io.path.inputStream import kotlin.io.path.isRegularFile import kotlin.system.exitProcess @Command(name = "nebulosa") class Nebulosa : Runnable { - private val properties = Properties(3) + private val preferencesPath = Path(System.getProperty(APP_DIR_KEY), PreferenceService.FILENAME) + private val preferences = PreferenceService() init { - Path(System.getProperty(APP_DIR_KEY), PROPERTIES_FILENAME) + preferencesPath .takeIf { it.exists() && it.isRegularFile() } - ?.also { it.inputStream().use(properties::load) } + ?.also(preferences::load) } @Option(name = ["-h", "--host"]) - private var host = properties.getProperty("host")?.ifBlank { null } ?: DEFAULT_HOST + private var host = preferences["host"]?.ifBlank { null } ?: DEFAULT_HOST @Option(name = ["-p", "--port"]) - private var port = properties.getProperty("port")?.ifBlank { null }?.toIntOrNull() ?: DEFAULT_PORT + private var port = preferences["port"]?.toIntOrNull() ?: DEFAULT_PORT @Option(name = ["-d", "--debug"]) - private var debug = properties.getProperty("debug")?.toBoolean() == true + private var debug = preferences["debug"]?.toBoolean() == true @Option(name = ["-t", "--trace"]) - private var trace = properties.getProperty("trace")?.toBoolean() == true + private var trace = preferences["trace"]?.toBoolean() == true @Option(name = ["-f", "--files"]) private val files = mutableListOf() @@ -106,13 +113,19 @@ class Nebulosa : Runnable { configureMonitoring(debug) }.start(false) - // app.exception(Exception::class.java, ::handleException) - - koinApp.modules(serverModule(server)) - koinApp.modules(objectMapperModule(OBJECT_MAPPER)) - koinApp.modules(servicesModule()) - koinApp.modules(controllersModule()) - startKoin(koinApp) + val koinApp = startKoin { + modules(pathModule()) + modules(coreModule()) + modules(httpModule()) + modules(databaseModule()) + modules(eventBusModule()) + modules(repositoriesModule()) + modules(phd2Module()) + modules(serverModule(server)) + modules(objectMapperModule(OBJECT_MAPPER)) + modules(servicesModule(preferences)) + modules(controllersModule()) + } with(runBlocking { server.engine.resolvedConnectors().first().port }) { println("server is started at port: $this") @@ -144,7 +157,6 @@ class Nebulosa : Runnable { internal val LOG = loggerFor() - const val PROPERTIES_FILENAME = "nebulosa.properties" const val DEFAULT_HOST = "0.0.0.0" const val DEFAULT_PORT = 0 diff --git a/api/src/main/kotlin/nebulosa/api/atlas/IERSUpdateTask.kt b/api/src/main/kotlin/nebulosa/api/atlas/IERSUpdateTask.kt index 123b44d18..5023afdb7 100644 --- a/api/src/main/kotlin/nebulosa/api/atlas/IERSUpdateTask.kt +++ b/api/src/main/kotlin/nebulosa/api/atlas/IERSUpdateTask.kt @@ -17,6 +17,7 @@ import org.koin.core.component.get import java.nio.file.Path import java.util.concurrent.ScheduledExecutorService import java.util.concurrent.TimeUnit +import kotlin.io.path.exists import kotlin.io.path.inputStream import kotlin.io.path.outputStream @@ -34,53 +35,71 @@ class IERSUpdateTask( override fun run() { get().await() - val iersa = IERSA() - val iersb = IERSB() + var iersa: IERSA? = null + var iersb: IERSB? = null with(Path.of("$dataPath", "finals2000A.all")) { - download(IERSA.URL, IERSA_UPDATED_AT_KEY) - inputStream().use(iersa::load) + if (download(IERSA.URL, IERSA_UPDATED_AT_KEY)) { + iersa = IERSA() + inputStream().use(iersa::load) + } } with(Path.of("$dataPath", "eopc04.1962-now.txt")) { - download(IERSB.URL, IERSB_UPDATED_AT_KEY) - inputStream().use(iersb::load) + if (download(IERSB.URL, IERSB_UPDATED_AT_KEY)) { + iersb = IERSB() + inputStream().use(iersb::load) + } } - IERS.attach(IERSAB(iersa, iersb)) + if (iersa != null && iersb != null) IERS.attach(IERSAB(iersa, iersb)) + else if (iersa != null) IERS.attach(iersa) + else if (iersb != null) IERS.attach(iersb) } - private fun Path.download(url: String, key: String) { + private fun Path.download(url: String, key: String): Boolean { try { var request = Request.Builder().head().url(url).build() var modifiedAt = httpClient.newCall(request).execute() .use { it.headers.getDate(HttpHeaders.LastModified) } + ?.toInstant()?.toEpochMilli() - if (modifiedAt != null && "$modifiedAt" == preferenceService.getText(key)) { + if (exists() && modifiedAt != null && modifiedAt == preferenceService[key]?.toLongOrNull()) { LOG.info("{} is up to date. modifiedAt={}", url, modifiedAt) - return + return true } request = request.newBuilder().get().build() - LOG.d { debug("downloading {}", url) } + LOG.d { debug("{} is out of date. modifiedAt={}", url, modifiedAt) } httpClient.newCall(request).execute().use { - it.body!!.byteStream().transferAndClose(outputStream()) - modifiedAt = it.headers.getDate(HttpHeaders.LastModified) - preferenceService.putText(key, "$modifiedAt") - LOG.d { debug("{} downloaded. modifiedAt={}", url, modifiedAt) } + if (it.isSuccessful) { + it.body!!.byteStream().transferAndClose(outputStream()) + modifiedAt = it.headers.getDate(HttpHeaders.LastModified)?.toInstant()?.toEpochMilli() + + if (modifiedAt != null) { + preferenceService[key] = modifiedAt + preferenceService.save() + } + + LOG.d { debug("{} downloaded. modifiedAt={}", url, modifiedAt) } + + return true + } } } catch (e: Throwable) { LOG.error("failed to download finals2000A.all", e) } + + return false } companion object { - const val IERSA_UPDATED_AT_KEY = "IERSA.UPDATED_AT" - const val IERSB_UPDATED_AT_KEY = "IERSB.UPDATED_AT" + const val IERSA_UPDATED_AT_KEY = "iersa.updatedAt" + const val IERSB_UPDATED_AT_KEY = "iersb.updatedAt" private val LOG = loggerFor() } diff --git a/api/src/main/kotlin/nebulosa/api/atlas/LibWCSDownloadTask.kt b/api/src/main/kotlin/nebulosa/api/atlas/LibWCSDownloadTask.kt index 89fb0f0be..0fc5af466 100644 --- a/api/src/main/kotlin/nebulosa/api/atlas/LibWCSDownloadTask.kt +++ b/api/src/main/kotlin/nebulosa/api/atlas/LibWCSDownloadTask.kt @@ -47,7 +47,7 @@ class LibWCSDownloadTask( if (response.isSuccessful) { val newestVersion = response.body!!.string() - if (newestVersion != preferenceService.getText(VERSION_KEY) || !libraryPath.exists()) { + if (newestVersion != preferenceService[VERSION_KEY] || !libraryPath.exists()) { LOG.info("libwcs is out of date. Downloading...") request = Request.Builder().get().url(libraryUrl).build() @@ -55,7 +55,8 @@ class LibWCSDownloadTask( httpClient.newCall(request).execute().use { if (it.isSuccessful) { it.body!!.byteStream().transferAndCloseOutput(libraryPath.outputStream()) - preferenceService.putText(VERSION_KEY, newestVersion) + preferenceService[VERSION_KEY] = newestVersion + preferenceService.save() } } } else { @@ -77,7 +78,7 @@ class LibWCSDownloadTask( companion object { const val VERSION_URL = "https://raw.githubusercontent.com/tiagohm/nebulosa.data/main/libs/wcs/VERSION.txt" - const val VERSION_KEY = "LIBWCS.VERSION" + const val VERSION_KEY = "libwcs.version" const val LINUX_X86_64 = "linux-x86-64" const val LINUX_AARCH_64 = "linux-aarch64" diff --git a/api/src/main/kotlin/nebulosa/api/atlas/SatelliteUpdateTask.kt b/api/src/main/kotlin/nebulosa/api/atlas/SatelliteUpdateTask.kt index 38f60825a..991ee6a82 100644 --- a/api/src/main/kotlin/nebulosa/api/atlas/SatelliteUpdateTask.kt +++ b/api/src/main/kotlin/nebulosa/api/atlas/SatelliteUpdateTask.kt @@ -35,9 +35,9 @@ class SatelliteUpdateTask( private fun isOutOfDate(): Boolean { try { - val updatedAt = preferenceService.getLong(UPDATED_AT_KEY) ?: 0L + val updatedAt = preferenceService[UPDATED_AT_KEY]?.toLongOrNull() ?: 0L return System.currentTimeMillis() - updatedAt >= UPDATE_INTERVAL - } catch (e: JsonMappingException) { + } catch (_: JsonMappingException) { return true } } @@ -47,7 +47,8 @@ class SatelliteUpdateTask( LOG.info("satellites is out of date") if (updateTLEs()) { - preferenceService.putLong(UPDATED_AT_KEY, System.currentTimeMillis()) + preferenceService[UPDATED_AT_KEY] = System.currentTimeMillis() + preferenceService.save() } else { LOG.warn("no satellites was updated") } @@ -122,7 +123,7 @@ class SatelliteUpdateTask( companion object { const val UPDATE_INTERVAL = 1000L * 60 * 60 * 24 * 2 // 2 days in ms - const val UPDATED_AT_KEY = "SATELLITES.UPDATED_AT" + const val UPDATED_AT_KEY = "satellites.updatedAt" private val LOG = loggerFor() } diff --git a/api/src/main/kotlin/nebulosa/api/atlas/SkyAtlasUpdateTask.kt b/api/src/main/kotlin/nebulosa/api/atlas/SkyAtlasUpdateTask.kt index 3ffbcad46..2e6e302ea 100644 --- a/api/src/main/kotlin/nebulosa/api/atlas/SkyAtlasUpdateTask.kt +++ b/api/src/main/kotlin/nebulosa/api/atlas/SkyAtlasUpdateTask.kt @@ -34,7 +34,7 @@ class SkyAtlasUpdateTask( httpClient.newCall(request).execute().use { response -> if (response.isSuccessful) { val newestVersion = response.body!!.string().trim() - val currentVersion = preferenceService.getText(VERSION_KEY) + val currentVersion = preferenceService[VERSION_KEY] if (newestVersion != currentVersion || skyObjectEntityRepository.size == 0L) { skyObjectEntityRepository.clear() @@ -71,7 +71,9 @@ class SkyAtlasUpdateTask( } } - preferenceService.putText(VERSION_KEY, newestVersion) + preferenceService[VERSION_KEY] = newestVersion + preferenceService.save() + messageService.sendMessage(SkyAtlasUpdateNotificationEvent.Finished(newestVersion)) LOG.info("Sky Atlas database was updated. version={}, size={}", newestVersion, skyObjectEntityRepository.size) @@ -85,7 +87,7 @@ class SkyAtlasUpdateTask( companion object { const val VERSION_URL = "https://raw.githubusercontent.com/tiagohm/nebulosa.data/main/simbad/VERSION.txt" - const val VERSION_KEY = "SKY_ATLAS.VERSION" + const val VERSION_KEY = "skyAtlas.version" const val DATA_URL = "https://raw.githubusercontent.com/tiagohm/nebulosa.data/main/simbad/simbad.%02d.dat" const val MAX_DATA_COUNT = 100 diff --git a/api/src/main/kotlin/nebulosa/api/guiding/GuidingService.kt b/api/src/main/kotlin/nebulosa/api/guiding/GuidingService.kt index 80809a987..e5c1aae66 100644 --- a/api/src/main/kotlin/nebulosa/api/guiding/GuidingService.kt +++ b/api/src/main/kotlin/nebulosa/api/guiding/GuidingService.kt @@ -1,5 +1,6 @@ package nebulosa.api.guiding +import com.fasterxml.jackson.databind.ObjectMapper import nebulosa.api.message.MessageService import nebulosa.api.preference.PreferenceService import nebulosa.guiding.GuideStar @@ -16,6 +17,7 @@ class GuidingService( private val messageService: MessageService, private val phd2Client: PHD2Client, private val guider: Guider, + private val mapper: ObjectMapper, ) : GuiderListener { private val guideHistory = GuideStepHistory() @@ -35,7 +37,7 @@ class GuidingService( phd2Client.open(host, port) guider.registerGuiderListener(this) - settle(preferenceService.getJSON("GUIDER.SETTLE_INFO") ?: SettleInfo.EMPTY) + settle(preferenceService["GUIDER.SETTLE_INFO"]?.ifBlank { null }?.let { mapper.readValue(it, SettleInfo::class.java) } ?: SettleInfo.EMPTY) messageService.sendMessage(GuiderMessageEvent(GUIDER_CONNECTED)) } @@ -81,7 +83,8 @@ class GuidingService( guider.settleTime = Duration.ofSeconds(settle.time) guider.settleTimeout = Duration.ofSeconds(settle.timeout) - preferenceService.putJSON("GUIDER.SETTLE_INFO", settle) + preferenceService["GUIDER.SETTLE_INFO"] = mapper.writeValueAsString(settle) + preferenceService.save() } fun dither(amount: Double, raOnly: Boolean = false) { diff --git a/api/src/main/kotlin/nebulosa/api/inject/Inject.kt b/api/src/main/kotlin/nebulosa/api/inject/Inject.kt index bb7974430..d0f720782 100644 --- a/api/src/main/kotlin/nebulosa/api/inject/Inject.kt +++ b/api/src/main/kotlin/nebulosa/api/inject/Inject.kt @@ -68,7 +68,6 @@ import nebulosa.api.mounts.MountEventHub import nebulosa.api.mounts.MountService import nebulosa.api.platesolver.PlateSolverController import nebulosa.api.platesolver.PlateSolverService -import nebulosa.api.preference.PreferenceRepository import nebulosa.api.preference.PreferenceService import nebulosa.api.rotators.RotatorController import nebulosa.api.rotators.RotatorEventHub @@ -101,7 +100,6 @@ import okhttp3.logging.HttpLoggingInterceptor import org.greenrobot.eventbus.EventBus import org.jetbrains.exposed.sql.Database import org.koin.core.qualifier.named -import org.koin.dsl.koinApplication import org.koin.dsl.module import java.nio.file.Path import java.time.LocalDate @@ -118,32 +116,23 @@ import kotlin.io.path.deleteIfExists import kotlin.io.path.exists import kotlin.io.path.listDirectoryEntries -val koinApp = koinApplication { - modules(pathModule()) - modules(coreModule()) - modules(httpModule()) - modules(databaseModule()) - modules(eventBusModule()) - modules(repositoriesModule()) - modules(phd2Module()) -} - object Named { - val appDir = named("appDir") - val logsDir = named("logsDir") - val dataDir = named("dataDir") - val capturesDir = named("capturesDir") - val sequencesDir = named("sequencesDir") - val cacheDir = named("cacheDir") - val libsDir = named("libsDir") - val liveStackingDir = named("liveStackingDir") - val defaultHttpClient = named("defaultHttpClient") - val alpacaHttpClient = named("alpacaHttpClient") - val mainConnection = named("mainConnection") - val mainDatasourceUrl = named("mainDatasource") - val skyConnection = named("skyConnection") - val skyDatasourceUrl = named("skyDatasource") + @JvmField val appDir = named("appDir") + @JvmField val logsDir = named("logsDir") + @JvmField val dataDir = named("dataDir") + @JvmField val capturesDir = named("capturesDir") + @JvmField val sequencesDir = named("sequencesDir") + @JvmField val cacheDir = named("cacheDir") + @JvmField val libsDir = named("libsDir") + @JvmField val liveStackingDir = named("liveStackingDir") + @JvmField val preferencesPath = named("preferencesPath") + @JvmField val defaultHttpClient = named("defaultHttpClient") + @JvmField val alpacaHttpClient = named("alpacaHttpClient") + @JvmField val mainConnection = named("mainConnection") + @JvmField val mainDatasourceUrl = named("mainDatasource") + @JvmField val skyConnection = named("skyConnection") + @JvmField val skyDatasourceUrl = named("skyDatasource") } // PATH @@ -175,6 +164,7 @@ fun pathModule(root: Path = Path(requireNotNull(System.getProperty(APP_DIR_KEY)) single(Named.cacheDir) { Path("$root", "cache").createDirectories() } single(Named.libsDir) { Path("$root", "libs").createDirectories() } single(Named.liveStackingDir) { Path("$root", "live-stacking").createDirectories() } + single(Named.preferencesPath) { Path("$root", PreferenceService.FILENAME) } } // CORE @@ -255,7 +245,6 @@ fun phd2Module() = module { fun repositoriesModule() = module { single { CalibrationFrameRepository(get(Named.mainConnection)) } - single { PreferenceRepository(get(Named.mainConnection)) } single { SatelliteRepository(get(Named.skyConnection)) } single { SkyObjectEntityRepository(get(Named.skyConnection)) } } @@ -282,7 +271,7 @@ fun tasksModule() = module(true) { single { LibWCSDownloadTask(get(Named.libsDir), get(Named.defaultHttpClient), get(), get()) } } -fun servicesModule() = module { +fun servicesModule(preferenceService: PreferenceService) = module { single { HorizonsService(httpClient = get(Named.defaultHttpClient)) } single { SimbadService(httpClient = get(Named.defaultHttpClient)) } single { SmallBodyDatabaseService(httpClient = get(Named.defaultHttpClient)) } @@ -313,8 +302,8 @@ fun servicesModule() = module { single { DARVExecutor(get(), get(), get()) } single { TPPAExecutor(get(), get(), get()) } single { PolarAlignmentService(get(), get()) } - single { PreferenceService(get(), get()) } - single { GuidingService(get(), get(), get(), get()) } + single { preferenceService } + single { GuidingService(get(), get(), get(), get(), get()) } single { SequencerExecutor(get(), get(), get(), get(), get()) } single { SequencerService(get(Named.sequencesDir), get()) } single { MoonPhaseFinder(get()) } diff --git a/api/src/main/kotlin/nebulosa/api/preference/PreferenceEntity.kt b/api/src/main/kotlin/nebulosa/api/preference/PreferenceEntity.kt deleted file mode 100644 index b8342799d..000000000 --- a/api/src/main/kotlin/nebulosa/api/preference/PreferenceEntity.kt +++ /dev/null @@ -1,23 +0,0 @@ -package nebulosa.api.preference - -import org.jetbrains.exposed.sql.ResultRow -import org.jetbrains.exposed.sql.statements.UpdateBuilder - -data class PreferenceEntity( - @JvmField var key: String = "", - @JvmField var value: String? = null, -) { - - fun mapTo(builder: UpdateBuilder, update: Boolean = false) { - if (!update) builder[PreferenceTable.key] = key - builder[PreferenceTable.value] = value - } - - companion object { - - fun from(row: ResultRow) = PreferenceEntity( - row[PreferenceTable.key], - row[PreferenceTable.value], - ) - } -} diff --git a/api/src/main/kotlin/nebulosa/api/preference/PreferenceRepository.kt b/api/src/main/kotlin/nebulosa/api/preference/PreferenceRepository.kt deleted file mode 100644 index a01c83d6c..000000000 --- a/api/src/main/kotlin/nebulosa/api/preference/PreferenceRepository.kt +++ /dev/null @@ -1,53 +0,0 @@ -package nebulosa.api.preference - -import org.jetbrains.exposed.sql.Count -import org.jetbrains.exposed.sql.Database -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.deleteAll -import org.jetbrains.exposed.sql.deleteWhere -import org.jetbrains.exposed.sql.insert -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction -import org.jetbrains.exposed.sql.update - -class PreferenceRepository(private val connection: Database) { - - operator fun contains(key: String) = transaction(connection) { - !PreferenceTable - .select(PreferenceTable.key) - .where { PreferenceTable.key eq key } - .empty() - } - - operator fun get(key: String) = transaction(connection) { - PreferenceTable - .selectAll() - .where { PreferenceTable.key eq key } - .firstOrNull() - ?.let(PreferenceEntity::from) - } - - private val count = Count(PreferenceTable.key) - - val size - get() = transaction(connection) { PreferenceTable.select(count).first()[count] } - - fun add(entity: PreferenceEntity) = transaction(connection) { - PreferenceTable.insert { entity.mapTo(it) } - entity - } - - fun update(entity: PreferenceEntity) = transaction(connection) { - PreferenceTable.update({ PreferenceTable.key eq entity.key }) { entity.mapTo(it, true) } - } - - fun delete(key: String) = transaction(connection) { - PreferenceTable - .deleteWhere { PreferenceTable.key eq key } == 1 - } - - fun clear() = transaction(connection) { - PreferenceTable - .deleteAll() > 0 - } -} diff --git a/api/src/main/kotlin/nebulosa/api/preference/PreferenceService.kt b/api/src/main/kotlin/nebulosa/api/preference/PreferenceService.kt index 116280175..92fc371a4 100644 --- a/api/src/main/kotlin/nebulosa/api/preference/PreferenceService.kt +++ b/api/src/main/kotlin/nebulosa/api/preference/PreferenceService.kt @@ -1,55 +1,46 @@ package nebulosa.api.preference -import com.fasterxml.jackson.databind.ObjectMapper +import nebulosa.api.inject.Named +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import java.nio.file.Path +import java.util.* +import kotlin.io.path.inputStream +import kotlin.io.path.outputStream -class PreferenceService( - private val preferenceRepository: PreferenceRepository, - private val objectMapper: ObjectMapper, -) { +data class PreferenceService(private val properties: Properties) : KoinComponent { - operator fun get(key: String) = preferenceRepository[key] + private val defaultSavePath by inject(Named.preferencesPath) - val size - get() = preferenceRepository.size + constructor(capacity: Int = 16) : this(Properties(capacity)) - fun put(entity: PreferenceEntity) { - if (entity.key in this) preferenceRepository.update(entity) - else preferenceRepository.add(entity) + operator fun get(key: String): String? { + return properties.getProperty(key) } - operator fun contains(key: String) = key in preferenceRepository - - fun getJSON(key: String, type: Class): T? = this[key]?.value?.let { objectMapper.readValue(it, type) } - - inline fun getJSON(key: String) = getJSON(key, T::class.java) - - fun getBoolean(key: String) = getJSON(key, Boolean::class.java) - - fun getText(key: String) = getJSON(key, String::class.java) - - inline fun > getEnum(key: String) = getText(key)?.takeIf { it.isNotBlank() }?.let { enumValueOf(it) } - - fun getInt(key: String) = getJSON(key, Int::class.java) - - fun getLong(key: String) = getJSON(key, Long::class.java) - - fun getDouble(key: String) = getJSON(key, Double::class.java) - - fun putJSON(key: String, value: Any?) = put(PreferenceEntity(key, if (value == null) null else objectMapper.writeValueAsString(value))) - - fun putBoolean(key: String, value: Boolean) = putJSON(key, value) - - fun putText(key: String, value: String?) = putJSON(key, value) + operator fun set(key: String, value: Any) { + properties.setProperty(key, "$value") + } - fun putEnum(key: String, value: Enum<*>) = putText(key, value.name) + operator fun contains(key: String): Boolean { + return properties.containsKey(key) + } - fun putInt(key: String, value: Int) = putJSON(key, value) + fun clear() { + properties.clear() + } - fun putLong(key: String, value: Long) = putJSON(key, value) + fun load(path: Path) { + path.inputStream().use(properties::load) + } - fun putDouble(key: String, value: Double) = putJSON(key, value) + @Synchronized + fun save(path: Path? = null) { + requireNotNull(path ?: defaultSavePath).outputStream().use { properties.store(it, "") } + } - fun clear() = preferenceRepository.clear() + companion object { - fun delete(key: String) = preferenceRepository.delete(key) + const val FILENAME = "nebulosa.properties" + } } diff --git a/api/src/main/kotlin/nebulosa/api/preference/PreferenceTable.kt b/api/src/main/kotlin/nebulosa/api/preference/PreferenceTable.kt deleted file mode 100644 index e0a6818cd..000000000 --- a/api/src/main/kotlin/nebulosa/api/preference/PreferenceTable.kt +++ /dev/null @@ -1,10 +0,0 @@ -package nebulosa.api.preference - -import org.jetbrains.exposed.sql.Table - -object PreferenceTable : Table("PREFERENCES") { - val key = text("KEY") - val value = text("VALUE").nullable() - - override val primaryKey = PrimaryKey(key) -} diff --git a/api/src/test/kotlin/PreferenceServiceTest.kt b/api/src/test/kotlin/PreferenceServiceTest.kt index a8a737a77..81c85c882 100644 --- a/api/src/test/kotlin/PreferenceServiceTest.kt +++ b/api/src/test/kotlin/PreferenceServiceTest.kt @@ -9,8 +9,6 @@ import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe import nebulosa.api.atlas.Location import nebulosa.api.database.migration.MainDatabaseMigrator -import nebulosa.api.preference.PreferenceRepository -import nebulosa.api.preference.PreferenceService import nebulosa.indi.device.camera.FrameType import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.transactions.TransactionManager