Serializer is now specified instead of unit function that manages kryo registrations

master
Robinson 2023-02-23 20:14:09 +01:00
parent 8cb3b5abec
commit 687889abce
No known key found for this signature in database
GPG Key ID: 8E7DB78588BD6F5C
3 changed files with 151 additions and 69 deletions

View File

@ -15,7 +15,9 @@
*/
package dorkbox.storage
import com.esotericsoftware.kryo.Kryo
import dorkbox.bytes.decodeBase58
import dorkbox.bytes.encodeToBase58String
import dorkbox.storage.serializer.SerializerBytes
import dorkbox.storage.types.MemoryStore
import dorkbox.storage.types.PropertyStore
import mu.KLogger
@ -24,7 +26,7 @@ import org.slf4j.helpers.NOPLogger
import java.io.File
typealias AccessFunc = ((key: Any, value: Any, load: (key: Any, value: Any) -> Unit) -> Unit)
typealias AccessFunc = ((serializer: SerializerBytes, key: Any, value: Any?, load: (key: Any, value: Any?) -> Unit) -> Unit)
abstract class Storage(val logger: KLogger) : AutoCloseable {
companion object {
@ -39,6 +41,8 @@ abstract class Storage(val logger: KLogger) : AutoCloseable {
}
const val versionTag = "__VERSION__"
private val defaultLogger: KLogger = KotlinLogging.logger(NOPLogger.NOP_LOGGER)
}
fun init(initMessage: String) {
@ -152,9 +156,9 @@ abstract class Storage(val logger: KLogger) : AutoCloseable {
fun onSave(onSave: AccessFunc): Builder
/**
* Specify how to configure the kryo instance for serialization. This instance of kryo will be used to serialize the key/value data, which is subsequently saved to disk
* Specify how to configure the kryo instance for serialization. This instance of kryo will be used to serialize the key/value data, which is subsequently saved
*/
fun onNewSerializer(onNewKryo: Kryo.() -> Unit): Builder
fun serializer(serializer: SerializerBytes): Builder
/**
* Assigns a logger to use for the storage system. The default is a No Operation (NOP) logger which will ignore everything.
@ -165,14 +169,19 @@ abstract class Storage(val logger: KLogger) : AutoCloseable {
@Suppress("UNCHECKED_CAST")
abstract class FileBuilder<B : Builder>: Builder {
var onLoad: AccessFunc? = null
var onSave: AccessFunc? = null
var onNewKryo: Kryo.() -> Unit = { }
companion object {
private val defaultSerializer = SerializerBytes {}
}
abstract var onLoad: AccessFunc
abstract var onSave: AccessFunc
var serializer: SerializerBytes = defaultSerializer
var shared = false
@Volatile var sharedBuild: Storage? = null
@Volatile
var sharedBuild: Storage? = null
var logger: KLogger = KotlinLogging.logger(NOPLogger.NOP_LOGGER)
var logger: KLogger = defaultLogger
var file = File("storage.db")
var readOnly = false
@ -207,8 +216,8 @@ abstract class Storage(val logger: KLogger) : AutoCloseable {
return this
}
override fun onNewSerializer(onNewKryo: Kryo.() -> Unit): Builder {
this.onNewKryo = onNewKryo
override fun serializer(serializer: SerializerBytes): Builder {
this.serializer = serializer
return this
}
@ -249,6 +258,38 @@ abstract class Storage(val logger: KLogger) : AutoCloseable {
readOnlyViolent = true
return this as B
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is FileBuilder<*>) return false
if (onLoad != other.onLoad) return false //lambda
if (onSave != other.onSave) return false //lambda
if (serializer != other.serializer) return false //lambda
if (shared != other.shared) return false
if (sharedBuild != other.sharedBuild) return false
if (logger != other.logger) return false
if (file != other.file) return false
if (readOnly != other.readOnly) return false
if (readOnlyViolent != other.readOnlyViolent) return false
if (isStringBased != other.isStringBased) return false
return true
}
override fun hashCode(): Int {
var result = onLoad?.hashCode() ?: 0 //lambda
result = 31 * result + (onSave?.hashCode() ?: 0) //lambda
result = 31 * result + serializer.hashCode() //lambda
result = 31 * result + shared.hashCode()
result = 31 * result + (sharedBuild?.hashCode() ?: 0)
result = 31 * result + logger.hashCode()
result = 31 * result + file.hashCode()
result = 31 * result + readOnly.hashCode()
result = 31 * result + readOnlyViolent.hashCode()
result = 31 * result + isStringBased.hashCode()
return result
}
}
@ -260,7 +301,7 @@ abstract class Storage(val logger: KLogger) : AutoCloseable {
* This storage system DOES NOT care about serializing data, so `register` has no effect.
*/
class Memory : Builder {
private var logger: KLogger = KotlinLogging.logger(NOPLogger.NOP_LOGGER)
private var logger: KLogger = defaultLogger
private var shared = false
@Volatile private var sharedBuild: Storage? = null
@ -292,7 +333,7 @@ abstract class Storage(val logger: KLogger) : AutoCloseable {
return this
}
override fun onNewSerializer(onNewKryo: Kryo.() -> Unit): Builder {
override fun serializer(serializer: SerializerBytes): Builder {
return this
}
@ -304,14 +345,55 @@ abstract class Storage(val logger: KLogger) : AutoCloseable {
this.logger = logger
return this
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Memory) return false
if (logger != other.logger) return false
if (shared != other.shared) return false
if (sharedBuild != other.sharedBuild) return false
if (isStringBased != other.isStringBased) return false
return true
}
override fun hashCode(): Int {
var result = logger.hashCode()
result = 31 * result + shared.hashCode()
result = 31 * result + (sharedBuild?.hashCode() ?: 0)
result = 31 * result + isStringBased.hashCode()
return result
}
}
/**
* Java property file storage system
*/
class Property : FileBuilder<Property>() {
companion object {
// property files MUST load via strings.
val defaultOnLoad: AccessFunc = { serializer, key, value, load ->
val keyBytes = (key as String).decodeBase58()
val valueBytes = (value as String).decodeBase58()
val xKey = serializer.deserialize<Any>(keyBytes)!!
val xValue = serializer.deserialize<Any>(valueBytes)
load(xKey, xValue)
}
val defaultOnSave: AccessFunc = { serializer, key, value, save ->
val xKey = serializer.serialize(key).encodeToBase58String()
val xValue = serializer.serialize(value).encodeToBase58String()
save(xKey, xValue)
}
}
private var autoLoad = false
override var onLoad: AccessFunc = defaultOnLoad
override var onSave: AccessFunc = defaultOnSave
override val isStringBased = true
/**
@ -319,8 +401,16 @@ abstract class Storage(val logger: KLogger) : AutoCloseable {
*/
override fun build(): Storage {
return manageShared {
PropertyStore(file.absoluteFile, autoLoad, readOnly, readOnlyViolent, logger, onNewKryo,
onLoad, onSave)
PropertyStore(
dbFile = file.absoluteFile,
autoLoad = autoLoad,
readOnly = readOnly,
readOnlyViolent = readOnlyViolent,
logger = logger,
serializer = serializer,
onLoad = onLoad,
onSave = onSave
)
}
}
@ -331,6 +421,24 @@ abstract class Storage(val logger: KLogger) : AutoCloseable {
autoLoad = true
return this
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Property) return false
if (!super.equals(other)) return false
if (autoLoad != other.autoLoad) return false
if (isStringBased != other.isStringBased) return false
return true
}
override fun hashCode(): Int {
var result = super.hashCode()
result = 31 * result + autoLoad.hashCode()
result = 31 * result + isStringBased.hashCode()
return result
}
}
// /**

View File

@ -15,8 +15,8 @@
*/
package dorkbox.storage.types
import com.esotericsoftware.kryo.Kryo
import dorkbox.storage.AccessFunc
import dorkbox.storage.serializer.SerializerBytes
import mu.KLogger
import java.io.File
import java.io.FileInputStream
@ -32,10 +32,19 @@ class PropertyStore(
readOnly: Boolean,
readOnlyViolent: Boolean,
logger: KLogger,
onNewKryo: Kryo.() -> Unit,
onLoad: AccessFunc?,
onSave: AccessFunc?,
) : StringStore(dbFile, autoLoad, readOnly, readOnlyViolent, logger, onNewKryo, onLoad, onSave) {
serializer: SerializerBytes,
onLoad: AccessFunc,
onSave: AccessFunc,
) : StringStore(
dbFile = dbFile,
autoLoad = autoLoad,
readOnly = readOnly,
readOnlyViolent = readOnlyViolent,
logger = logger,
serializer = serializer,
onLoad = onLoad,
onSave = onSave
) {
private val propertiesOnDisk = object : Properties() {
override fun keys(): Enumeration<Any> {
@ -71,7 +80,7 @@ class PropertyStore(
} else {
try {
propertiesOnDisk[k] = v
onLoadFunc(k, v, loadFunc)
onLoad(serializer, k, v, loadFunc)
} catch (e: Exception) {
logger.error("Unable to parse property ($dbFile) [$k] : $v", e)
}

View File

@ -15,9 +15,6 @@
*/
package dorkbox.storage.types
import com.esotericsoftware.kryo.Kryo
import dorkbox.bytes.decodeBase58
import dorkbox.bytes.encodeToBase58String
import dorkbox.storage.AccessFunc
import dorkbox.storage.Storage
import dorkbox.storage.serializer.SerializerBytes
@ -35,9 +32,9 @@ abstract class StringStore(
val readOnly: Boolean,
val readOnlyViolent: Boolean,
logger: KLogger,
onNewKryo: Kryo.() -> Unit,
onLoad: AccessFunc?,
onSave: AccessFunc?,
val serializer: SerializerBytes,
val onLoad: AccessFunc,
val onSave: AccessFunc,
) : Storage(logger) {
companion object {
@ -52,13 +49,10 @@ abstract class StringStore(
@Volatile
private var isDirty = false
protected val onLoadFunc: AccessFunc
private val onSaveFunc: AccessFunc
protected val loadFunc: (key: Any, value: Any) -> Unit
protected val loadFunc: (key: Any, value: Any?) -> Unit
protected val saveFunc: (key: Any, value: Any?) -> Unit
private val loadedProps = ConcurrentHashMap<Any, Any>()
private val serializer = SerializerBytes(onNewKryo)
private val loadedProps = ConcurrentHashMap<Any, Any?>()
init {
loadFunc = { key, value ->
@ -68,29 +62,6 @@ abstract class StringStore(
onSave(key, value)
}
onLoadFunc = if (onLoad != null) {
onLoad
} else {
{ key, value, load ->
val keyBytes = (key as String).decodeBase58()
val valueBytes = (value as String).decodeBase58()
val xKey = serializer.deserialize<Any>(keyBytes)!!
val xValue = serializer.deserialize<Any>(valueBytes)!!
load(xKey, xValue)
}
}
onSaveFunc = if (onSave != null) {
onSave
} else {
{ key, value, save ->
val xKey = serializer.serialize(key)!!.encodeToBase58String()
val xValue = serializer.serialize(value)!!.encodeToBase58String()
save(xKey, xValue)
}
}
// Make sure that the timer is run on shutdown. A HARD shutdown will just POW! kill it, a "nice" shutdown will run the hook
Runtime.getRuntime().addShutdownHook(thread)
}
@ -132,7 +103,7 @@ abstract class StringStore(
synchronized(dbFile) {
if (value != null) {
try {
onSaveFunc(key, value, saveFunc)
onSave(serializer, key, value, saveFunc)
} catch (e: Exception) {
logger.error("Unable to parse property ($dbFile) [$key] : $value", e)
}
@ -168,7 +139,7 @@ abstract class StringStore(
save(k, v.toString())
} else {
try {
onSaveFunc(k, v, saveFunc)
onSave(serializer, k, v, saveFunc)
} catch (e: Exception) {
logger.error("Unable to parse property ($dbFile) [$k] : $v", e)
}
@ -287,15 +258,12 @@ abstract class StringStore(
if (autoLoad != other.autoLoad) return false
if (readOnly != other.readOnly) return false
if (readOnlyViolent != other.readOnlyViolent) return false
if (thread != other.thread) return false
if (lastModifiedTime != other.lastModifiedTime) return false
if (isDirty != other.isDirty) return false
if (onLoadFunc != other.onLoadFunc) return false
if (onSaveFunc != other.onSaveFunc) return false
if (loadFunc != other.loadFunc) return false
if (saveFunc != other.saveFunc) return false
if (serializer != other.serializer) return false // class with lambda
if (onLoad != other.onLoad) return false //lambda
if (onSave != other.onSave) return false //lambda
if (loadedProps != other.loadedProps) return false
if (serializer != other.serializer) return false
return true
}
@ -305,15 +273,12 @@ abstract class StringStore(
result = 31 * result + autoLoad.hashCode()
result = 31 * result + readOnly.hashCode()
result = 31 * result + readOnlyViolent.hashCode()
result = 31 * result + thread.hashCode()
result = 31 * result + lastModifiedTime.hashCode()
result = 31 * result + isDirty.hashCode()
result = 31 * result + onLoadFunc.hashCode()
result = 31 * result + onSaveFunc.hashCode()
result = 31 * result + loadFunc.hashCode()
result = 31 * result + saveFunc.hashCode()
result = 31 * result + serializer.hashCode() // class with lambda
result = 31 * result + onLoad.hashCode() //lambda
result = 31 * result + onSave.hashCode() //lambda
result = 31 * result + loadedProps.hashCode()
result = 31 * result + serializer.hashCode()
return result
}
}