diff --git a/.editorconfig b/.editorconfig index d340b76d..ce7dcfe8 100644 --- a/.editorconfig +++ b/.editorconfig @@ -15,8 +15,12 @@ charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true +[*.{kt,kts}] +ktlint_standard_no-wildcard-imports = disabled + [*.md] trim_trailing_whitespace = false [*.{json,yaml,yml}] +indent_style = space indent_size = 2 diff --git a/CODEOWNERS b/CODEOWNERS index 57ad8f59..c354ea6d 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,5 +1,4 @@ # These owners will be the default owners for everything in the repo. # Unless a later match takes precedence, they will be requested for review when someone # opens a pull request. -* @blootsvoets -testing/* @nivemaham @fnobilia \ No newline at end of file +* @bdegraaf1234 diff --git a/README.md b/README.md index cdcc67ab..3af31c31 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ repositories { } dependencies { - implementation("org.radarbase:radar-commons:1.1.1") + implementation("org.radarbase:radar-commons:1.1.2") } ``` @@ -62,7 +62,7 @@ repositories { } dependencies { - implementation("org.radarbase:radar-commons-server:1.1.1") + implementation("org.radarbase:radar-commons-server:1.1.2") } ``` @@ -75,7 +75,7 @@ repositories { } dependencies { - testImplementation("org.radarbase:radar-commons-testing:1.1.1") + testImplementation("org.radarbase:radar-commons-testing:1.1.2") } ``` @@ -102,7 +102,7 @@ configurations.all { } dependencies { - implementation("org.radarbase:radar-commons:1.1.2-SNAPSHOT") + implementation("org.radarbase:radar-commons:1.1.3-SNAPSHOT") } ``` diff --git a/build.gradle.kts b/build.gradle.kts index eee21afd..6b61096b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -48,9 +48,9 @@ subprojects { githubUrl.set("https://github.com/$githubRepoName") developers { developer { - id.set("blootsvoets") - name.set("Joris Borgdorff") - email.set("joris@thehyve.nl") + id.set("bdegraaf1234") + name.set("Bastiaan de Graaf") + email.set("bastiaan@thehyve.nl") organization.set("The Hyve") } developer { diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 5013cfcb..d4f14b3d 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -9,7 +9,6 @@ repositories { mavenCentral() } - tasks.withType { sourceCompatibility = "17" targetCompatibility = "17" diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index e3313772..c3ee5ddd 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -1,16 +1,19 @@ @Suppress("ConstPropertyName", "MemberVisibilityCanBePrivate") object Versions { - const val project = "1.1.1" + const val project = "1.1.2" object Plugins { - const val kotlin = "1.9.10" + const val licenseReport = "2.5" + const val kotlin = "1.9.21" + const val dokka = "1.9.10" const val kotlinSerialization = kotlin const val kotlinAllOpen = kotlin const val avro = "1.8.0" const val gradle = "8.3" + const val publishPlugin = "2.0.0-rc-1" } - const val java = 11 + const val java = 17 const val slf4j = "2.0.9" const val confluent = "7.5.0" const val kafka = "7.5.0-ce" @@ -25,6 +28,9 @@ object Versions { const val opencsv = "5.8" const val ktor = "2.3.4" const val coroutines = "1.7.3" - const val commonsCompress = "1.24.0" + const val commonsCompress = "1.26.0" const val snappy = "1.1.10.5" + const val guava = "32.1.1-jre" + const val gradleVersionsPlugin = "0.50.0" + const val ktlint = "12.0.3" } diff --git a/radar-commons-gradle/build.gradle.kts b/radar-commons-gradle/build.gradle.kts index 8cebf42a..9585a341 100644 --- a/radar-commons-gradle/build.gradle.kts +++ b/radar-commons-gradle/build.gradle.kts @@ -4,14 +4,15 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { `kotlin-dsl` `java-gradle-plugin` - kotlin("jvm") version "1.9.0" + // Match to the versions in the bottom of this file + kotlin("jvm") version "1.9.21" `maven-publish` id("io.github.gradle-nexus.publish-plugin") version "2.0.0-rc-1" - id("org.jetbrains.dokka") version "1.9.0" + id("org.jetbrains.dokka") version "1.9.10" signing } -version = "1.1.1" +version = "1.1.2" group = "org.radarbase" description = "RADAR-base common Gradle plugin setup" @@ -23,12 +24,12 @@ repositories { } dependencies { - implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.10") - implementation("org.jetbrains.dokka:dokka-gradle-plugin:1.9.0") - implementation("com.github.ben-manes:gradle-versions-plugin:0.48.0") - implementation("io.github.gradle-nexus:publish-plugin:2.0.0-rc-1") - implementation("org.jlleitschuh.gradle:ktlint-gradle:11.6.0") - implementation("com.github.jk1.dependency-license-report:com.github.jk1.dependency-license-report.gradle.plugin:2.5") + implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.Plugins.kotlin}") + implementation("org.jetbrains.dokka:dokka-gradle-plugin:${Versions.Plugins.dokka}") + implementation("com.github.ben-manes:gradle-versions-plugin:${Versions.gradleVersionsPlugin}") + implementation("io.github.gradle-nexus:publish-plugin:${Versions.Plugins.publishPlugin}") + implementation("org.jlleitschuh.gradle:ktlint-gradle:${Versions.ktlint}") + implementation("com.github.jk1.dependency-license-report:com.github.jk1.dependency-license-report.gradle.plugin:${Versions.Plugins.licenseReport}") } gradlePlugin { @@ -53,11 +54,12 @@ gradlePlugin { } tasks.withType { - options.release.set(11) + options.release.set(Versions.java) } + tasks.withType { compilerOptions { - jvmTarget.set(JvmTarget.JVM_11) + jvmTarget.set(JvmTarget.JVM_17) languageVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9) apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9) } @@ -102,9 +104,9 @@ tasks.withType { } developers { developer { - id.set("blootsvoets") - name.set("Joris Borgdorff") - email.set("joris@thehyve.nl") + id.set("bdegraaf1234") + name.set("Bastiaan de Graaf") + email.set("bastiaan@thehyve.nl") organization.set("The Hyve") } } @@ -169,3 +171,43 @@ tasks.withType { tasks.withType { dependsOn(tasks.withType()) } + +// Because this project is where all the required plugins get built, we need to add the dependencies separately here. +// They should be copied from the Versions.kt file directly to maintain consistency. +@Suppress("ConstPropertyName", "MemberVisibilityCanBePrivate") +object Versions { + const val project = "1.1.2" + + object Plugins { + const val licenseReport = "2.5" + const val kotlin = "1.9.21" + const val dokka = "1.9.10" + const val kotlinSerialization = kotlin + const val kotlinAllOpen = kotlin + const val avro = "1.8.0" + const val gradle = "8.3" + const val publishPlugin = "2.0.0-rc-1" + } + + const val java = 17 + const val slf4j = "2.0.9" + const val confluent = "7.5.0" + const val kafka = "7.5.0-ce" + const val avro = "1.11.3" + const val jackson = "2.15.2" + const val okhttp = "4.11.0" + const val junit = "5.10.0" + const val mockito = "5.5.0" + const val mockitoKotlin = "5.1.0" + const val hamcrest = "2.2" + const val radarSchemas = "0.8.4" + const val opencsv = "5.8" + const val ktor = "2.3.4" + const val coroutines = "1.7.3" + const val commonsCompress = "1.26.0" + const val snappy = "1.1.10.5" + const val guava = "32.1.1-jre" + const val gradleVersionsPlugin = "0.50.0" + const val ktlint = "12.0.3" +} + diff --git a/radar-commons-gradle/src/main/kotlin/org/radarbase/gradle/plugin/RadarDependencyManagementPlugin.kt b/radar-commons-gradle/src/main/kotlin/org/radarbase/gradle/plugin/RadarDependencyManagementPlugin.kt index 8baf32b6..f36993c2 100644 --- a/radar-commons-gradle/src/main/kotlin/org/radarbase/gradle/plugin/RadarDependencyManagementPlugin.kt +++ b/radar-commons-gradle/src/main/kotlin/org/radarbase/gradle/plugin/RadarDependencyManagementPlugin.kt @@ -22,7 +22,7 @@ interface RadarDependencyManagementExtension { class RadarDependencyManagementPlugin : Plugin { override fun apply(project: Project): Unit = with(project) { val extension = extensions.create("radarDependencies").apply { - regex.convention("(^[0-9,.v-]+(-r)?|RELEASE|FINAL|GA|-CE)$") + regex.convention("(^[0-9,.v-]+(-r)?|RELEASE|FINAL|GA|-CE|-JRE|-ANDROID)$") rejectMajorVersionUpdates.convention(false) } diff --git a/radar-commons-kotlin/README.md b/radar-commons-kotlin/README.md index ecb5b40c..4d3ba96e 100644 --- a/radar-commons-kotlin/README.md +++ b/radar-commons-kotlin/README.md @@ -10,7 +10,7 @@ First of all, it contains a cache implementation to use with coroutines, with su import java.io.IOException val dirCache = CachedMap { - withContext(Dispatchers.IO) { + runInterruptible(Dispatchers.IO) { client.fetchDirectoryMap() } } diff --git a/radar-commons-kotlin/build.gradle.kts b/radar-commons-kotlin/build.gradle.kts index a288b7cc..0ef365a0 100644 --- a/radar-commons-kotlin/build.gradle.kts +++ b/radar-commons-kotlin/build.gradle.kts @@ -6,12 +6,12 @@ description = "Library for Kotlin utility classes and functions" dependencies { api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:${Versions.coroutines}")) - api("org.jetbrains.kotlinx:kotlinx-coroutines-core") + api("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.coroutines}") api(platform("io.ktor:ktor-bom:${Versions.ktor}")) - api("io.ktor:ktor-client-auth") - implementation("io.ktor:ktor-client-content-negotiation") - implementation("io.ktor:ktor-serialization-kotlinx-json") + api("io.ktor:ktor-client-auth:${Versions.ktor}") + implementation("io.ktor:ktor-client-content-negotiation:${Versions.ktor}") + implementation("io.ktor:ktor-serialization-kotlinx-json:${Versions.ktor}") testImplementation("org.hamcrest:hamcrest:${Versions.hamcrest}") } diff --git a/radar-commons-kotlin/src/main/kotlin/org/radarbase/kotlin/coroutines/Extensions.kt b/radar-commons-kotlin/src/main/kotlin/org/radarbase/kotlin/coroutines/Extensions.kt index 0329c62b..cf24b97b 100644 --- a/radar-commons-kotlin/src/main/kotlin/org/radarbase/kotlin/coroutines/Extensions.kt +++ b/radar-commons-kotlin/src/main/kotlin/org/radarbase/kotlin/coroutines/Extensions.kt @@ -11,8 +11,8 @@ import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.consume import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch +import kotlinx.coroutines.runInterruptible import kotlinx.coroutines.sync.Semaphore -import kotlinx.coroutines.withContext import java.util.concurrent.Future import java.util.concurrent.TimeUnit import kotlin.coroutines.CoroutineContext @@ -49,15 +49,13 @@ suspend fun Future.suspendGet( } } try { - withContext(Dispatchers.IO) { + runInterruptible(Dispatchers.IO) { if (duration != null) { get(duration.inWholeMilliseconds, TimeUnit.MILLISECONDS) } else { get() } } - } catch (ex: InterruptedException) { - throw CancellationException("Future was interrupted", ex) } finally { channel.send(Unit) } diff --git a/radar-commons-kotlin/src/main/kotlin/org/radarbase/kotlin/coroutines/flow/Extensions.kt b/radar-commons-kotlin/src/main/kotlin/org/radarbase/kotlin/coroutines/flow/Extensions.kt index 66ee4b20..ee1f124a 100644 --- a/radar-commons-kotlin/src/main/kotlin/org/radarbase/kotlin/coroutines/flow/Extensions.kt +++ b/radar-commons-kotlin/src/main/kotlin/org/radarbase/kotlin/coroutines/flow/Extensions.kt @@ -1,13 +1,15 @@ package org.radarbase.kotlin.coroutines.flow import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.zip /** * Convert a list of flows to one flow with a list of values. The list contains the latest value of - * each respective flow, in the same order as the original flows. + * each respective flow, in the same order as the original flows. One value is produced when all + * flows produce a new value. */ fun List>.zipAll(): Flow> = when (val numberOfFlows = size) { 0 -> flowOf(listOf()) @@ -23,3 +25,12 @@ fun List>.zipAll(): Flow> = when (val numberOfFlows = size) fun zipAll( vararg flows: Flow, ): Flow> = flows.toList().zipAll() + +/** + * Combine the latest values of the flows in a list to a single list. + */ +inline fun List>.combine(): Flow> = when (size) { + 0 -> flowOf(emptyList()) + 1 -> get(0).map { listOf(it) } + else -> combine(this) { it.toList() } +} diff --git a/radar-commons-server/build.gradle.kts b/radar-commons-server/build.gradle.kts index b8b03ea6..05ef72b0 100644 --- a/radar-commons-server/build.gradle.kts +++ b/radar-commons-server/build.gradle.kts @@ -36,10 +36,12 @@ dependencies { // For POJO classes and ConfigLoader implementation(platform("com.fasterxml.jackson:jackson-bom:${Versions.jackson}")) - implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml") - implementation("com.fasterxml.jackson.core:jackson-databind") + implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:${Versions.jackson}") + implementation("com.fasterxml.jackson.core:jackson-databind:${Versions.jackson}") - api("org.apache.avro:avro:${Versions.avro}") + api("org.apache.avro:avro:${Versions.avro}") { + implementation("org.apache.commons:commons-compress:${Versions.commonsCompress}") + } implementation("org.apache.kafka:kafka-clients:${Versions.kafka}") { implementation("org.xerial.snappy:snappy-java:${Versions.snappy}") @@ -47,7 +49,10 @@ dependencies { testImplementation("org.mockito:mockito-core:${Versions.mockito}") // Direct producer uses KafkaAvroSerializer if initialized - testImplementation("io.confluent:kafka-avro-serializer:${Versions.confluent}") + + implementation("io.confluent:kafka-avro-serializer:${Versions.confluent}") { + runtimeOnly("com.google.guava:guava:${Versions.guava}") + } testImplementation("org.radarbase:radar-schemas-commons:${Versions.radarSchemas}") } diff --git a/radar-commons-testing/build.gradle.kts b/radar-commons-testing/build.gradle.kts index 4c14246e..5f64e645 100644 --- a/radar-commons-testing/build.gradle.kts +++ b/radar-commons-testing/build.gradle.kts @@ -40,20 +40,26 @@ dependencies { api(project(":radar-commons-server")) api(project(":radar-commons-kotlin")) - api("org.apache.avro:avro:${Versions.avro}") api("org.radarbase:radar-schemas-commons:${Versions.radarSchemas}") implementation("com.opencsv:opencsv:${Versions.opencsv}") implementation(platform("com.fasterxml.jackson:jackson-bom:${Versions.jackson}")) - implementation("com.fasterxml.jackson.core:jackson-databind") + implementation("com.fasterxml.jackson.core:jackson-databind:${Versions.jackson}") implementation("org.apache.kafka:kafka-clients:${Versions.kafka}") { implementation("org.xerial.snappy:snappy-java:${Versions.snappy}") } - implementation("io.confluent:kafka-avro-serializer:${Versions.confluent}") + + implementation("io.confluent:kafka-avro-serializer:${Versions.confluent}") { + runtimeOnly("com.google.guava:guava:${Versions.guava}") + } + + api("org.apache.avro:avro:${Versions.avro}") { + implementation("org.apache.commons:commons-compress:${Versions.commonsCompress}") + } implementation(platform("io.ktor:ktor-bom:${Versions.ktor}")) - implementation("io.ktor:ktor-serialization-kotlinx-json") + implementation("io.ktor:ktor-serialization-kotlinx-json:${Versions.ktor}") applicationRuntimeOnly("org.slf4j:slf4j-simple:${Versions.slf4j}") diff --git a/radar-commons-testing/src/test/java/org/radarbase/mock/data/MockRecordValidatorTest.kt b/radar-commons-testing/src/test/java/org/radarbase/mock/data/MockRecordValidatorTest.kt index ec1d68d8..b92eb671 100644 --- a/radar-commons-testing/src/test/java/org/radarbase/mock/data/MockRecordValidatorTest.kt +++ b/radar-commons-testing/src/test/java/org/radarbase/mock/data/MockRecordValidatorTest.kt @@ -16,8 +16,8 @@ package org.radarbase.mock.data import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runInterruptible import kotlinx.coroutines.test.runTest -import kotlinx.coroutines.withContext import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.io.TempDir @@ -56,7 +56,7 @@ class MockRecordValidatorTest { fun validateEnum() = runTest { val config = makeConfig() config.valueSchema = ApplicationServerStatus::class.java.name - withContext(Dispatchers.IO) { + runInterruptible(Dispatchers.IO) { config.getDataFile(root).bufferedWriter().use { writer -> writer.append("key.projectId,key.userId,key.sourceId,value.time,value.serverStatus,value.ipAddress\n") writer.append("test,a,b,1,UNKNOWN,\n") @@ -199,7 +199,7 @@ class MockRecordValidatorTest { private suspend fun writeConfig(write: Writer.() -> Unit): MockDataConfig { val config = makeConfig() - withContext(Dispatchers.IO) { + runInterruptible(Dispatchers.IO) { config.getDataFile(root).bufferedWriter().use(write) } return config diff --git a/radar-commons/build.gradle.kts b/radar-commons/build.gradle.kts index da61630e..b5a55811 100644 --- a/radar-commons/build.gradle.kts +++ b/radar-commons/build.gradle.kts @@ -24,11 +24,11 @@ dependencies { implementation(project(":radar-commons-kotlin")) api(platform("io.ktor:ktor-bom:${Versions.ktor}")) - api("io.ktor:ktor-client-core") - api("io.ktor:ktor-client-cio") - api("io.ktor:ktor-client-auth") - implementation("io.ktor:ktor-client-content-negotiation") - implementation("io.ktor:ktor-serialization-kotlinx-json") + api("io.ktor:ktor-client-core:${Versions.ktor}") + api("io.ktor:ktor-client-cio:${Versions.ktor}") + api("io.ktor:ktor-client-auth:${Versions.ktor}") + implementation("io.ktor:ktor-client-content-negotiation:${Versions.ktor}") + implementation("io.ktor:ktor-serialization-kotlinx-json:${Versions.ktor}") api("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.coroutines}") diff --git a/radar-commons/src/main/java/org/radarbase/producer/rest/RestKafkaSender.kt b/radar-commons/src/main/java/org/radarbase/producer/rest/RestKafkaSender.kt index f5a6974d..9c3cc082 100644 --- a/radar-commons/src/main/java/org/radarbase/producer/rest/RestKafkaSender.kt +++ b/radar-commons/src/main/java/org/radarbase/producer/rest/RestKafkaSender.kt @@ -41,6 +41,7 @@ import io.ktor.util.reflect.TypeInfo import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.async import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first import kotlinx.coroutines.withContext @@ -181,11 +182,11 @@ class RestKafkaSender(config: Config) : KafkaSender { return true } val lastState = try { - val response = withContext(Dispatchers.IO) { + val response = scope.async { restClient.head { url("") } - } + }.await() if (response.status.isSuccess()) { _connectionState.didConnect() ConnectionState.State.CONNECTED