Skip to content

Commit

Permalink
A separate function to load single service inmplementation
Browse files Browse the repository at this point in the history
  • Loading branch information
osipxd committed Nov 7, 2024
1 parent 1fe37d7 commit 1eeba07
Show file tree
Hide file tree
Showing 6 changed files with 30 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@ public interface HttpClientEngineContainer {
}

@OptIn(InternalAPI::class)
private val engines = loadService<HttpClientEngineContainer>()

private val FACTORY = engines.firstOrNull()?.factory ?: error(
private val FACTORY = loadServiceOrNull<HttpClientEngineContainer>()?.factory ?: error(
"Failed to find HTTP client engine implementation: consider adding client engine dependency. " +
"See https://ktor.io/docs/http-client-engines.html"
)
Original file line number Diff line number Diff line change
Expand Up @@ -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<JsonSerializer>()
return serializers.maxByOrNull { it::javaClass.name } ?: error(
"""
Failed to find serializer. Consider adding one of the following dependencies:
Expand All @@ -20,3 +18,7 @@ public actual fun defaultSerializer(): JsonSerializer {
""".trimIndent()
)
}

@OptIn(InternalAPI::class)
@Suppress("DEPRECATION_ERROR")
private val serializers = loadServices<JsonSerializer>()
Original file line number Diff line number Diff line change
Expand Up @@ -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<HttpClientEngineContainer> by lazy { loadService<HttpClientEngineContainer>() }
private val engines: List<HttpClientEngineContainer> by lazy { loadServices<HttpClientEngineContainer>() }

/**
* Perform test against all clients from dependencies.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ internal actual val CONFIG_PATH: List<String>
)

@OptIn(InternalAPI::class)
public actual val configLoaders: List<ConfigLoader> = loadService<ConfigLoader>().toList()
public actual val configLoaders: List<ConfigLoader> = loadServices<ConfigLoader>()
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ import io.ktor.utils.io.*

@OptIn(InternalAPI::class)
internal actual val providers: Iterable<KotlinxSerializationExtensionProvider> =
loadService<KotlinxSerializationExtensionProvider>()
loadServices<KotlinxSerializationExtensionProvider>()
33 changes: 22 additions & 11 deletions ktor-utils/jvm/src/io/ktor/util/reflect/ServiceLoader.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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 <reified T : Any> loadServices(): List<T> = 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 <reified T> loadService(): Iterable<T> = Iterable {
ServiceLoader.load(
T::class.java,
T::class.java.classLoader,
).iterator()
}
public inline fun <reified T : Any> loadServiceOrNull(): T? = ServiceLoader.load(
T::class.java,
T::class.java.classLoader,
).iterator().asSequence().firstOrNull()

0 comments on commit 1eeba07

Please sign in to comment.