=
+ LiteralParameter(name to value)
+ }
+}
diff --git a/subprojects/core-api/src/main/jvm/com/planetscale/jvm/param/ParameterSource.kt b/subprojects/core-api/src/main/jvm/com/planetscale/jvm/param/ParameterSource.kt
new file mode 100644
index 0000000..a692716
--- /dev/null
+++ b/subprojects/core-api/src/main/jvm/com/planetscale/jvm/param/ParameterSource.kt
@@ -0,0 +1,18 @@
+package com.planetscale.jvm.param
+
+/**
+ * TBD.
+ */
+public enum class ParameterSource {
+ /** The value originates from the driver URL. */
+ URL,
+
+ /** The value originates from environment variables. */
+ ENVIRONMENT,
+
+ /** The value originates from VM properties. */
+ VM_PROPERTY,
+
+ /** The value is a default or provided programmatically. */
+ PROGRAMMATIC,
+}
diff --git a/subprojects/core-api/src/main/jvm/com/planetscale/jvm/param/package-info.java b/subprojects/core-api/src/main/jvm/com/planetscale/jvm/param/package-info.java
new file mode 100644
index 0000000..4929dc9
--- /dev/null
+++ b/subprojects/core-api/src/main/jvm/com/planetscale/jvm/param/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Defines parameter-parsing logic for the Planetscale/J driver.
+ */
+package com.planetscale.jvm.param;
diff --git a/subprojects/core-api/src/main/jvm/com/planetscale/package-info.java b/subprojects/core-api/src/main/jvm/com/planetscale/package-info.java
new file mode 100644
index 0000000..4263c8d
--- /dev/null
+++ b/subprojects/core-api/src/main/jvm/com/planetscale/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Provides Planetscale-specific JDBC drivers and utilities.
+ */
+package com.planetscale;
diff --git a/subprojects/core-api/src/main/jvm/module-info.java b/subprojects/core-api/src/main/jvm/module-info.java
new file mode 100644
index 0000000..2199cf1
--- /dev/null
+++ b/subprojects/core-api/src/main/jvm/module-info.java
@@ -0,0 +1,16 @@
+/**
+ * # PlanetScale Connector/J
+ *
+ * Wraps the MySQL Connector for Java with functionality specific to Planetscale, which leverages MySQL-compatible
+ * cloud databasing via Vitess.
+ */
+module planetscale.connector {
+ requires java.base;
+ requires java.sql;
+ requires kotlin.stdlib;
+
+ exports com.planetscale.jvm;
+ exports com.planetscale.jvm.driver;
+
+ provides java.sql.Driver with com.planetscale.Driver;
+}
diff --git a/subprojects/core-api/src/main/resources/META-INF/services/java.sql.Driver b/subprojects/core-api/src/main/resources/META-INF/services/java.sql.Driver
new file mode 100644
index 0000000..ae325c9
--- /dev/null
+++ b/subprojects/core-api/src/main/resources/META-INF/services/java.sql.Driver
@@ -0,0 +1 @@
+com.planetscale.Driver
diff --git a/subprojects/core-api/src/main/resources/logback-test.xml b/subprojects/core-api/src/main/resources/logback-test.xml
new file mode 100644
index 0000000..203c544
--- /dev/null
+++ b/subprojects/core-api/src/main/resources/logback-test.xml
@@ -0,0 +1,13 @@
+
+
+ true
+
+ [%cyan(%d{HH:mm:ss.SSS}) %highlight(%5level)] %boldWhite(%logger) %msg%n
+
+
+
+
+
+
+
+
diff --git a/subprojects/core-api/src/test/kotlin/com/planetscale/DriverTest.kt b/subprojects/core-api/src/test/kotlin/com/planetscale/DriverTest.kt
new file mode 100644
index 0000000..a1ce466
--- /dev/null
+++ b/subprojects/core-api/src/test/kotlin/com/planetscale/DriverTest.kt
@@ -0,0 +1,50 @@
+package com.planetscale
+
+import org.junit.jupiter.api.assertDoesNotThrow
+import java.util.ServiceLoader
+import kotlin.test.Test
+import kotlin.test.assertNotEquals
+import kotlin.test.assertNotNull
+import kotlin.test.assertTrue
+
+/** Tests for the main driver facade, at [com.planetscale.Driver]. */
+class DriverTest {
+ private val testDriver: Driver by lazy {
+ Driver()
+ }
+
+ private fun withDriver(op: Driver.() -> Unit) {
+ assertNotNull(testDriver, "should be able to obtain test driver")
+ op.invoke(testDriver)
+ }
+
+ @Test fun testDriverConstruct() {
+ assertDoesNotThrow {
+ Driver()
+ }
+ }
+
+ @Test fun testDriverAPI() = withDriver {
+ assertNotNull(majorVersion, "majorVersion should not return `null`")
+ assertNotNull(minorVersion, "majorVersion should not return `null`")
+ }
+
+ @Test fun testDriverMajorVersion() = withDriver {
+ assertNotNull(majorVersion, "majorVersion should not return `null`")
+ assertNotEquals(0, majorVersion, "majorVersion should not be `0`")
+ }
+
+ @Test fun testDriverMinorVersion() = withDriver {
+ assertNotNull(minorVersion, "minorVersion should not return `null`")
+ assertNotEquals(0, minorVersion, "minorVersion should not be `0`")
+ }
+
+ @Test fun testDriverServiceLoader() {
+ val all = ServiceLoader.load(java.sql.Driver::class.java)
+ .toList()
+ .map { it::class }
+
+ assertNotEquals(0, all.size, "should have at least one driver")
+ assertTrue(all.contains(Driver::class), "should contain our driver")
+ }
+}
diff --git a/subprojects/core-api/src/test/kotlin/com/planetscale/jvm/PlanetscaleConfigTest.kt b/subprojects/core-api/src/test/kotlin/com/planetscale/jvm/PlanetscaleConfigTest.kt
new file mode 100644
index 0000000..3e779ac
--- /dev/null
+++ b/subprojects/core-api/src/test/kotlin/com/planetscale/jvm/PlanetscaleConfigTest.kt
@@ -0,0 +1,128 @@
+package com.planetscale.jvm
+
+import kotlin.test.*
+
+/** Tests for Planetscale configuration classes, like [PlanetscaleConfig]. */
+class PlanetscaleConfigTest {
+ @Test fun testConfigDefaults() {
+ val cfg = PlanetscaleConfig.defaults(
+ PlanetscaleConfig.parseUri("jdbc:planetscale://user:pass@us-west-1.aws/dbname"),
+ PlanetscaleCredential.user("dbname", "user", "pass"),
+ )
+ assertNotNull(cfg, "config should not be null")
+ assertNotNull(cfg.uri, "URI in config should not be null")
+ assertNotNull(cfg.credential, "credential in config should not be null")
+ assertNotNull(cfg.enableBoost(), "`enableBoost` should never return null")
+ assertNotNull(cfg.multiHost(), "`multiHost` should never return null")
+ assertNotNull(cfg.targetHosts(), "`targetHosts` should never return null")
+ assertTrue(cfg.targetHosts().isNotEmpty(), "`targetHosts` should not be empty")
+ assertEquals(cfg.credential.database, "dbname", "database name should be extracted correctly")
+ assertEquals(cfg.credential.username, "user", "username should be extracted correctly")
+ assertEquals(cfg.credential.password, "pass", "password should be extracted correctly")
+ assertEquals(cfg.targetHosts().size, 1, "should have 1 target host")
+ assertEquals(cfg.targetHosts().first(), "us-west-1.aws")
+ assertFalse(cfg.enableBoost(), "boost should default to false")
+ }
+
+ @Test fun testConfigManual() {
+ val cfg = PlanetscaleConfig.of(
+ PlanetscaleConfig.parseUri("jdbc:planetscale://user:pass@us-west-1.aws/dbname"),
+ PlanetscaleCredential.user("dbname", "user", "pass"),
+ )
+ assertNotNull(cfg, "config should not be null")
+ assertNotNull(cfg.uri, "URI in config should not be null")
+ assertNotNull(cfg.credential, "credential in config should not be null")
+ assertNotNull(cfg.enableBoost(), "`enableBoost` should never return null")
+ assertNotNull(cfg.multiHost(), "`multiHost` should never return null")
+ assertNotNull(cfg.targetHosts(), "`targetHosts` should never return null")
+ assertTrue(cfg.targetHosts().isNotEmpty(), "`targetHosts` should not be empty")
+ assertEquals(cfg.credential.database, "dbname", "database name should be extracted correctly")
+ assertEquals(cfg.credential.username, "user", "username should be extracted correctly")
+ assertEquals(cfg.credential.password, "pass", "password should be extracted correctly")
+ assertEquals(cfg.targetHosts().size, 1, "should have 1 target host")
+ assertEquals(cfg.targetHosts().first(), "us-west-1.aws")
+ assertFalse(cfg.enableBoost(), "boost should default to false")
+ }
+
+ @Test fun testConfigResolveFromUri() {
+ val cfg = PlanetscaleConfig.resolve(
+ PlanetscaleConfig.parseUri("jdbc:planetscale://user:pass@us-west-1.aws/dbname"),
+ )
+ assertNotNull(cfg, "config should not be null")
+ assertNotNull(cfg.uri, "URI in config should not be null")
+ assertNotNull(cfg.credential, "credential in config should not be null")
+ assertNotNull(cfg.enableBoost(), "`enableBoost` should never return null")
+ assertNotNull(cfg.multiHost(), "`multiHost` should never return null")
+ assertNotNull(cfg.targetHosts(), "`targetHosts` should never return null")
+ assertTrue(cfg.targetHosts().isNotEmpty(), "`targetHosts` should not be empty")
+ assertEquals(cfg.credential.database, "dbname", "database name should be extracted correctly")
+ assertEquals(cfg.credential.username, "user", "username should be extracted correctly")
+ assertEquals(cfg.credential.password, "pass", "password should be extracted correctly")
+ assertEquals(cfg.targetHosts().size, 1, "should have 1 target host")
+ assertEquals(cfg.targetHosts().first(), "us-west-1.aws")
+ assertFalse(cfg.enableBoost(), "boost should default to false")
+ }
+
+ @Test fun testConfigResolveFromUriMultiHost() {
+ val cfg = PlanetscaleConfig.resolve(
+ PlanetscaleConfig.parseUri("jdbc:planetscale://user:pass@us-west-1.aws..aws/dbname"),
+ )
+ assertNotNull(cfg, "config should not be null")
+ assertNotNull(cfg.uri, "URI in config should not be null")
+ assertNotNull(cfg.credential, "credential in config should not be null")
+ assertNotNull(cfg.enableBoost(), "`enableBoost` should never return null")
+ assertNotNull(cfg.multiHost(), "`multiHost` should never return null")
+ assertNotNull(cfg.targetHosts(), "`targetHosts` should never return null")
+ assertTrue(cfg.targetHosts().isNotEmpty(), "`targetHosts` should not be empty")
+ assertEquals(cfg.credential.database, "dbname", "database name should be extracted correctly")
+ assertEquals(cfg.credential.username, "user", "username should be extracted correctly")
+ assertEquals(cfg.credential.password, "pass", "password should be extracted correctly")
+ assertEquals(cfg.targetHosts().size, 2, "should have 2 target hosts")
+ assertEquals(cfg.targetHosts().first(), "us-west-1.aws")
+ assertEquals(cfg.targetHosts()[1], "aws")
+ assertFalse(cfg.enableBoost(), "boost should default to false")
+ }
+
+ @Test fun testConfigKnownOptions() {
+ val cfg = PlanetscaleConfig.resolve(
+ PlanetscaleConfig.parseUri("jdbc:planetscale://user:pass@us-west-1.aws..aws/dbname?enableBoost=true"),
+ )
+ assertNotNull(cfg, "config should not be null")
+ assertNotNull(cfg.uri, "URI in config should not be null")
+ assertNotNull(cfg.credential, "credential in config should not be null")
+ assertNotNull(cfg.enableBoost(), "`enableBoost` should never return null")
+ assertNotNull(cfg.multiHost(), "`multiHost` should never return null")
+ assertNotNull(cfg.targetHosts(), "`targetHosts` should never return null")
+ assertTrue(cfg.targetHosts().isNotEmpty(), "`targetHosts` should not be empty")
+ assertEquals(cfg.credential.database, "dbname", "database name should be extracted correctly")
+ assertEquals(cfg.credential.username, "user", "username should be extracted correctly")
+ assertEquals(cfg.credential.password, "pass", "password should be extracted correctly")
+ assertEquals(cfg.targetHosts().size, 2, "should have 2 target hosts")
+ assertEquals(cfg.targetHosts().first(), "us-west-1.aws")
+ assertEquals(cfg.targetHosts()[1], "aws")
+ assertTrue(cfg.enableBoost(), "boost should be active")
+ }
+
+ @Test fun testConfigPassthrough() {
+ val cfg = PlanetscaleConfig.resolve(
+ PlanetscaleConfig.parseUri("jdbc:planetscale://user:pass@us-west-1.aws..aws/dbname?someParam=hi"),
+ )
+ assertNotNull(cfg, "config should not be null")
+ assertNotNull(cfg.uri, "URI in config should not be null")
+ assertNotNull(cfg.credential, "credential in config should not be null")
+ assertNotNull(cfg.enableBoost(), "`enableBoost` should never return null")
+ assertNotNull(cfg.multiHost(), "`multiHost` should never return null")
+ assertNotNull(cfg.targetHosts(), "`targetHosts` should never return null")
+ assertTrue(cfg.targetHosts().isNotEmpty(), "`targetHosts` should not be empty")
+ assertEquals(cfg.credential.database, "dbname", "database name should be extracted correctly")
+ assertEquals(cfg.credential.username, "user", "username should be extracted correctly")
+ assertEquals(cfg.credential.password, "pass", "password should be extracted correctly")
+ assertEquals(cfg.targetHosts().size, 2, "should have 2 target hosts")
+ assertEquals(cfg.targetHosts().first(), "us-west-1.aws")
+ assertEquals(cfg.targetHosts()[1], "aws")
+ assertFalse(cfg.enableBoost(), "boost should default to false")
+ assertTrue(cfg.extraParams().isNotEmpty(), "should have extra params")
+ assertTrue(cfg.extraParams().contains("someParam"), "unrecognized param should be in `someParams`")
+ assertEquals(cfg.extraParams()["someParam"], "hi", "`someParam` should be set to `hi`")
+ }
+}
diff --git a/subprojects/driver/build.gradle.kts b/subprojects/driver/build.gradle.kts
new file mode 100644
index 0000000..1b74b83
--- /dev/null
+++ b/subprojects/driver/build.gradle.kts
@@ -0,0 +1,16 @@
+import PlanetscaleBuild.publishable
+
+plugins {
+ id("common-conventions.kotlin")
+ id("planetscale-publishable.klib")
+}
+
+dependencies {
+ api(projects.subprojects.coreApi)
+ implementation(projects.subprojects.implMysqlj)
+}
+
+publishable(
+ name = "planetscale-jvm",
+ description = "Planetscale JDBC meta-driver",
+)
diff --git a/subprojects/driver/gradle.lockfile b/subprojects/driver/gradle.lockfile
new file mode 100644
index 0000000..496aa7c
--- /dev/null
+++ b/subprojects/driver/gradle.lockfile
@@ -0,0 +1,9 @@
+# This is a Gradle generated file for dependency locking.
+# Manual edits can break the build and are not advised.
+# This file is expected to be part of source control.
+com.google.protobuf:protobuf-java:3.21.9=runtimeClasspath
+com.mysql:mysql-connector-j:8.2.0=runtimeClasspath
+org.jetbrains.kotlin:kotlin-stdlib:1.9.20=compileClasspath,runtimeClasspath
+org.jetbrains:annotations:13.0=compileClasspath,runtimeClasspath
+org.slf4j:slf4j-api:2.0.9=compileClasspath,runtimeClasspath
+empty=
diff --git a/subprojects/impl-h2/build.gradle.kts b/subprojects/impl-h2/build.gradle.kts
new file mode 100644
index 0000000..81404c1
--- /dev/null
+++ b/subprojects/impl-h2/build.gradle.kts
@@ -0,0 +1,12 @@
+plugins {
+ id("planetscale-connector.klib")
+}
+
+dependencies {
+ api(projects.subprojects.coreApi)
+ implementation(libs.bundles.h2)
+}
+
+kotlin {
+ compilerOptions.moduleName = "planetscale.connector.inmemory"
+}
diff --git a/subprojects/impl-h2/gradle.lockfile b/subprojects/impl-h2/gradle.lockfile
new file mode 100644
index 0000000..22bbebf
--- /dev/null
+++ b/subprojects/impl-h2/gradle.lockfile
@@ -0,0 +1,8 @@
+# This is a Gradle generated file for dependency locking.
+# Manual edits can break the build and are not advised.
+# This file is expected to be part of source control.
+com.h2database:h2:2.2.224=compileClasspath,runtimeClasspath
+org.jetbrains.kotlin:kotlin-stdlib:1.9.20=compileClasspath,runtimeClasspath
+org.jetbrains:annotations:13.0=compileClasspath,runtimeClasspath
+org.slf4j:slf4j-api:2.0.9=compileClasspath,runtimeClasspath
+empty=
diff --git a/subprojects/impl-h2/src/main/jvm/com/planetscale/jvm/h2/PlanetscaleH2Driver.kt b/subprojects/impl-h2/src/main/jvm/com/planetscale/jvm/h2/PlanetscaleH2Driver.kt
new file mode 100644
index 0000000..e60fd26
--- /dev/null
+++ b/subprojects/impl-h2/src/main/jvm/com/planetscale/jvm/h2/PlanetscaleH2Driver.kt
@@ -0,0 +1,35 @@
+package com.planetscale.jvm.h2
+
+import com.planetscale.jvm.PlanetscaleConfig
+import com.planetscale.jvm.driver.AbstractPlanetscaleAdapter
+import com.planetscale.jvm.driver.Constants
+import java.net.URI
+import java.sql.Connection
+import java.sql.Driver
+import java.sql.DriverManager
+import java.sql.DriverPropertyInfo
+import java.util.*
+import java.util.logging.Logger
+
+/**
+ * TBD.
+ */
+public class PlanetscaleH2Driver : AbstractPlanetscaleAdapter() {
+ private companion object {
+ private const val H2_DRIVER = "org.h2.Driver"
+ }
+
+ override fun PlanetscaleConfig.toURI(): URI {
+ return URI.create(
+ "jdbc:h2:mem:${credential.database};MODE=MySQL;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE;"
+ )
+ }
+
+ override fun createDriver(): Driver {
+ return DriverManager.drivers().filter {
+ it.javaClass.canonicalName == H2_DRIVER
+ }.findFirst().orElse(null) ?: error(
+ "Failed to resolve H2 driver: check your classpath?"
+ )
+ }
+}
diff --git a/subprojects/impl-h2/src/main/jvm/module-info.java b/subprojects/impl-h2/src/main/jvm/module-info.java
new file mode 100644
index 0000000..191a8ee
--- /dev/null
+++ b/subprojects/impl-h2/src/main/jvm/module-info.java
@@ -0,0 +1,9 @@
+/**
+ * Planetscale/J: H2-based implementation.
+ */
+module planetscale.connector.inmemory {
+ requires java.base;
+ requires java.sql;
+ requires kotlin.stdlib;
+ requires planetscale.connector;
+}
diff --git a/subprojects/impl-h2/src/main/resources/META-INF/native-image/proxy-config.json b/subprojects/impl-h2/src/main/resources/META-INF/native-image/proxy-config.json
new file mode 100644
index 0000000..fbfaf87
--- /dev/null
+++ b/subprojects/impl-h2/src/main/resources/META-INF/native-image/proxy-config.json
@@ -0,0 +1,5 @@
+[
+ {
+ "interfaces":["java.sql.Connection"]
+ }
+]
\ No newline at end of file
diff --git a/subprojects/impl-h2/src/main/resources/META-INF/native-image/reflect-config.json b/subprojects/impl-h2/src/main/resources/META-INF/native-image/reflect-config.json
new file mode 100644
index 0000000..99e9a90
--- /dev/null
+++ b/subprojects/impl-h2/src/main/resources/META-INF/native-image/reflect-config.json
@@ -0,0 +1,5 @@
+[
+ {
+ "name":"com.planetscale.Driver"
+ }
+]
\ No newline at end of file
diff --git a/subprojects/impl-h2/src/main/resources/META-INF/native-image/resource-config.json b/subprojects/impl-h2/src/main/resources/META-INF/native-image/resource-config.json
new file mode 100644
index 0000000..a271675
--- /dev/null
+++ b/subprojects/impl-h2/src/main/resources/META-INF/native-image/resource-config.json
@@ -0,0 +1,12 @@
+{
+ "resources": {
+ "includes": [
+ {
+ "pattern": "\\QMETA-INF/services/java.sql.Driver\\E"
+ },
+ {
+ "pattern": "\\QMETA-INF/services/com.planetscale.jvm.PlanetscaleAdapter\\E"
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/subprojects/impl-h2/src/main/resources/META-INF/services/com.planetscale.jvm.PlanetscaleAdapter b/subprojects/impl-h2/src/main/resources/META-INF/services/com.planetscale.jvm.PlanetscaleAdapter
new file mode 100644
index 0000000..a6a0a7c
--- /dev/null
+++ b/subprojects/impl-h2/src/main/resources/META-INF/services/com.planetscale.jvm.PlanetscaleAdapter
@@ -0,0 +1 @@
+com.planetscale.jvm.h2.PlanetscaleH2Driver
diff --git a/subprojects/impl-mysqlj/build.gradle.kts b/subprojects/impl-mysqlj/build.gradle.kts
new file mode 100644
index 0000000..5e51678
--- /dev/null
+++ b/subprojects/impl-mysqlj/build.gradle.kts
@@ -0,0 +1,22 @@
+import PlanetscaleBuild.publishable
+
+plugins {
+ id("planetscale-connector.klib")
+ alias(libs.plugins.sonar)
+ alias(libs.plugins.testLogger)
+ alias(libs.plugins.kover)
+}
+
+dependencies {
+ api(projects.subprojects.coreApi)
+ implementation(libs.bundles.mysql)
+}
+
+kotlin {
+ compilerOptions.moduleName = "planetscale.connector.mysqlj"
+}
+
+publishable(
+ name = "planetscale-mysqlj",
+ description = "Planetscale driver MySQL/J implementation (internal)",
+)
diff --git a/subprojects/impl-mysqlj/gradle.lockfile b/subprojects/impl-mysqlj/gradle.lockfile
new file mode 100644
index 0000000..82918d5
--- /dev/null
+++ b/subprojects/impl-mysqlj/gradle.lockfile
@@ -0,0 +1,9 @@
+# This is a Gradle generated file for dependency locking.
+# Manual edits can break the build and are not advised.
+# This file is expected to be part of source control.
+com.google.protobuf:protobuf-java:3.21.9=compileClasspath,runtimeClasspath
+com.mysql:mysql-connector-j:8.2.0=compileClasspath,runtimeClasspath
+org.jetbrains.kotlin:kotlin-stdlib:1.9.20=compileClasspath,runtimeClasspath
+org.jetbrains:annotations:13.0=compileClasspath,runtimeClasspath
+org.slf4j:slf4j-api:2.0.9=compileClasspath,runtimeClasspath
+empty=
diff --git a/subprojects/impl-mysqlj/src/main/jvm/com/planetscale/jvm/mysqlj/PlanetscaleMysqlDriver.kt b/subprojects/impl-mysqlj/src/main/jvm/com/planetscale/jvm/mysqlj/PlanetscaleMysqlDriver.kt
new file mode 100644
index 0000000..69581e6
--- /dev/null
+++ b/subprojects/impl-mysqlj/src/main/jvm/com/planetscale/jvm/mysqlj/PlanetscaleMysqlDriver.kt
@@ -0,0 +1,160 @@
+package com.planetscale.jvm.mysqlj
+
+import com.planetscale.jvm.PlanetscaleConfig
+import com.planetscale.jvm.PlanetscaleCredential
+import com.planetscale.jvm.PlanetscaleParameter
+import com.planetscale.jvm.driver.AbstractPlanetscaleAdapter
+import com.planetscale.jvm.driver.CloudProvider
+import com.planetscale.jvm.driver.Constants
+import java.net.URI
+import java.sql.Connection
+import java.sql.Driver
+import java.sql.DriverManager
+import java.sql.DriverPropertyInfo
+import java.util.*
+import java.util.logging.Logger
+
+/**
+ * TBD.
+ */
+public class PlanetscaleMysqlDriver : AbstractPlanetscaleAdapter() {
+ public companion object {
+ private val defaultParams = mapOf(
+ Constants.DriverParams.AUTO_RECONNECT to Constants.StringValue.TRUE,
+ Constants.DriverParams.CACHE_SERVER_CONFIG to Constants.StringValue.TRUE,
+ Constants.DriverParams.CACHE_RESULT_SET_METADATA to Constants.StringValue.TRUE,
+ Constants.DriverParams.ROUND_ROBIN_LOAD_BALANCE to Constants.StringValue.TRUE,
+ Constants.DriverParams.SSL_MODE to Constants.DriverParams.SSL_MODE_VERIFY,
+ )
+
+ // Return a qualified Planetscale endpoint.
+ @JvmStatic public fun qualifiedEndpoint(qualifier: String): String = "$qualifier.${Constants.CONNECT_DOMAIN}"
+
+ // Return a qualified Planetscale endpoint for AWS.
+ @JvmStatic public fun awsEndpoint(qualifier: String? = null): String = when (qualifier?.ifBlank { null }) {
+ null -> qualifiedEndpoint(Constants.Provider.AWS)
+ else -> qualifiedEndpoint("${Constants.Provider.AWS}.$qualifier")
+ }
+
+ // Return a qualified Planetscale endpoint for GCP.
+ @JvmStatic public fun gcpEndpoint(qualifier: String? = null): String = when (qualifier?.ifBlank { null }) {
+ null -> qualifiedEndpoint(Constants.Provider.GCP)
+ else -> qualifiedEndpoint("${Constants.Provider.GCP}.$qualifier")
+ }
+
+ // Resolve the active cloud provider, if any.
+ @JvmStatic public fun detectProvider(): CloudProvider? {
+ return listOf(
+ Constants.Provider.GCP_CREDS_VAR to CloudProvider.GCP,
+ Constants.Provider.AWS_CREDS_VAR to CloudProvider.AWS,
+ ).firstOrNull {
+ System.getenv(it.first) != null
+ }?.second
+ }
+
+ // Resolve symbolic host names to their actual targets.
+ @JvmStatic public fun resolveHostSymbols(target: String): String {
+ // safeguard: if it contains more than one period, it's a hostname
+ return if (target.count { it == '.' } > 1) target else when (target) {
+ // covers literal `aws`
+ Constants.SymbolicHosts.AWS -> awsEndpoint()
+
+ // covers literal `gcp`
+ Constants.SymbolicHosts.GCP -> gcpEndpoint()
+
+ // dynamic resolution
+ Constants.SymbolicHosts.CLOUD -> detectProvider().let {
+ when (it) {
+ CloudProvider.AWS -> awsEndpoint()
+ CloudProvider.GCP -> gcpEndpoint()
+ else -> error("Unable to resolve Planetscale database endpoint")
+ }
+ }
+
+ else -> when {
+ // covers `.aws`
+ target.endsWith(Constants.SymbolicHosts.AWS) -> awsEndpoint(
+ target.removeSuffix(Constants.SymbolicHosts.AWS)
+ )
+
+ // covers `.gcp`
+ target.endsWith(Constants.SymbolicHosts.GCP) -> awsEndpoint(
+ target.removeSuffix(Constants.SymbolicHosts.GCP)
+ )
+
+ // otherwise, we don't recognize it, and it should be preserved
+ else -> target
+ }
+ }
+ }
+ }
+
+ private fun PlanetscaleCredential.toUriInfo(): StringBuilder = StringBuilder().apply {
+ append(username)
+ append(":")
+ append(password)
+ }
+
+ private fun StringBuilder.appendHost(host: String) {
+ append(host)
+ append(":")
+ append(Constants.MYSQL_PORT)
+ }
+
+ private fun PlanetscaleConfig.endpoints(): StringBuilder = StringBuilder().apply {
+ if (multiHost()) {
+ append(targetHosts().map {
+ StringBuilder().appendHost(resolveHostSymbols(it))
+ }.joinToString(","))
+ } else {
+ appendHost(targetHosts().first())
+ }
+ }
+
+ private fun PlanetscaleConfig.sessionVariables(): List>? {
+ return if (!enableBoost()) null else listOf(
+ PlanetscaleParameter.EnableBoost.option.paramName to Constants.StringValue.TRUE,
+ )
+ }
+
+ private fun PlanetscaleConfig.driverParameters(): List> {
+ return defaultParams.plus(extraParams().mapNotNull {
+ if (it.value.isNullOrEmpty() || it.value.isBlank()) {
+ null
+ } else {
+ it.key to it.value
+ }
+ }).toSortedMap().toList().plus(when (val sessionVariables = sessionVariables()) {
+ null -> emptyList()
+ else -> listOf(
+ Constants.DriverParams.SESSION_VARIABLES to
+ sessionVariables.joinToString(separator = ",") { (k, v) -> "$k=$v" }
+ )
+ })
+ }
+
+ override fun PlanetscaleConfig.toURI(): URI {
+ return URI.create(StringBuilder().apply {
+ append(Constants.Prefix.MYSQL)
+ if (multiHost()) append(Constants.Prefix.REPLICATION)
+ append("//")
+ append(credential.toUriInfo())
+ append("@")
+ append(endpoints())
+ append("/")
+ append(credential.database)
+ append("?")
+ driverParameters().joinToString("&") { (k, v) ->
+ append(k)
+ append("=")
+ append(v)
+ }
+ }.toString())
+ }
+
+ override fun createDriver(): Driver = DriverManager.drivers().filter {
+ it.javaClass.canonicalName == Constants.MYSQL_DRIVER
+ }.findFirst().orElse(null) ?: error(
+ "Failed to resolve MySQL driver: check your classpath?"
+ )
+}
diff --git a/subprojects/impl-mysqlj/src/main/jvm/module-info.java b/subprojects/impl-mysqlj/src/main/jvm/module-info.java
new file mode 100644
index 0000000..33c05ed
--- /dev/null
+++ b/subprojects/impl-mysqlj/src/main/jvm/module-info.java
@@ -0,0 +1,9 @@
+/**
+ * Planetscale/J: MySQL-based implementation.
+ */
+module planetscale.connector.mysqlj {
+ requires java.base;
+ requires java.sql;
+ requires kotlin.stdlib;
+ requires planetscale.connector;
+}
diff --git a/subprojects/impl-mysqlj/src/main/resources/META-INF/services/com.planetscale.jvm.PlanetscaleAdapter b/subprojects/impl-mysqlj/src/main/resources/META-INF/services/com.planetscale.jvm.PlanetscaleAdapter
new file mode 100644
index 0000000..d9b37a5
--- /dev/null
+++ b/subprojects/impl-mysqlj/src/main/resources/META-INF/services/com.planetscale.jvm.PlanetscaleAdapter
@@ -0,0 +1 @@
+com.planetscale.jvm.mysqlj.PlanetscaleMysqlDriver
diff --git a/subprojects/integration-graalvm/build.gradle.kts b/subprojects/integration-graalvm/build.gradle.kts
new file mode 100644
index 0000000..a34d52a
--- /dev/null
+++ b/subprojects/integration-graalvm/build.gradle.kts
@@ -0,0 +1,23 @@
+import PlanetscaleBuild.publishable
+
+plugins {
+ id("planetscale-connector.klib")
+ alias(libs.plugins.ksp)
+ alias(libs.plugins.kover)
+ alias(libs.plugins.micronaut.library)
+ alias(libs.plugins.sonar)
+ alias(libs.plugins.testLogger)
+}
+
+dependencies {
+ api(projects.subprojects.coreApi)
+}
+
+kotlin {
+ compilerOptions.moduleName = "planetscale.graalvm"
+}
+
+publishable(
+ name = "planetscale-graalvm",
+ description = "Planetscale/GraalVM integration library",
+)
diff --git a/subprojects/integration-graalvm/gradle.lockfile b/subprojects/integration-graalvm/gradle.lockfile
new file mode 100644
index 0000000..8a77958
--- /dev/null
+++ b/subprojects/integration-graalvm/gradle.lockfile
@@ -0,0 +1,101 @@
+# This is a Gradle generated file for dependency locking.
+# Manual edits can break the build and are not advised.
+# This file is expected to be part of source control.
+com.amazonaws:aws-java-sdk-bom:1.12.566=compileClasspath,runtimeClasspath
+com.azure:azure-sdk-bom:1.2.14=compileClasspath,runtimeClasspath
+com.fasterxml.jackson:jackson-bom:2.15.2=compileClasspath,runtimeClasspath
+com.google.protobuf:protobuf-bom:3.23.4=compileClasspath,runtimeClasspath
+com.oracle.coherence.ce:coherence-bom:23.03=compileClasspath,runtimeClasspath
+io.grpc:grpc-bom:1.56.1=compileClasspath,runtimeClasspath
+io.ktor:ktor-bom:2.3.4=compileClasspath,runtimeClasspath
+io.micrometer:micrometer-bom:1.11.1=compileClasspath,runtimeClasspath
+io.micronaut.acme:micronaut-acme-bom:4.0.1=compileClasspath,runtimeClasspath
+io.micronaut.aot:micronaut-aot-bom:2.1.1=compileClasspath,runtimeClasspath
+io.micronaut.aws:micronaut-aws-bom:4.0.7=compileClasspath,runtimeClasspath
+io.micronaut.azure:micronaut-azure-bom:5.0.1=compileClasspath,runtimeClasspath
+io.micronaut.beanvalidation:micronaut-hibernate-validator-bom:4.0.2=compileClasspath,runtimeClasspath
+io.micronaut.cache:micronaut-cache-bom:4.0.2=compileClasspath,runtimeClasspath
+io.micronaut.cassandra:micronaut-cassandra-bom:6.0.2=compileClasspath,runtimeClasspath
+io.micronaut.chatbots:micronaut-chatbots-bom:2.0.0-M6=compileClasspath,runtimeClasspath
+io.micronaut.coherence:micronaut-coherence-bom:4.0.0=compileClasspath,runtimeClasspath
+io.micronaut.controlpanel:micronaut-control-panel-bom:1.0.1=compileClasspath,runtimeClasspath
+io.micronaut.crac:micronaut-crac-bom:2.0.3=compileClasspath,runtimeClasspath
+io.micronaut.data:micronaut-data-bom:4.1.4=compileClasspath,runtimeClasspath
+io.micronaut.discovery:micronaut-discovery-client-bom:4.0.3=compileClasspath,runtimeClasspath
+io.micronaut.elasticsearch:micronaut-elasticsearch-bom:5.0.1=compileClasspath,runtimeClasspath
+io.micronaut.email:micronaut-email-bom:2.2.1=compileClasspath,runtimeClasspath
+io.micronaut.flyway:micronaut-flyway-bom:6.1.0=compileClasspath,runtimeClasspath
+io.micronaut.gcp:micronaut-gcp-bom:5.0.4=compileClasspath,runtimeClasspath
+io.micronaut.graphql:micronaut-graphql-bom:4.0.2=compileClasspath,runtimeClasspath
+io.micronaut.groovy:micronaut-groovy-bom:4.0.1=compileClasspath,runtimeClasspath
+io.micronaut.grpc:micronaut-grpc-bom:4.0.1=compileClasspath,runtimeClasspath
+io.micronaut.jaxrs:micronaut-jaxrs-bom:4.0.2=compileClasspath,runtimeClasspath
+io.micronaut.jms:micronaut-jms-bom:3.0.1=compileClasspath,runtimeClasspath
+io.micronaut.jmx:micronaut-jmx-bom:4.0.1=compileClasspath,runtimeClasspath
+io.micronaut.kafka:micronaut-kafka-bom:5.1.2=compileClasspath,runtimeClasspath
+io.micronaut.kotlin:micronaut-kotlin-bom:4.0.2=compileClasspath,runtimeClasspath
+io.micronaut.kubernetes:micronaut-kubernetes-bom:5.0.1=compileClasspath,runtimeClasspath
+io.micronaut.liquibase:micronaut-liquibase-bom:6.0.2=compileClasspath,runtimeClasspath
+io.micronaut.logging:micronaut-logging-bom:1.1.2=compileClasspath,runtimeClasspath
+io.micronaut.micrometer:micronaut-micrometer-bom:5.0.1=compileClasspath,runtimeClasspath
+io.micronaut.microstream:micronaut-microstream-bom:2.0.2=compileClasspath,runtimeClasspath
+io.micronaut.mongodb:micronaut-mongo-bom:5.0.1=compileClasspath,runtimeClasspath
+io.micronaut.mqtt:micronaut-mqtt-bom:3.0.2=compileClasspath,runtimeClasspath
+io.micronaut.multitenancy:micronaut-multitenancy-bom:5.0.3=compileClasspath,runtimeClasspath
+io.micronaut.nats:micronaut-nats-bom:4.0.1=compileClasspath,runtimeClasspath
+io.micronaut.neo4j:micronaut-neo4j-bom:6.1.0=compileClasspath,runtimeClasspath
+io.micronaut.objectstorage:micronaut-object-storage-bom:2.0.4=compileClasspath,runtimeClasspath
+io.micronaut.openapi:micronaut-openapi-bom:5.1.1=compileClasspath,runtimeClasspath
+io.micronaut.oraclecloud:micronaut-oraclecloud-bom:3.0.6=compileClasspath,runtimeClasspath
+io.micronaut.picocli:micronaut-picocli-bom:5.0.2=compileClasspath,runtimeClasspath
+io.micronaut.platform:micronaut-platform:4.1.6=compileClasspath,runtimeClasspath
+io.micronaut.problem:micronaut-problem-json-bom:3.0.2=compileClasspath,runtimeClasspath
+io.micronaut.pulsar:micronaut-pulsar-bom:2.0.1=compileClasspath,runtimeClasspath
+io.micronaut.r2dbc:micronaut-r2dbc-bom:5.0.1=compileClasspath,runtimeClasspath
+io.micronaut.rabbitmq:micronaut-rabbitmq-bom:4.1.0=compileClasspath,runtimeClasspath
+io.micronaut.reactor:micronaut-reactor-bom:3.0.5=compileClasspath,runtimeClasspath
+io.micronaut.redis:micronaut-redis-bom:6.0.2=compileClasspath,runtimeClasspath
+io.micronaut.rss:micronaut-rss-bom:4.0.1=compileClasspath,runtimeClasspath
+io.micronaut.rxjava2:micronaut-rxjava2-bom:2.0.1=compileClasspath,runtimeClasspath
+io.micronaut.rxjava3:micronaut-rxjava3-bom:3.0.2=compileClasspath,runtimeClasspath
+io.micronaut.security:micronaut-security-bom:4.1.0=compileClasspath,runtimeClasspath
+io.micronaut.serde:micronaut-serde-bom:2.2.6=compileClasspath,runtimeClasspath
+io.micronaut.servlet:micronaut-servlet-bom:4.1.1=compileClasspath,runtimeClasspath
+io.micronaut.session:micronaut-session-bom:4.0.0=compileClasspath,runtimeClasspath
+io.micronaut.spring:micronaut-spring-bom:5.0.2=compileClasspath,runtimeClasspath
+io.micronaut.sql:micronaut-sql-bom:5.0.3=compileClasspath,runtimeClasspath
+io.micronaut.test:micronaut-test-bom:4.0.2=compileClasspath,runtimeClasspath
+io.micronaut.testresources:micronaut-test-resources-bom:2.0.0=compileClasspath,runtimeClasspath
+io.micronaut.toml:micronaut-toml-bom:2.0.1=compileClasspath,runtimeClasspath
+io.micronaut.tracing:micronaut-tracing-bom:5.0.2=compileClasspath,runtimeClasspath
+io.micronaut.validation:micronaut-validation-bom:4.0.3=compileClasspath,runtimeClasspath
+io.micronaut.views:micronaut-views-bom:4.0.1=compileClasspath,runtimeClasspath
+io.micronaut.xml:micronaut-jackson-xml-bom:4.0.1=compileClasspath,runtimeClasspath
+io.micronaut:micronaut-core-bom:4.1.11=compileClasspath,runtimeClasspath
+io.micronaut:micronaut-core:4.1.11=compileClasspath,runtimeClasspath
+io.micronaut:micronaut-inject:4.1.11=compileClasspath,runtimeClasspath
+io.netty:netty-bom:4.1.100.Final=compileClasspath,runtimeClasspath
+io.opentelemetry.instrumentation:opentelemetry-instrumentation-bom-alpha:1.26.0-alpha=compileClasspath,runtimeClasspath
+io.opentelemetry.instrumentation:opentelemetry-instrumentation-bom:1.26.0=compileClasspath,runtimeClasspath
+io.opentelemetry:opentelemetry-bom-alpha:1.26.0-alpha=compileClasspath,runtimeClasspath
+io.opentelemetry:opentelemetry-bom:1.26.0=compileClasspath,runtimeClasspath
+io.projectreactor:reactor-bom:2022.0.12=compileClasspath,runtimeClasspath
+io.rest-assured:rest-assured-bom:5.3.2=compileClasspath,runtimeClasspath
+io.zipkin.brave:brave-bom:5.16.0=compileClasspath,runtimeClasspath
+io.zipkin.reporter2:zipkin-reporter-bom:2.16.4=compileClasspath,runtimeClasspath
+jakarta.annotation:jakarta.annotation-api:2.1.1=compileClasspath,runtimeClasspath
+jakarta.inject:jakarta.inject-api:2.0.1=compileClasspath,runtimeClasspath
+org.apache.groovy:groovy-bom:4.0.14=compileClasspath,runtimeClasspath
+org.apache.logging.log4j:log4j-bom:2.20.0=compileClasspath,runtimeClasspath
+org.jdbi:jdbi3-bom:3.39.1=compileClasspath,runtimeClasspath
+org.jetbrains.kotlin:kotlin-bom:1.8.21=compileClasspath,runtimeClasspath
+org.jetbrains.kotlin:kotlin-stdlib:1.9.20=compileClasspath,runtimeClasspath
+org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3=compileClasspath,runtimeClasspath
+org.jetbrains:annotations:13.0=compileClasspath,runtimeClasspath
+org.junit:junit-bom:5.9.3=compileClasspath,runtimeClasspath
+org.slf4j:slf4j-api:2.0.9=compileClasspath,runtimeClasspath
+org.spockframework:spock-bom:2.3-groovy-4.0=compileClasspath,runtimeClasspath
+org.springframework:spring-framework-bom:6.0.11=compileClasspath,runtimeClasspath
+org.testcontainers:testcontainers-bom:1.19.1=compileClasspath,runtimeClasspath
+software.amazon.awssdk:bom:2.20.162=compileClasspath,runtimeClasspath
+empty=
diff --git a/subprojects/integration-graalvm/src/main/jvm/com/planetscale/PlanetscaleFeature.kt b/subprojects/integration-graalvm/src/main/jvm/com/planetscale/PlanetscaleFeature.kt
new file mode 100644
index 0000000..ed8adea
--- /dev/null
+++ b/subprojects/integration-graalvm/src/main/jvm/com/planetscale/PlanetscaleFeature.kt
@@ -0,0 +1,8 @@
+package com.planetscale
+
+/**
+ * TBD.
+ */
+public class PlanetscaleFeature {
+ // Nothing yet
+}
diff --git a/subprojects/integration-micronaut/build.gradle.kts b/subprojects/integration-micronaut/build.gradle.kts
new file mode 100644
index 0000000..490b727
--- /dev/null
+++ b/subprojects/integration-micronaut/build.gradle.kts
@@ -0,0 +1,24 @@
+import PlanetscaleBuild.publishable
+
+plugins {
+ id("planetscale-connector.klib")
+ alias(libs.plugins.ksp)
+ alias(libs.plugins.kover)
+ alias(libs.plugins.micronaut.library)
+ alias(libs.plugins.sonar)
+ alias(libs.plugins.testLogger)
+}
+
+dependencies {
+ api(projects.subprojects.coreApi)
+ ksp(mn.micronaut.inject.kotlin)
+}
+
+kotlin {
+ compilerOptions.moduleName = "planetscale.micronaut"
+}
+
+publishable(
+ name = "planetscale-micronaut",
+ description = "Planetscale/Micronaut integration library",
+)
diff --git a/subprojects/integration-micronaut/gradle.lockfile b/subprojects/integration-micronaut/gradle.lockfile
new file mode 100644
index 0000000..8a77958
--- /dev/null
+++ b/subprojects/integration-micronaut/gradle.lockfile
@@ -0,0 +1,101 @@
+# This is a Gradle generated file for dependency locking.
+# Manual edits can break the build and are not advised.
+# This file is expected to be part of source control.
+com.amazonaws:aws-java-sdk-bom:1.12.566=compileClasspath,runtimeClasspath
+com.azure:azure-sdk-bom:1.2.14=compileClasspath,runtimeClasspath
+com.fasterxml.jackson:jackson-bom:2.15.2=compileClasspath,runtimeClasspath
+com.google.protobuf:protobuf-bom:3.23.4=compileClasspath,runtimeClasspath
+com.oracle.coherence.ce:coherence-bom:23.03=compileClasspath,runtimeClasspath
+io.grpc:grpc-bom:1.56.1=compileClasspath,runtimeClasspath
+io.ktor:ktor-bom:2.3.4=compileClasspath,runtimeClasspath
+io.micrometer:micrometer-bom:1.11.1=compileClasspath,runtimeClasspath
+io.micronaut.acme:micronaut-acme-bom:4.0.1=compileClasspath,runtimeClasspath
+io.micronaut.aot:micronaut-aot-bom:2.1.1=compileClasspath,runtimeClasspath
+io.micronaut.aws:micronaut-aws-bom:4.0.7=compileClasspath,runtimeClasspath
+io.micronaut.azure:micronaut-azure-bom:5.0.1=compileClasspath,runtimeClasspath
+io.micronaut.beanvalidation:micronaut-hibernate-validator-bom:4.0.2=compileClasspath,runtimeClasspath
+io.micronaut.cache:micronaut-cache-bom:4.0.2=compileClasspath,runtimeClasspath
+io.micronaut.cassandra:micronaut-cassandra-bom:6.0.2=compileClasspath,runtimeClasspath
+io.micronaut.chatbots:micronaut-chatbots-bom:2.0.0-M6=compileClasspath,runtimeClasspath
+io.micronaut.coherence:micronaut-coherence-bom:4.0.0=compileClasspath,runtimeClasspath
+io.micronaut.controlpanel:micronaut-control-panel-bom:1.0.1=compileClasspath,runtimeClasspath
+io.micronaut.crac:micronaut-crac-bom:2.0.3=compileClasspath,runtimeClasspath
+io.micronaut.data:micronaut-data-bom:4.1.4=compileClasspath,runtimeClasspath
+io.micronaut.discovery:micronaut-discovery-client-bom:4.0.3=compileClasspath,runtimeClasspath
+io.micronaut.elasticsearch:micronaut-elasticsearch-bom:5.0.1=compileClasspath,runtimeClasspath
+io.micronaut.email:micronaut-email-bom:2.2.1=compileClasspath,runtimeClasspath
+io.micronaut.flyway:micronaut-flyway-bom:6.1.0=compileClasspath,runtimeClasspath
+io.micronaut.gcp:micronaut-gcp-bom:5.0.4=compileClasspath,runtimeClasspath
+io.micronaut.graphql:micronaut-graphql-bom:4.0.2=compileClasspath,runtimeClasspath
+io.micronaut.groovy:micronaut-groovy-bom:4.0.1=compileClasspath,runtimeClasspath
+io.micronaut.grpc:micronaut-grpc-bom:4.0.1=compileClasspath,runtimeClasspath
+io.micronaut.jaxrs:micronaut-jaxrs-bom:4.0.2=compileClasspath,runtimeClasspath
+io.micronaut.jms:micronaut-jms-bom:3.0.1=compileClasspath,runtimeClasspath
+io.micronaut.jmx:micronaut-jmx-bom:4.0.1=compileClasspath,runtimeClasspath
+io.micronaut.kafka:micronaut-kafka-bom:5.1.2=compileClasspath,runtimeClasspath
+io.micronaut.kotlin:micronaut-kotlin-bom:4.0.2=compileClasspath,runtimeClasspath
+io.micronaut.kubernetes:micronaut-kubernetes-bom:5.0.1=compileClasspath,runtimeClasspath
+io.micronaut.liquibase:micronaut-liquibase-bom:6.0.2=compileClasspath,runtimeClasspath
+io.micronaut.logging:micronaut-logging-bom:1.1.2=compileClasspath,runtimeClasspath
+io.micronaut.micrometer:micronaut-micrometer-bom:5.0.1=compileClasspath,runtimeClasspath
+io.micronaut.microstream:micronaut-microstream-bom:2.0.2=compileClasspath,runtimeClasspath
+io.micronaut.mongodb:micronaut-mongo-bom:5.0.1=compileClasspath,runtimeClasspath
+io.micronaut.mqtt:micronaut-mqtt-bom:3.0.2=compileClasspath,runtimeClasspath
+io.micronaut.multitenancy:micronaut-multitenancy-bom:5.0.3=compileClasspath,runtimeClasspath
+io.micronaut.nats:micronaut-nats-bom:4.0.1=compileClasspath,runtimeClasspath
+io.micronaut.neo4j:micronaut-neo4j-bom:6.1.0=compileClasspath,runtimeClasspath
+io.micronaut.objectstorage:micronaut-object-storage-bom:2.0.4=compileClasspath,runtimeClasspath
+io.micronaut.openapi:micronaut-openapi-bom:5.1.1=compileClasspath,runtimeClasspath
+io.micronaut.oraclecloud:micronaut-oraclecloud-bom:3.0.6=compileClasspath,runtimeClasspath
+io.micronaut.picocli:micronaut-picocli-bom:5.0.2=compileClasspath,runtimeClasspath
+io.micronaut.platform:micronaut-platform:4.1.6=compileClasspath,runtimeClasspath
+io.micronaut.problem:micronaut-problem-json-bom:3.0.2=compileClasspath,runtimeClasspath
+io.micronaut.pulsar:micronaut-pulsar-bom:2.0.1=compileClasspath,runtimeClasspath
+io.micronaut.r2dbc:micronaut-r2dbc-bom:5.0.1=compileClasspath,runtimeClasspath
+io.micronaut.rabbitmq:micronaut-rabbitmq-bom:4.1.0=compileClasspath,runtimeClasspath
+io.micronaut.reactor:micronaut-reactor-bom:3.0.5=compileClasspath,runtimeClasspath
+io.micronaut.redis:micronaut-redis-bom:6.0.2=compileClasspath,runtimeClasspath
+io.micronaut.rss:micronaut-rss-bom:4.0.1=compileClasspath,runtimeClasspath
+io.micronaut.rxjava2:micronaut-rxjava2-bom:2.0.1=compileClasspath,runtimeClasspath
+io.micronaut.rxjava3:micronaut-rxjava3-bom:3.0.2=compileClasspath,runtimeClasspath
+io.micronaut.security:micronaut-security-bom:4.1.0=compileClasspath,runtimeClasspath
+io.micronaut.serde:micronaut-serde-bom:2.2.6=compileClasspath,runtimeClasspath
+io.micronaut.servlet:micronaut-servlet-bom:4.1.1=compileClasspath,runtimeClasspath
+io.micronaut.session:micronaut-session-bom:4.0.0=compileClasspath,runtimeClasspath
+io.micronaut.spring:micronaut-spring-bom:5.0.2=compileClasspath,runtimeClasspath
+io.micronaut.sql:micronaut-sql-bom:5.0.3=compileClasspath,runtimeClasspath
+io.micronaut.test:micronaut-test-bom:4.0.2=compileClasspath,runtimeClasspath
+io.micronaut.testresources:micronaut-test-resources-bom:2.0.0=compileClasspath,runtimeClasspath
+io.micronaut.toml:micronaut-toml-bom:2.0.1=compileClasspath,runtimeClasspath
+io.micronaut.tracing:micronaut-tracing-bom:5.0.2=compileClasspath,runtimeClasspath
+io.micronaut.validation:micronaut-validation-bom:4.0.3=compileClasspath,runtimeClasspath
+io.micronaut.views:micronaut-views-bom:4.0.1=compileClasspath,runtimeClasspath
+io.micronaut.xml:micronaut-jackson-xml-bom:4.0.1=compileClasspath,runtimeClasspath
+io.micronaut:micronaut-core-bom:4.1.11=compileClasspath,runtimeClasspath
+io.micronaut:micronaut-core:4.1.11=compileClasspath,runtimeClasspath
+io.micronaut:micronaut-inject:4.1.11=compileClasspath,runtimeClasspath
+io.netty:netty-bom:4.1.100.Final=compileClasspath,runtimeClasspath
+io.opentelemetry.instrumentation:opentelemetry-instrumentation-bom-alpha:1.26.0-alpha=compileClasspath,runtimeClasspath
+io.opentelemetry.instrumentation:opentelemetry-instrumentation-bom:1.26.0=compileClasspath,runtimeClasspath
+io.opentelemetry:opentelemetry-bom-alpha:1.26.0-alpha=compileClasspath,runtimeClasspath
+io.opentelemetry:opentelemetry-bom:1.26.0=compileClasspath,runtimeClasspath
+io.projectreactor:reactor-bom:2022.0.12=compileClasspath,runtimeClasspath
+io.rest-assured:rest-assured-bom:5.3.2=compileClasspath,runtimeClasspath
+io.zipkin.brave:brave-bom:5.16.0=compileClasspath,runtimeClasspath
+io.zipkin.reporter2:zipkin-reporter-bom:2.16.4=compileClasspath,runtimeClasspath
+jakarta.annotation:jakarta.annotation-api:2.1.1=compileClasspath,runtimeClasspath
+jakarta.inject:jakarta.inject-api:2.0.1=compileClasspath,runtimeClasspath
+org.apache.groovy:groovy-bom:4.0.14=compileClasspath,runtimeClasspath
+org.apache.logging.log4j:log4j-bom:2.20.0=compileClasspath,runtimeClasspath
+org.jdbi:jdbi3-bom:3.39.1=compileClasspath,runtimeClasspath
+org.jetbrains.kotlin:kotlin-bom:1.8.21=compileClasspath,runtimeClasspath
+org.jetbrains.kotlin:kotlin-stdlib:1.9.20=compileClasspath,runtimeClasspath
+org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3=compileClasspath,runtimeClasspath
+org.jetbrains:annotations:13.0=compileClasspath,runtimeClasspath
+org.junit:junit-bom:5.9.3=compileClasspath,runtimeClasspath
+org.slf4j:slf4j-api:2.0.9=compileClasspath,runtimeClasspath
+org.spockframework:spock-bom:2.3-groovy-4.0=compileClasspath,runtimeClasspath
+org.springframework:spring-framework-bom:6.0.11=compileClasspath,runtimeClasspath
+org.testcontainers:testcontainers-bom:1.19.1=compileClasspath,runtimeClasspath
+software.amazon.awssdk:bom:2.20.162=compileClasspath,runtimeClasspath
+empty=