Serializer is now specified instead of unit function that manages kryo registrations
This commit is contained in:
parent
8cb3b5abec
commit
687889abce
|
@ -15,7 +15,9 @@
|
||||||
*/
|
*/
|
||||||
package dorkbox.storage
|
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.MemoryStore
|
||||||
import dorkbox.storage.types.PropertyStore
|
import dorkbox.storage.types.PropertyStore
|
||||||
import mu.KLogger
|
import mu.KLogger
|
||||||
|
@ -24,7 +26,7 @@ import org.slf4j.helpers.NOPLogger
|
||||||
import java.io.File
|
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 {
|
abstract class Storage(val logger: KLogger) : AutoCloseable {
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -39,6 +41,8 @@ abstract class Storage(val logger: KLogger) : AutoCloseable {
|
||||||
}
|
}
|
||||||
|
|
||||||
const val versionTag = "__VERSION__"
|
const val versionTag = "__VERSION__"
|
||||||
|
|
||||||
|
private val defaultLogger: KLogger = KotlinLogging.logger(NOPLogger.NOP_LOGGER)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun init(initMessage: String) {
|
fun init(initMessage: String) {
|
||||||
|
@ -152,9 +156,9 @@ abstract class Storage(val logger: KLogger) : AutoCloseable {
|
||||||
fun onSave(onSave: AccessFunc): Builder
|
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.
|
* 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")
|
@Suppress("UNCHECKED_CAST")
|
||||||
abstract class FileBuilder<B : Builder>: Builder {
|
abstract class FileBuilder<B : Builder>: Builder {
|
||||||
var onLoad: AccessFunc? = null
|
companion object {
|
||||||
var onSave: AccessFunc? = null
|
private val defaultSerializer = SerializerBytes {}
|
||||||
var onNewKryo: Kryo.() -> Unit = { }
|
}
|
||||||
|
abstract var onLoad: AccessFunc
|
||||||
|
abstract var onSave: AccessFunc
|
||||||
|
|
||||||
|
var serializer: SerializerBytes = defaultSerializer
|
||||||
|
|
||||||
var shared = false
|
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 file = File("storage.db")
|
||||||
var readOnly = false
|
var readOnly = false
|
||||||
|
@ -207,8 +216,8 @@ abstract class Storage(val logger: KLogger) : AutoCloseable {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNewSerializer(onNewKryo: Kryo.() -> Unit): Builder {
|
override fun serializer(serializer: SerializerBytes): Builder {
|
||||||
this.onNewKryo = onNewKryo
|
this.serializer = serializer
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,6 +258,38 @@ abstract class Storage(val logger: KLogger) : AutoCloseable {
|
||||||
readOnlyViolent = true
|
readOnlyViolent = true
|
||||||
return this as B
|
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.
|
* This storage system DOES NOT care about serializing data, so `register` has no effect.
|
||||||
*/
|
*/
|
||||||
class Memory : Builder {
|
class Memory : Builder {
|
||||||
private var logger: KLogger = KotlinLogging.logger(NOPLogger.NOP_LOGGER)
|
private var logger: KLogger = defaultLogger
|
||||||
|
|
||||||
private var shared = false
|
private var shared = false
|
||||||
@Volatile private var sharedBuild: Storage? = null
|
@Volatile private var sharedBuild: Storage? = null
|
||||||
|
@ -292,7 +333,7 @@ abstract class Storage(val logger: KLogger) : AutoCloseable {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNewSerializer(onNewKryo: Kryo.() -> Unit): Builder {
|
override fun serializer(serializer: SerializerBytes): Builder {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,14 +345,55 @@ abstract class Storage(val logger: KLogger) : AutoCloseable {
|
||||||
this.logger = logger
|
this.logger = logger
|
||||||
return this
|
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
|
* Java property file storage system
|
||||||
*/
|
*/
|
||||||
class Property : FileBuilder<Property>() {
|
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
|
private var autoLoad = false
|
||||||
|
|
||||||
|
override var onLoad: AccessFunc = defaultOnLoad
|
||||||
|
override var onSave: AccessFunc = defaultOnSave
|
||||||
|
|
||||||
override val isStringBased = true
|
override val isStringBased = true
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -319,8 +401,16 @@ abstract class Storage(val logger: KLogger) : AutoCloseable {
|
||||||
*/
|
*/
|
||||||
override fun build(): Storage {
|
override fun build(): Storage {
|
||||||
return manageShared {
|
return manageShared {
|
||||||
PropertyStore(file.absoluteFile, autoLoad, readOnly, readOnlyViolent, logger, onNewKryo,
|
PropertyStore(
|
||||||
onLoad, onSave)
|
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
|
autoLoad = true
|
||||||
return this
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package dorkbox.storage.types
|
package dorkbox.storage.types
|
||||||
|
|
||||||
import com.esotericsoftware.kryo.Kryo
|
|
||||||
import dorkbox.storage.AccessFunc
|
import dorkbox.storage.AccessFunc
|
||||||
|
import dorkbox.storage.serializer.SerializerBytes
|
||||||
import mu.KLogger
|
import mu.KLogger
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
|
@ -32,10 +32,19 @@ class PropertyStore(
|
||||||
readOnly: Boolean,
|
readOnly: Boolean,
|
||||||
readOnlyViolent: Boolean,
|
readOnlyViolent: Boolean,
|
||||||
logger: KLogger,
|
logger: KLogger,
|
||||||
onNewKryo: Kryo.() -> Unit,
|
serializer: SerializerBytes,
|
||||||
onLoad: AccessFunc?,
|
onLoad: AccessFunc,
|
||||||
onSave: AccessFunc?,
|
onSave: AccessFunc,
|
||||||
) : StringStore(dbFile, autoLoad, readOnly, readOnlyViolent, logger, onNewKryo, onLoad, onSave) {
|
) : StringStore(
|
||||||
|
dbFile = dbFile,
|
||||||
|
autoLoad = autoLoad,
|
||||||
|
readOnly = readOnly,
|
||||||
|
readOnlyViolent = readOnlyViolent,
|
||||||
|
logger = logger,
|
||||||
|
serializer = serializer,
|
||||||
|
onLoad = onLoad,
|
||||||
|
onSave = onSave
|
||||||
|
) {
|
||||||
|
|
||||||
private val propertiesOnDisk = object : Properties() {
|
private val propertiesOnDisk = object : Properties() {
|
||||||
override fun keys(): Enumeration<Any> {
|
override fun keys(): Enumeration<Any> {
|
||||||
|
@ -71,7 +80,7 @@ class PropertyStore(
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
propertiesOnDisk[k] = v
|
propertiesOnDisk[k] = v
|
||||||
onLoadFunc(k, v, loadFunc)
|
onLoad(serializer, k, v, loadFunc)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger.error("Unable to parse property ($dbFile) [$k] : $v", e)
|
logger.error("Unable to parse property ($dbFile) [$k] : $v", e)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,9 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package dorkbox.storage.types
|
package dorkbox.storage.types
|
||||||
|
|
||||||
import com.esotericsoftware.kryo.Kryo
|
|
||||||
import dorkbox.bytes.decodeBase58
|
|
||||||
import dorkbox.bytes.encodeToBase58String
|
|
||||||
import dorkbox.storage.AccessFunc
|
import dorkbox.storage.AccessFunc
|
||||||
import dorkbox.storage.Storage
|
import dorkbox.storage.Storage
|
||||||
import dorkbox.storage.serializer.SerializerBytes
|
import dorkbox.storage.serializer.SerializerBytes
|
||||||
|
@ -35,9 +32,9 @@ abstract class StringStore(
|
||||||
val readOnly: Boolean,
|
val readOnly: Boolean,
|
||||||
val readOnlyViolent: Boolean,
|
val readOnlyViolent: Boolean,
|
||||||
logger: KLogger,
|
logger: KLogger,
|
||||||
onNewKryo: Kryo.() -> Unit,
|
val serializer: SerializerBytes,
|
||||||
onLoad: AccessFunc?,
|
val onLoad: AccessFunc,
|
||||||
onSave: AccessFunc?,
|
val onSave: AccessFunc,
|
||||||
) : Storage(logger) {
|
) : Storage(logger) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -52,13 +49,10 @@ abstract class StringStore(
|
||||||
@Volatile
|
@Volatile
|
||||||
private var isDirty = false
|
private var isDirty = false
|
||||||
|
|
||||||
protected val onLoadFunc: AccessFunc
|
protected val loadFunc: (key: Any, value: Any?) -> Unit
|
||||||
private val onSaveFunc: AccessFunc
|
|
||||||
protected val loadFunc: (key: Any, value: Any) -> Unit
|
|
||||||
protected val saveFunc: (key: Any, value: Any?) -> Unit
|
protected val saveFunc: (key: Any, value: Any?) -> Unit
|
||||||
|
|
||||||
private val loadedProps = ConcurrentHashMap<Any, Any>()
|
private val loadedProps = ConcurrentHashMap<Any, Any?>()
|
||||||
private val serializer = SerializerBytes(onNewKryo)
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
loadFunc = { key, value ->
|
loadFunc = { key, value ->
|
||||||
|
@ -68,29 +62,6 @@ abstract class StringStore(
|
||||||
onSave(key, value)
|
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
|
// 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)
|
Runtime.getRuntime().addShutdownHook(thread)
|
||||||
}
|
}
|
||||||
|
@ -132,7 +103,7 @@ abstract class StringStore(
|
||||||
synchronized(dbFile) {
|
synchronized(dbFile) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
try {
|
try {
|
||||||
onSaveFunc(key, value, saveFunc)
|
onSave(serializer, key, value, saveFunc)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger.error("Unable to parse property ($dbFile) [$key] : $value", e)
|
logger.error("Unable to parse property ($dbFile) [$key] : $value", e)
|
||||||
}
|
}
|
||||||
|
@ -168,7 +139,7 @@ abstract class StringStore(
|
||||||
save(k, v.toString())
|
save(k, v.toString())
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
onSaveFunc(k, v, saveFunc)
|
onSave(serializer, k, v, saveFunc)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger.error("Unable to parse property ($dbFile) [$k] : $v", e)
|
logger.error("Unable to parse property ($dbFile) [$k] : $v", e)
|
||||||
}
|
}
|
||||||
|
@ -287,15 +258,12 @@ abstract class StringStore(
|
||||||
if (autoLoad != other.autoLoad) return false
|
if (autoLoad != other.autoLoad) return false
|
||||||
if (readOnly != other.readOnly) return false
|
if (readOnly != other.readOnly) return false
|
||||||
if (readOnlyViolent != other.readOnlyViolent) return false
|
if (readOnlyViolent != other.readOnlyViolent) return false
|
||||||
if (thread != other.thread) return false
|
|
||||||
if (lastModifiedTime != other.lastModifiedTime) return false
|
if (lastModifiedTime != other.lastModifiedTime) return false
|
||||||
if (isDirty != other.isDirty) return false
|
if (isDirty != other.isDirty) return false
|
||||||
if (onLoadFunc != other.onLoadFunc) return false
|
if (serializer != other.serializer) return false // class with lambda
|
||||||
if (onSaveFunc != other.onSaveFunc) return false
|
if (onLoad != other.onLoad) return false //lambda
|
||||||
if (loadFunc != other.loadFunc) return false
|
if (onSave != other.onSave) return false //lambda
|
||||||
if (saveFunc != other.saveFunc) return false
|
|
||||||
if (loadedProps != other.loadedProps) return false
|
if (loadedProps != other.loadedProps) return false
|
||||||
if (serializer != other.serializer) return false
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -305,15 +273,12 @@ abstract class StringStore(
|
||||||
result = 31 * result + autoLoad.hashCode()
|
result = 31 * result + autoLoad.hashCode()
|
||||||
result = 31 * result + readOnly.hashCode()
|
result = 31 * result + readOnly.hashCode()
|
||||||
result = 31 * result + readOnlyViolent.hashCode()
|
result = 31 * result + readOnlyViolent.hashCode()
|
||||||
result = 31 * result + thread.hashCode()
|
|
||||||
result = 31 * result + lastModifiedTime.hashCode()
|
result = 31 * result + lastModifiedTime.hashCode()
|
||||||
result = 31 * result + isDirty.hashCode()
|
result = 31 * result + isDirty.hashCode()
|
||||||
result = 31 * result + onLoadFunc.hashCode()
|
result = 31 * result + serializer.hashCode() // class with lambda
|
||||||
result = 31 * result + onSaveFunc.hashCode()
|
result = 31 * result + onLoad.hashCode() //lambda
|
||||||
result = 31 * result + loadFunc.hashCode()
|
result = 31 * result + onSave.hashCode() //lambda
|
||||||
result = 31 * result + saveFunc.hashCode()
|
|
||||||
result = 31 * result + loadedProps.hashCode()
|
result = 31 * result + loadedProps.hashCode()
|
||||||
result = 31 * result + serializer.hashCode()
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue