From 1eeba0739dab72ce5b3d0f23ebec351656acfbd3 Mon Sep 17 00:00:00 2001 From: Osip Fatkullin Date: Thu, 7 Nov 2024 15:59:47 +0100 Subject: [PATCH] A separate function to load single service inmplementation --- .../jvm/src/io/ktor/client/HttpClientJvm.kt | 4 +-- .../io/ktor/client/plugins/json/DefaultJvm.kt | 6 ++-- .../client/tests/utils/ClientLoaderJvm.kt | 2 +- .../io/ktor/server/config/ConfigLoadersJvm.kt | 2 +- .../serialization/kotlinx/ExtensionsJvm.kt | 2 +- .../src/io/ktor/util/reflect/ServiceLoader.kt | 33 ++++++++++++------- 6 files changed, 30 insertions(+), 19 deletions(-) diff --git a/ktor-client/ktor-client-core/jvm/src/io/ktor/client/HttpClientJvm.kt b/ktor-client/ktor-client-core/jvm/src/io/ktor/client/HttpClientJvm.kt index f7faa2ad62..24632d8fa4 100644 --- a/ktor-client/ktor-client-core/jvm/src/io/ktor/client/HttpClientJvm.kt +++ b/ktor-client/ktor-client-core/jvm/src/io/ktor/client/HttpClientJvm.kt @@ -36,9 +36,7 @@ public interface HttpClientEngineContainer { } @OptIn(InternalAPI::class) -private val engines = loadService() - -private val FACTORY = engines.firstOrNull()?.factory ?: error( +private val FACTORY = loadServiceOrNull()?.factory ?: error( "Failed to find HTTP client engine implementation: consider adding client engine dependency. " + "See https://ktor.io/docs/http-client-engines.html" ) diff --git a/ktor-client/ktor-client-plugins/ktor-client-json/jvm/src/io/ktor/client/plugins/json/DefaultJvm.kt b/ktor-client/ktor-client-plugins/ktor-client-json/jvm/src/io/ktor/client/plugins/json/DefaultJvm.kt index 109b1350ee..bf6c9c8dc3 100644 --- a/ktor-client/ktor-client-plugins/ktor-client-json/jvm/src/io/ktor/client/plugins/json/DefaultJvm.kt +++ b/ktor-client/ktor-client-plugins/ktor-client-json/jvm/src/io/ktor/client/plugins/json/DefaultJvm.kt @@ -7,10 +7,8 @@ package io.ktor.client.plugins.json import io.ktor.util.reflect.* import io.ktor.utils.io.* -@OptIn(InternalAPI::class) @Suppress("DEPRECATION_ERROR") public actual fun defaultSerializer(): JsonSerializer { - val serializers = loadService() return serializers.maxByOrNull { it::javaClass.name } ?: error( """ Failed to find serializer. Consider adding one of the following dependencies: @@ -20,3 +18,7 @@ public actual fun defaultSerializer(): JsonSerializer { """.trimIndent() ) } + +@OptIn(InternalAPI::class) +@Suppress("DEPRECATION_ERROR") +private val serializers = loadServices() diff --git a/ktor-client/ktor-client-tests/jvm/src/io/ktor/client/tests/utils/ClientLoaderJvm.kt b/ktor-client/ktor-client-tests/jvm/src/io/ktor/client/tests/utils/ClientLoaderJvm.kt index 0290e60a1b..7aaea789cb 100644 --- a/ktor-client/ktor-client-tests/jvm/src/io/ktor/client/tests/utils/ClientLoaderJvm.kt +++ b/ktor-client/ktor-client-tests/jvm/src/io/ktor/client/tests/utils/ClientLoaderJvm.kt @@ -19,7 +19,7 @@ import kotlin.time.Duration.Companion.seconds actual abstract class ClientLoader actual constructor(val timeoutSeconds: Int) { @OptIn(InternalAPI::class) - private val engines: Iterable by lazy { loadService() } + private val engines: List by lazy { loadServices() } /** * Perform test against all clients from dependencies. diff --git a/ktor-server/ktor-server-core/jvm/src/io/ktor/server/config/ConfigLoadersJvm.kt b/ktor-server/ktor-server-core/jvm/src/io/ktor/server/config/ConfigLoadersJvm.kt index fe0e99a478..e782a33e15 100644 --- a/ktor-server/ktor-server-core/jvm/src/io/ktor/server/config/ConfigLoadersJvm.kt +++ b/ktor-server/ktor-server-core/jvm/src/io/ktor/server/config/ConfigLoadersJvm.kt @@ -16,4 +16,4 @@ internal actual val CONFIG_PATH: List ) @OptIn(InternalAPI::class) -public actual val configLoaders: List = loadService().toList() +public actual val configLoaders: List = loadServices() diff --git a/ktor-shared/ktor-serialization/ktor-serialization-kotlinx/jvm/src/io/ktor/serialization/kotlinx/ExtensionsJvm.kt b/ktor-shared/ktor-serialization/ktor-serialization-kotlinx/jvm/src/io/ktor/serialization/kotlinx/ExtensionsJvm.kt index 06e585fa67..aa8b0a05c9 100644 --- a/ktor-shared/ktor-serialization/ktor-serialization-kotlinx/jvm/src/io/ktor/serialization/kotlinx/ExtensionsJvm.kt +++ b/ktor-shared/ktor-serialization/ktor-serialization-kotlinx/jvm/src/io/ktor/serialization/kotlinx/ExtensionsJvm.kt @@ -9,4 +9,4 @@ import io.ktor.utils.io.* @OptIn(InternalAPI::class) internal actual val providers: Iterable = - loadService() + loadServices() diff --git a/ktor-utils/jvm/src/io/ktor/util/reflect/ServiceLoader.kt b/ktor-utils/jvm/src/io/ktor/util/reflect/ServiceLoader.kt index d1be71f0ac..fa0195bff3 100644 --- a/ktor-utils/jvm/src/io/ktor/util/reflect/ServiceLoader.kt +++ b/ktor-utils/jvm/src/io/ktor/util/reflect/ServiceLoader.kt @@ -7,17 +7,28 @@ package io.ktor.util.reflect import io.ktor.utils.io.* import java.util.* +// NOTE: ServiceLoader should use specific call convention to be optimized by R8 on Android: +// `ServiceLoader.load(X.class, X.class.getClassLoader()).iterator()` +// See: https://r8.googlesource.com/r8/+/refs/heads/main/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java + +/** + * Loads all implementations of the service [T] using [ServiceLoader.load]. + * @see loadServiceOrNull + */ +@InternalAPI +public inline fun loadServices(): List = ServiceLoader.load( + T::class.java, + T::class.java.classLoader, +).iterator().asSequence().toList() + + /** - * Loads implementations of service [T] using [ServiceLoader.load]. - * - * NOTE: ServiceLoader should use specific call convention to be optimized by R8 on Android: - * `ServiceLoader.load(X.class, X.class.getClassLoader()).iterator()` - * See: https://r8.googlesource.com/r8/+/refs/heads/main/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java + * Loads single implementation of the service [T] using [ServiceLoader.load] + * returns `null` if there are no any implementations. + * @see loadServices */ @InternalAPI -public inline fun loadService(): Iterable = Iterable { - ServiceLoader.load( - T::class.java, - T::class.java.classLoader, - ).iterator() -} +public inline fun loadServiceOrNull(): T? = ServiceLoader.load( + T::class.java, + T::class.java.classLoader, +).iterator().asSequence().firstOrNull()