diff options
author | Dico Karssiens <dico.karssiens@gmail.com> | 2018-07-26 22:50:29 +0100 |
---|---|---|
committer | Dico Karssiens <dico.karssiens@gmail.com> | 2018-07-26 22:50:29 +0100 |
commit | ea7c27a7fd7c127920eb5e2aa8f2b2b7c921c607 (patch) | |
tree | bbb26fc4cb349632debdb9e5387a7597a3050c6c | |
parent | bf1da033703f1343cfc54c61f8ace1fea7dbac25 (diff) |
Minor tweaks, switch to PostgreSQL for debuggigng purposes
12 files changed, 135 insertions, 219 deletions
diff --git a/build.gradle.kts b/build.gradle.kts index f0cd548..4f06249 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,6 +6,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformJvmPlugin import java.io.PrintWriter +val firstImport = false val stdout = PrintWriter(File("$rootDir/gradle-output.txt")) buildscript { @@ -30,8 +31,8 @@ allprojects { } dependencies { val spigotVersion = "1.13-R0.1-SNAPSHOT" - compile("org.bukkit:bukkit:$spigotVersion") - compile("org.spigotmc:spigot-api:$spigotVersion") + compile("org.bukkit:bukkit:$spigotVersion") { isTransitive = false } + compile("org.spigotmc:spigot-api:$spigotVersion") { isTransitive = false } compile("net.sf.trove4j:trove4j:3.0.3") testCompile("junit:junit:4.12") @@ -80,73 +81,23 @@ dependencies { compile("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion") compile("com.fasterxml.jackson.module:jackson-module-kotlin:$jacksonVersion") compile("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:$jacksonVersion") + //compile("org.yaml:snakeyaml:1.19") compile("org.slf4j:slf4j-api:1.7.25") compile("ch.qos.logback:logback-classic:1.2.3") } tasks { - val compileKotlin by getting(KotlinCompile::class) { - //this.setupPlugins() - - //serializedCompilerArguments.add("-java-parameters") - } - - fun Jar.packageDependencies(vararg names: String) { - //afterEvaluate { - from(*project.configurations.compile.resolvedConfiguration.firstLevelModuleDependencies - .filter { it.moduleName in names } - .flatMap { it.allModuleArtifacts } - .map { it.file } - .map(::zipTree) - .toTypedArray() - ) - //} - } - - fun Jar.packageDependency(name: String, configure: ModuleDependency.() -> Unit) { - val configuration = project.configurations.compile.copyRecursive() - - configuration.dependencies.removeIf { - if (it is ModuleDependency && it.name == name) { - it.configure() - false - } else true - } - - from(*configuration.resolvedConfiguration.resolvedArtifacts - .map { it.file } - .map(::zipTree) - .toTypedArray()) - } - - fun Jar.packageArtifacts(vararg names: String) { - //afterEvaluate { - from(*project.configurations.compile.resolvedConfiguration.resolvedArtifacts - .filter { - val id = it.moduleVersion.id - (id.name in names).also { - if (!it) stdout.println("Not including artifact: ${id.group}:${id.name}") - } - } - .map { it.file } - .map(::zipTree) - .toTypedArray()) - //} - } - val serverDir = "$rootDir/debug" - val jar by getting(Jar::class) - val kotlinStdlibJar by creating(Jar::class) { destinationDir = file("$serverDir/lib") archiveName = "kotlin-stdlib.jar" packageDependencies("kotlin-stdlib-jdk8") } - val shadowJar by getting(ShadowJar::class) { - relocate("", "") + val debugEnvironment by creating(Exec::class) { + } val releaseJar by creating(ShadowJar::class) { @@ -201,4 +152,56 @@ allprojects { tasks.filter { it is Jar }.forEach { it.group = "artifacts" } } -stdout.flush()
\ No newline at end of file +stdout.flush() +stdout.close() + +fun Jar.packageDependencies(vararg names: String) { + if (!firstImport) { + from(*project.configurations.compile.resolvedConfiguration.firstLevelModuleDependencies + .filter { it.moduleName in names } + .flatMap { it.allModuleArtifacts } + .map { it.file } + .map(::zipTree) + .toTypedArray() + ) + } +} + +fun Jar.packageDependency(name: String, configure: ModuleDependency.() -> Unit) { + if (!firstImport) { + val configuration = project.configurations.compile.copyRecursive() + + configuration.dependencies.removeIf { + if (it is ModuleDependency && it.name == name) { + it.configure() + false + } else true + } + + from(*configuration.resolvedConfiguration.resolvedArtifacts + .map { it.file } + .map(::zipTree) + .toTypedArray()) + } +} + +@Suppress("IMPLICIT_CAST_TO_ANY") +fun Jar.packageArtifacts(vararg names: String) { + if (!firstImport) { + from(*project.configurations.compile.resolvedConfiguration.firstLevelModuleDependencies + .flatMap { dep -> dep.allModuleArtifacts.map { dep to it } } + .filter { pair -> + val (dep, art) = pair + val id = art.moduleVersion.id + (id.name in names).also { + val artName = art.moduleVersion.id.let {"${it.group}:${it.name}:${it.version}"} + val depName = dep.let { "${it.moduleGroup}:${it.moduleName}:${it.moduleVersion}" } + val name = "$artName \n from $depName" + stdout.println("${if (it) "Including" else "Not including"} artifact $name") + } + } + .map { pair -> pair.second.file } + .map { if (it.isDirectory()) it else zipTree(it) } + .toTypedArray()) + } +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/ICommandSuspendReceiver.java b/dicore3/command/src/main/java/io/dico/dicore/command/ICommandSuspendReceiver.java deleted file mode 100644 index 3f952d3..0000000 --- a/dicore3/command/src/main/java/io/dico/dicore/command/ICommandSuspendReceiver.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.dico.dicore.command; - -public interface ICommandSuspendReceiver extends ICommandReceiver { - - int getTimeout(); - -} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/SuspensionTimeout.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/SuspensionTimeout.java new file mode 100644 index 0000000..b70b100 --- /dev/null +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/SuspensionTimeout.java @@ -0,0 +1,13 @@ +package io.dico.dicore.command.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface SuspensionTimeout { + // not currently implemented + int timeoutMillis(); +} diff --git a/dicore3/command/src/main/kotlin/io/dico/dicore/command/registration/reflect/KotlinReflectiveRegistration.kt b/dicore3/command/src/main/kotlin/io/dico/dicore/command/registration/reflect/KotlinReflectiveRegistration.kt index b31d091..bc48a26 100644 --- a/dicore3/command/src/main/kotlin/io/dico/dicore/command/registration/reflect/KotlinReflectiveRegistration.kt +++ b/dicore3/command/src/main/kotlin/io/dico/dicore/command/registration/reflect/KotlinReflectiveRegistration.kt @@ -26,7 +26,9 @@ fun callAsCoroutine(command: ReflectiveCommand, args: Array<Any?>): String? { val dispatcher = Executor { task -> factory.plugin.server.scheduler.runTask(factory.plugin, task) }.asCoroutineDispatcher() - // UNDISPATCHED causes the handler to run until the first suspension point on the current thread + // UNDISPATCHED causes the handler to run until the first suspension point on the current thread, + // meaning command handlers that don't have suspension points will run completely synchronously. + // Tasks that take time to compute should suspend the coroutine and resume on another thread. val job = async(context = dispatcher, start = UNDISPATCHED) { command.method.invokeSuspend(command.instance, args) } if (job.isCompleted) { @@ -48,11 +50,6 @@ fun callAsCoroutine(command: ReflectiveCommand, private suspend fun Method.invokeSuspend(instance: Any?, args: Array<Any?>): Any? { return suspendCoroutineOrReturn { cont -> - println() - println("Calling command method suspendedly") - println(toGenericString()) - println(Arrays.toString(arrayOf(instance, *args, cont))) - println() invoke(instance, *args, cont) } } diff --git a/src/main/kotlin/io/dico/parcels2/Options.kt b/src/main/kotlin/io/dico/parcels2/Options.kt index d8cc9d6..1ae8410 100644 --- a/src/main/kotlin/io/dico/parcels2/Options.kt +++ b/src/main/kotlin/io/dico/parcels2/Options.kt @@ -17,7 +17,7 @@ import java.util.* class Options { var worlds: Map<String, WorldOptions> = HashMap() private set - var storage: StorageOptions = StorageOptions("mysql", DataConnectionOptions()) + var storage: StorageOptions = StorageOptions("postgresql", DataConnectionOptions()) fun addWorld(name: String, options: WorldOptions) = (worlds as MutableMap).put(name, options) @@ -90,7 +90,7 @@ data class DataConnectionOptions(val address: String = "localhost", logger.error("(Invalidly) blank address in data storage options") } - val port = address.substring(idx).toIntOrNull() ?: return null.also { + val port = address.substring(idx + 1).toIntOrNull() ?: return null.also { logger.error("Invalid port number in data storage options: $it, using $defaultPort as default") } diff --git a/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt b/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt index 4330d1c..82a6f59 100644 --- a/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt +++ b/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt @@ -66,7 +66,12 @@ class ParcelsPlugin : JavaPlugin() { yamlObjectMapper.readerForUpdating(options).readValue<Options>(optionsFile) } else if (optionsFile.tryCreate()) { options.addWorld("plotworld", WorldOptions()) - yamlObjectMapper.writeValue(optionsFile, options) + try { + yamlObjectMapper.writeValue(optionsFile, options) + } catch (ex: Throwable) { + optionsFile.delete() + throw ex + } } else { plogger.error("Failed to save options file ${optionsFile.canonicalPath}") return false diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandAsync.kt b/src/main/kotlin/io/dico/parcels2/command/CommandAsync.kt deleted file mode 100644 index 2131715..0000000 --- a/src/main/kotlin/io/dico/parcels2/command/CommandAsync.kt +++ /dev/null @@ -1,83 +0,0 @@ -package io.dico.parcels2.command - -import io.dico.dicore.command.CommandException -import io.dico.dicore.command.CommandResult -import io.dico.dicore.command.EMessageType -import io.dico.dicore.command.ExecutionContext -import io.dico.dicore.command.chat.IChatController -import io.dico.parcels2.ParcelsPlugin -import io.dico.parcels2.logger -import kotlinx.coroutines.experimental.* -import org.bukkit.command.CommandSender -import kotlin.coroutines.experimental.Continuation -import kotlin.coroutines.experimental.suspendCoroutine - -/* - * Interface to implicitly access plugin by creating extension functions for it - */ -interface HasPlugin { - val plugin: ParcelsPlugin -} - -class CommandAsyncScope { - - suspend fun <T> HasPlugin.awaitSynchronousTask(delay: Int = 0, task: () -> T): T { - return suspendCoroutine { cont: Continuation<T> -> - plugin.server.scheduler.runTaskLater(plugin, l@{ - val result = try { - task() - } catch (ex: CommandException) { - cont.resumeWithException(ex) - return@l - } catch (ex: Throwable) { - cont.context.cancel(ex) - return@l - } - cont.resume(result) - }, delay.toLong()) - } - } - - fun <T> HasPlugin.synchronousTask(delay: Int = 0, task: () -> T): Deferred<T> { - return async { awaitSynchronousTask(delay, task) } - } - -} - -fun <T : Any?> HasPlugin.delegateCommandAsync(context: ExecutionContext, - block: suspend CommandAsyncScope.() -> T) { - - val job: Deferred<Any?> = async(/*context = plugin.storage.asyncDispatcher, */start = CoroutineStart.ATOMIC) { - CommandAsyncScope().block() - } - - fun Job.invokeOnCompletionSynchronously(block: (Throwable?) -> Unit) = invokeOnCompletion { - plugin.server.scheduler.runTask(plugin) { block(it) } - } - - job.invokeOnCompletionSynchronously l@{ exception: Throwable? -> - exception?.let { - context.address.chatController.handleCoroutineException(context.sender, context, it) - return@l - } - - val result = job.getCompleted() - val message = when (result) { - is String -> result - is CommandResult -> result.message - else -> null - } - - context.address.chatController.sendMessage(context.sender, EMessageType.RESULT, message) - } - -} - -fun IChatController.handleCoroutineException(sender: CommandSender, context: ExecutionContext, exception: Throwable) { - if (exception is CancellationException) { - sendMessage(sender, EMessageType.EXCEPTION, "The command was cancelled unexpectedly (see console)") - logger.warn("An asynchronously dispatched command was cancelled unexpectedly", exception) - } else { - handleException(sender, context, exception) - } -} diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandRequirement.kt b/src/main/kotlin/io/dico/parcels2/command/CommandRequirement.kt index a98ddc1..f4b5c73 100644 --- a/src/main/kotlin/io/dico/parcels2/command/CommandRequirement.kt +++ b/src/main/kotlin/io/dico/parcels2/command/CommandRequirement.kt @@ -19,44 +19,28 @@ import kotlin.reflect.jvm.kotlinFunction @Retention(AnnotationRetention.RUNTIME) annotation class ParcelRequire(val admin: Boolean = false, val owner: Boolean = false) -sealed class BaseScope(private var _timeout: Int = 0) : ICommandSuspendReceiver { - override fun getTimeout() = _timeout - fun setTimeout(timeout: Int) { - _timeout = timeout - } -} +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.RUNTIME) +annotation class SuspensionTimeout(val millis: Int) -class SuspendOnlyScope : BaseScope() -class ParcelScope(val world: ParcelWorld, val parcel: Parcel) : BaseScope() -class WorldOnlyScope(val world: ParcelWorld) : BaseScope() +open class WorldScope(val world: ParcelWorld) : ICommandReceiver +open class ParcelScope(val parcel: Parcel) : WorldScope(parcel.world) fun getParcelCommandReceiver(worlds: Worlds, context: ExecutionContext, method: Method, cmdName: String): ICommandReceiver { + val player = context.sender as Player val function = method.kotlinFunction!! val receiverType = function.extensionReceiverParameter!!.type - logger.info("Receiver type: ${receiverType.javaType.typeName}") - val require = function.findAnnotation<ParcelRequire>() val admin = require?.admin == true val owner = require?.owner == true - val player = context.sender as Player - return when (receiverType.jvmErasure) { - ParcelScope::class -> worlds.getParcelRequired(player, admin = admin, own = owner).let { - ParcelScope(it.world, it) - } - WorldOnlyScope::class -> worlds.getWorldRequired(player, admin = admin).let { - WorldOnlyScope(it) - } - SuspendOnlyScope::class -> SuspendOnlyScope() + ParcelScope::class -> ParcelScope(worlds.getParcelRequired(player, admin, owner)) + WorldScope::class -> WorldScope(worlds.getWorldRequired(player, admin)) else -> throw InternalError("Invalid command receiver type") - } } -/* - * Functions for checking - */ fun Worlds.getWorldRequired(player: Player, admin: Boolean = false): ParcelWorld { if (admin) Validate.isTrue(player.hasAdminManage, "You must have admin rights to use that command") return getWorld(player.world) diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelCommands.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelCommands.kt index 0917cf9..bae138a 100644 --- a/src/main/kotlin/io/dico/parcels2/command/ParcelCommands.kt +++ b/src/main/kotlin/io/dico/parcels2/command/ParcelCommands.kt @@ -8,6 +8,7 @@ import io.dico.dicore.command.annotation.Desc import io.dico.dicore.command.annotation.RequireParameters import io.dico.parcels2.ParcelOwner import io.dico.parcels2.ParcelsPlugin +import io.dico.parcels2.command.NamedParcelDefaultValue.FIRST_OWNED import io.dico.parcels2.logger import io.dico.parcels2.storage.getParcelBySerializedValue import io.dico.parcels2.util.hasParcelHomeOthers @@ -34,7 +35,7 @@ class ParcelCommands(val plugin: ParcelsPlugin) : ICommandReceiver.Factory { @Desc("Finds the unclaimed parcel nearest to origin,", "and gives it to you", shortVersion = "sets you up with a fresh, unclaimed parcel") - suspend fun WorldOnlyScope.cmdAuto(player: Player): Any? { + suspend fun WorldScope.cmdAuto(player: Player): Any? { logger.info("cmdAuto thread before await: ${Thread.currentThread().name}") val numOwnedParcels = plugin.storage.getNumParcels(ParcelOwner(uuid = player.uuid)).await() logger.info("cmdAuto thread before await: ${Thread.currentThread().name}") @@ -65,8 +66,9 @@ class ParcelCommands(val plugin: ParcelsPlugin) : ICommandReceiver.Factory { "more than one parcel", shortVersion = "teleports you to parcels") @RequireParameters(0) - suspend fun SuspendOnlyScope.cmdHome(player: Player, context: ExecutionContext, - @NamedParcelDefault(NamedParcelDefaultValue.FIRST_OWNED) target: NamedParcelTarget): Any? { + suspend fun cmdHome(player: Player, + @NamedParcelDefault(FIRST_OWNED) target: NamedParcelTarget): Any? + { if (player !== target.player && !player.hasParcelHomeOthers) { error("You do not have permission to teleport to other people's parcels") } diff --git a/src/main/kotlin/io/dico/parcels2/storage/ExposedBacking.kt b/src/main/kotlin/io/dico/parcels2/storage/ExposedBacking.kt index f25c2b7..ce6b45b 100644 --- a/src/main/kotlin/io/dico/parcels2/storage/ExposedBacking.kt +++ b/src/main/kotlin/io/dico/parcels2/storage/ExposedBacking.kt @@ -57,32 +57,30 @@ object ParcelOptionsT : Table("parcel_options") { private class ExposedDatabaseException(message: String? = null) : Exception(message) @Suppress("NOTHING_TO_INLINE") -class ExposedBacking(val dataSource: DataSource) : Backing { +class ExposedBacking(private val dataSourceFactory: () -> DataSource) : Backing { override val name get() = "Exposed" + private var dataSource: DataSource? = null private var database: Database? = null private var isShutdown: Boolean = false override val isConnected get() = database != null override suspend fun init() { - synchronized { - if (isShutdown) throw IllegalStateException() - database = Database.connect(dataSource) - transaction(database) { - create(WorldsT, ParcelsT, AddedLocalT, ParcelOptionsT) - } + if (isShutdown) throw IllegalStateException() + dataSource = dataSourceFactory() + database = Database.connect(dataSource!!) + transaction(database) { + create(WorldsT, ParcelsT, AddedLocalT, ParcelOptionsT) } } override suspend fun shutdown() { - synchronized { - if (isShutdown) throw IllegalStateException() - if (dataSource is HikariDataSource) { - dataSource.close() - } - database = null - isShutdown = true + if (isShutdown) throw IllegalStateException() + dataSource?.let { + if (it is HikariDataSource) it.close() } + database = null + isShutdown = true } private fun <T> transaction(statement: Transaction.() -> T) = transaction(database, statement) @@ -206,7 +204,7 @@ class ExposedBacking(val dataSource: DataSource) : Backing { if (data == null) { transaction { getParcelId(parcelFor)?.let { id -> - ParcelsT.deleteIgnoreWhere(limit = 1) { ParcelsT.id eq id } + ParcelsT.deleteIgnoreWhere() { ParcelsT.id eq id } // Below should cascade automatically /* @@ -245,7 +243,7 @@ class ExposedBacking(val dataSource: DataSource) : Backing { else getOrInitParcelId(parcelFor) - ParcelsT.update({ ParcelsT.id eq id }, limit = 1) { + ParcelsT.update({ ParcelsT.id eq id }) { it[ParcelsT.owner_uuid] = binaryUuid it[ParcelsT.owner_name] = name } diff --git a/src/main/kotlin/io/dico/parcels2/storage/Hikari.kt b/src/main/kotlin/io/dico/parcels2/storage/Hikari.kt index ae16c83..e21a24e 100644 --- a/src/main/kotlin/io/dico/parcels2/storage/Hikari.kt +++ b/src/main/kotlin/io/dico/parcels2/storage/Hikari.kt @@ -1,25 +1,34 @@ package io.dico.parcels2.storage import com.zaxxer.hikari.HikariConfig -import com.zaxxer.hikari.HikariDataSource import io.dico.parcels2.DataConnectionOptions -import javax.sql.DataSource -fun getHikariDataSource(dialectName: String, - driver: String, - dco: DataConnectionOptions): DataSource = with(HikariConfig()) { +fun getHikariConfig(dialectName: String, + dco: DataConnectionOptions): HikariConfig = HikariConfig().apply { val (address, port) = dco.splitAddressAndPort() ?: throw IllegalArgumentException("Invalid address: ${dco.address}") - poolName = "redstonerplots" + when (dialectName) { + "postgresql" -> run { + dataSourceClassName = "org.postgresql.ds.PGSimpleDataSource" + dataSourceProperties["serverName"] = address + dataSourceProperties["portNumber"] = port.toString() + dataSourceProperties["databaseName"] = dco.database + } + else -> throw IllegalArgumentException("Unsupported dialect: $dialectName") + } + + poolName = "parcels" maximumPoolSize = dco.poolSize - dataSourceClassName = driver username = dco.username password = dco.password connectionTimeout = 15000 leakDetectionThreshold = 10000 connectionTestQuery = "SELECT 1" + + /* + addDataSourceProperty("serverName", address) addDataSourceProperty("port", port.toString()) addDataSourceProperty("databaseName", dco.database) @@ -32,7 +41,7 @@ fun getHikariDataSource(dialectName: String, dataSourceProperties.remove("port") dataSourceProperties.remove("databaseName") addDataSourceProperty("url", "jdbc:h2:${if (address.isBlank()) "" else "tcp://$address/"}~/${dco.database}") - } else { + } else if (dialectName.toLowerCase() == "mysql") { // doesn't exist on the MariaDB driver addDataSourceProperty("cachePrepStmts", "true") addDataSourceProperty("alwaysSendSetIsolation", "false") @@ -49,8 +58,7 @@ fun getHikariDataSource(dialectName: String, // make sure unicode characters can be used. addDataSourceProperty("characterEncoding", "utf8") addDataSourceProperty("useUnicode", "true") - } - - HikariDataSource(this) + } else { + }*/ } diff --git a/src/main/kotlin/io/dico/parcels2/storage/StorageFactory.kt b/src/main/kotlin/io/dico/parcels2/storage/StorageFactory.kt index 38c5f6d..13df2ed 100644 --- a/src/main/kotlin/io/dico/parcels2/storage/StorageFactory.kt +++ b/src/main/kotlin/io/dico/parcels2/storage/StorageFactory.kt @@ -1,5 +1,6 @@ package io.dico.parcels2.storage +import com.zaxxer.hikari.HikariDataSource import io.dico.parcels2.DataConnectionOptions import kotlin.reflect.KClass @@ -26,21 +27,16 @@ interface StorageFactory { class ConnectionStorageFactory : StorageFactory { override val optionsClass = DataConnectionOptions::class - - private val types: Map<String, String> = mutableMapOf( - "mysql" to "com.mysql.jdbc.jdbc2.optional.MysqlDataSource", - "h2" to "org.h2.jdbcx.JdbcDataSource" - ) + private val types: List<String> = listOf("postgresql") fun register(companion: StorageFactory.StorageFactories) { - types.keys.forEach { - companion.registerFactory(it, this) - } + types.forEach { companion.registerFactory(it, this) } } override fun newStorageInstance(dialect: String, options: Any): Storage { - val driverClass = types[dialect.toLowerCase()] ?: throw IllegalArgumentException("Storage dialect $dialect is not supported") - return StorageWithCoroutineBacking(ExposedBacking(getHikariDataSource(dialect, driverClass, options as DataConnectionOptions))) + val hikariConfig = getHikariConfig(dialect, options as DataConnectionOptions) + val dataSourceFactory = { HikariDataSource(hikariConfig) } + return StorageWithCoroutineBacking(ExposedBacking(dataSourceFactory)) } }
\ No newline at end of file |