Skip to content

Commit

Permalink
Implemented new MongoDB handler
Browse files Browse the repository at this point in the history
  • Loading branch information
WillFP committed Aug 25, 2024
1 parent 84d481d commit 3d78bad
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 69 deletions.
3 changes: 0 additions & 3 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ plugins {
id("maven-publish")
id("java")
kotlin("jvm") version "1.9.21"
kotlin("plugin.serialization") version "1.9.21"
}

dependencies {
Expand All @@ -41,7 +40,6 @@ allprojects {
apply(plugin = "maven-publish")
apply(plugin = "io.github.goooler.shadow")
apply(plugin = "kotlin")
apply(plugin = "org.jetbrains.kotlin.plugin.serialization")

repositories {
mavenCentral()
Expand Down Expand Up @@ -212,7 +210,6 @@ tasks {
//relocate("com.mysql", "com.willfp.eco.libs.mysql")
relocate("com.mongodb", "com.willfp.eco.libs.mongodb")
relocate("org.bson", "com.willfp.eco.libs.bson")
relocate("org.litote", "com.willfp.eco.libs.litote")
relocate("org.reactivestreams", "com.willfp.eco.libs.reactivestreams")
relocate("reactor.", "com.willfp.eco.libs.reactor.") // Dot in name to be safe
relocate("com.moandjiezana.toml", "com.willfp.eco.libs.toml")
Expand Down
15 changes: 6 additions & 9 deletions eco-core/core-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,14 @@ dependencies {

// Libraries
implementation("com.github.WillFP:Crunch:1.1.3")
implementation("mysql:mysql-connector-java:8.0.25")
implementation("org.jetbrains.exposed:exposed-core:0.37.3")
implementation("org.jetbrains.exposed:exposed-dao:0.37.3")
implementation("org.jetbrains.exposed:exposed-jdbc:0.37.3")
implementation("com.zaxxer:HikariCP:5.0.0")
implementation("mysql:mysql-connector-java:8.0.28")
implementation("org.jetbrains.exposed:exposed-core:0.53.0")
implementation("org.jetbrains.exposed:exposed-jdbc:0.53.0")
implementation("com.zaxxer:HikariCP:5.1.0")
implementation("net.kyori:adventure-platform-bukkit:4.1.0")
implementation("org.javassist:javassist:3.29.2-GA")
implementation("org.mongodb:mongodb-driver-kotlin-coroutine:5.0.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.5.1")
implementation("org.mongodb:bson-kotlinx:5.0.0")
implementation("org.mongodb:mongodb-driver-kotlin-coroutine:5.1.2")
implementation("org.mongodb:bson-kotlinx:5.1.2")
implementation("com.moandjiezana.toml:toml4j:0.7.2") {
exclude(group = "com.google.code.gson", module = "gson")
}
Expand Down Expand Up @@ -76,7 +74,6 @@ dependencies {
tasks {
shadowJar {
minimize {
exclude(dependency("org.litote.kmongo:kmongo-coroutine:.*"))
exclude(dependency("org.jetbrains.exposed:.*:.*"))
exclude(dependency("com.willfp:ModelEngineBridge:.*"))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource
import org.jetbrains.exposed.dao.id.UUIDTable
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.Op
import org.jetbrains.exposed.sql.SchemaUtils
import org.jetbrains.exposed.sql.SqlExpressionBuilder
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction
Expand All @@ -37,7 +40,7 @@ class LegacyMySQLPersistentDataHandler(
private val database = Database.connect(dataSource)

private val table = object : UUIDTable("eco_data") {
val data = text("json_data")
val data = text("json_data", eagerLoading = true)
}

init {
Expand Down Expand Up @@ -65,7 +68,8 @@ class LegacyMySQLPersistentDataHandler(
private inner class LegacySerializer<T : Any> : DataTypeSerializer<T>() {
override fun readAsync(uuid: UUID, key: PersistentDataKey<T>): T? {
val json = transaction(database) {
table.select { table.id eq uuid }
table.selectAll()
.where { table.id eq uuid }
.limit(1)
.singleOrNull()
?.get(table.data)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.willfp.eco.internal.spigot.data.handlers.impl

import com.mongodb.MongoClientSettings
import com.mongodb.client.model.Filters
import com.mongodb.client.model.ReplaceOptions
import com.mongodb.kotlin.client.coroutine.MongoClient
Expand All @@ -9,95 +10,184 @@ import com.willfp.eco.core.data.handlers.DataTypeSerializer
import com.willfp.eco.core.data.handlers.PersistentDataHandler
import com.willfp.eco.core.data.keys.PersistentDataKey
import com.willfp.eco.core.data.keys.PersistentDataKeyType
import com.willfp.eco.internal.spigot.EcoSpigotPlugin
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.Contextual
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import org.bson.BsonArray
import org.bson.BsonBoolean
import org.bson.BsonDecimal128
import org.bson.BsonDocument
import org.bson.BsonDouble
import org.bson.BsonInt32
import org.bson.BsonObjectId
import org.bson.BsonString
import org.bson.BsonValue
import org.bson.codecs.configuration.CodecRegistries
import org.bson.codecs.pojo.PojoCodecProvider
import org.bson.types.Decimal128
import java.math.BigDecimal
import java.util.UUID

class MongoPersistentDataHandler(
config: Config
) : PersistentDataHandler("mongo") {
private val codecRegistry = CodecRegistries.fromRegistries(
MongoClientSettings.getDefaultCodecRegistry(),
CodecRegistries.fromProviders(PojoCodecProvider.builder().automatic(true).build())
)

private val client = MongoClient.create(config.getString("url"))
private val database = client.getDatabase(config.getString("database"))

// Collection name is set for backwards compatibility
private val collection = database.getCollection<UUIDProfile>("uuidprofile")
private val collection = database.getCollection<BsonDocument>(config.getString("collection"))
.withCodecRegistry(codecRegistry)

init {
PersistentDataKeyType.STRING.registerSerializer(this, MongoSerializer<String>())
PersistentDataKeyType.BOOLEAN.registerSerializer(this, MongoSerializer<Boolean>())
PersistentDataKeyType.INT.registerSerializer(this, MongoSerializer<Int>())
PersistentDataKeyType.DOUBLE.registerSerializer(this, MongoSerializer<Double>())
PersistentDataKeyType.STRING_LIST.registerSerializer(this, MongoSerializer<List<String>>())
PersistentDataKeyType.BIG_DECIMAL.registerSerializer(this, MongoSerializer<BigDecimal>())
PersistentDataKeyType.STRING.registerSerializer(this, object : MongoSerializer<String>() {
override fun serialize(value: String): BsonValue {
return BsonString(value)
}

override fun deserialize(value: BsonValue): String {
return value.asString().value
}
})

PersistentDataKeyType.BOOLEAN.registerSerializer(this, object : MongoSerializer<Boolean>() {
override fun serialize(value: Boolean): BsonValue {
return BsonBoolean(value)
}

override fun deserialize(value: BsonValue): Boolean {
return value.asBoolean().value
}
})

PersistentDataKeyType.INT.registerSerializer(this, object : MongoSerializer<Int>() {
override fun serialize(value: Int): BsonValue {
return BsonInt32(value)
}

override fun deserialize(value: BsonValue): Int {
return value.asInt32().value
}
})

PersistentDataKeyType.DOUBLE.registerSerializer(this, object : MongoSerializer<Double>() {
override fun serialize(value: Double): BsonValue {
return BsonDouble(value)
}

override fun deserialize(value: BsonValue): Double {
return value.asDouble().value
}
})

PersistentDataKeyType.STRING_LIST.registerSerializer(this, object : MongoSerializer<List<String>>() {
override fun serialize(value: List<String>): BsonValue {
return BsonArray(value.map { BsonString(it) })
}

override fun deserialize(value: BsonValue): List<String> {
return value.asArray().values.map { it.asString().value }
}
})

PersistentDataKeyType.BIG_DECIMAL.registerSerializer(this, object : MongoSerializer<BigDecimal>() {
override fun serialize(value: BigDecimal): BsonValue {
return BsonDecimal128(Decimal128(value))
}

override fun deserialize(value: BsonValue): BigDecimal {
return value.asDecimal128().value.bigDecimalValue()
}
})

PersistentDataKeyType.CONFIG.registerSerializer(this, object : MongoSerializer<Config>() {
override fun convertToMongo(value: Config): Any {
return value.toMap()
private fun deserializeConfigValue(value: BsonValue): Any {
return when (value) {
is BsonString -> value.value
is BsonInt32 -> value.value
is BsonDouble -> value.value
is BsonBoolean -> value.value
is BsonDecimal128 -> value.value.bigDecimalValue()
is BsonArray -> value.values.map { deserializeConfigValue(it) }
is BsonDocument -> value.mapValues { (_, v) -> deserializeConfigValue(v) }

else -> throw IllegalArgumentException("Could not deserialize config value type ${value::class.simpleName}")
}
}

private fun serializeConfigValue(value: Any): BsonValue {
return when (value) {
is String -> BsonString(value)
is Int -> BsonInt32(value)
is Double -> BsonDouble(value)
is Boolean -> BsonBoolean(value)
is BigDecimal -> BsonDecimal128(Decimal128(value))
is List<*> -> BsonArray(value.map { serializeConfigValue(it!!) })
is Map<*, *> -> BsonDocument().apply {
value.forEach { (k, v) -> append(k.toString(), serializeConfigValue(v!!)) }
}

else -> throw IllegalArgumentException("Could not serialize config value type ${value::class.simpleName}")
}
}

@Suppress("UNCHECKED_CAST")
override fun convertFromMongo(value: Any): Config {
return Configs.fromMap(value as Map<String, Any>)
override fun serialize(value: Config): BsonValue {
return serializeConfigValue(value.toMap())
}

override fun deserialize(value: BsonValue): Config {
@Suppress("UNCHECKED_CAST")
return Configs.fromMap(deserializeConfigValue(value.asDocument()) as Map<String, Any>)
}
})
}

override fun getSavedUUIDs(): Set<UUID> {
return runBlocking {
collection.find().toList().map { UUID.fromString(it.uuid) }.toSet()
collection.find().toList().map {
UUID.fromString(it.getString("uuid").value)
}.toSet()
}
}

private open inner class MongoSerializer<T : Any> : DataTypeSerializer<T>() {
private abstract inner class MongoSerializer<T : Any> : DataTypeSerializer<T>() {
override fun readAsync(uuid: UUID, key: PersistentDataKey<T>): T? {
return runBlocking {
val profile = collection.find(Filters.eq("uuid", uuid.toString())).firstOrNull()
?: return@runBlocking null
val filter = Filters.eq("uuid", uuid.toString())

val value = profile.data[key.key.toString()]
?: return@runBlocking null
val profile = collection.find(filter)
.firstOrNull() ?: return@runBlocking null

convertFromMongo(value)
}
}
val value = profile[key.key.toString()] ?: return@runBlocking null

protected open fun convertToMongo(value: T): Any {
return value
deserialize(value)
}
}

override fun writeAsync(uuid: UUID, key: PersistentDataKey<T>, value: T) {
runBlocking {
val profile = collection.find(Filters.eq("uuid", uuid.toString())).firstOrNull()
?: UUIDProfile(uuid.toString(), mutableMapOf())
val filter = Filters.eq("uuid", uuid.toString())

profile.data[key.key.toString()] = convertToMongo(value)
val profile = collection.find(filter).firstOrNull()
?: BsonDocument()
.append("_id", BsonObjectId())
.append("uuid", BsonString(uuid.toString()))

profile.append(key.key.toString(), serialize(value))

collection.replaceOne(
Filters.eq("uuid", uuid.toString()),
filter,
profile,
ReplaceOptions().upsert(true)
)
}
}

protected open fun convertFromMongo(value: Any): T {
@Suppress("UNCHECKED_CAST")
return value as T
}
protected abstract fun serialize(value: T): BsonValue
protected abstract fun deserialize(value: BsonValue): T
}

@Serializable
private data class UUIDProfile(
// Storing UUID as strings for serialization
@SerialName("_id") val uuid: String,

// Storing NamespacedKeys as strings for serialization
val data: MutableMap<String, @Contextual Any>
)
}
Loading

0 comments on commit 3d78bad

Please sign in to comment.