11package kotlinx.coroutines.internal
22
3- import java.util.*
43import java.io.*
54import java.net.*
5+ import java.util.*
66import java.util.jar.*
77import java.util.zip.*
88
99/* *
1010 * Name of the boolean property that enables using of [FastServiceLoader].
1111 */
12- private const val FAST_SERVICE_LOADER_PROPERTY_NAME = " kotlinx.coroutines.verify .service.loader"
12+ private const val FAST_SERVICE_LOADER_PROPERTY_NAME = " kotlinx.coroutines.fast .service.loader"
1313
1414/* *
1515 * A simplified version of [ServiceLoader].
@@ -22,12 +22,10 @@ private const val FAST_SERVICE_LOADER_PROPERTY_NAME = "kotlinx.coroutines.verify
2222 *
2323 * If any error occurs during loading, it fallbacks to [ServiceLoader], mostly to prevent R8 issues.
2424 */
25-
2625internal object FastServiceLoader {
2726 private const val PREFIX : String = " META-INF/services/"
2827
29- @JvmField
30- internal val FAST_SERVICE_LOADER_ENABLED = systemProp(FAST_SERVICE_LOADER_PROPERTY_NAME , true )
28+ private val FAST_SERVICE_LOADER_ENABLED = systemProp(FAST_SERVICE_LOADER_PROPERTY_NAME , true )
3129
3230 internal fun <S > load (service : Class <S >, loader : ClassLoader ): List <S > {
3331 if (! FAST_SERVICE_LOADER_ENABLED ) {
@@ -41,16 +39,14 @@ internal object FastServiceLoader {
4139 }
4240 }
4341
42+ // Visible for tests
4443 internal fun <S > loadProviders (service : Class <S >, loader : ClassLoader ): List <S > {
4544 val fullServiceName = PREFIX + service.name
46- val urls = loader.getResources(fullServiceName).toList()
47- val providers = mutableListOf<S >()
48- urls.forEach {
49- val providerNames = parse(it)
50- providers.addAll(providerNames.map { getProviderInstance(it, loader, service) })
51- }
45+ // Filter out situations when both JAR and regular files are in the classpath (e.g. IDEA)
46+ val urls = loader.getResources(fullServiceName)
47+ val providers = urls.toList().flatMap { parse(it) }.toSet()
5248 require(providers.isNotEmpty()) { " No providers were loaded with FastServiceLoader" }
53- return providers
49+ return providers.map { getProviderInstance(it, loader, service) }
5450 }
5551
5652 private fun <S > getProviderInstance (name : String , loader : ClassLoader , service : Class <S >): S {
@@ -60,16 +56,22 @@ internal object FastServiceLoader {
6056 }
6157
6258 private fun parse (url : URL ): List <String > {
63- val string = url.toString()
64- return if (string.startsWith(" jar" )) {
65- val pathToJar = string.substringAfter(" jar:file:" ).substringBefore(' !' )
66- val entry = string.substringAfter(" !/" )
59+ val path = url.toString()
60+ // Fast-path for JARs
61+ if (path.startsWith(" jar" )) {
62+ val pathToJar = path.substringAfter(" jar:file:" ).substringBefore(' !' )
63+ val entry = path.substringAfter(" !/" )
64+ // mind the verify = false flag!
6765 (JarFile (pathToJar, false ) as Closeable ).use { file ->
68- BufferedReader (InputStreamReader ((file as JarFile ).getInputStream(ZipEntry (entry))," UTF-8" )).use { r ->
69- parseFile(r)
66+ BufferedReader (InputStreamReader ((file as JarFile ).getInputStream(ZipEntry (entry)), " UTF-8" )).use { r ->
67+ return parseFile(r)
7068 }
7169 }
72- } else emptyList()
70+ }
71+ // Regular path for everything elese
72+ return BufferedReader (InputStreamReader (url.openStream())).use { reader ->
73+ parseFile(reader)
74+ }
7375 }
7476
7577 private fun parseFile (r : BufferedReader ): List <String > {
0 commit comments