1
1
package kotlinx.coroutines.internal
2
2
3
- import java.util.*
4
3
import java.io.*
5
4
import java.net.*
5
+ import java.util.*
6
6
import java.util.jar.*
7
7
import java.util.zip.*
8
8
9
9
/* *
10
10
* Name of the boolean property that enables using of [FastServiceLoader].
11
11
*/
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"
13
13
14
14
/* *
15
15
* A simplified version of [ServiceLoader].
@@ -22,12 +22,10 @@ private const val FAST_SERVICE_LOADER_PROPERTY_NAME = "kotlinx.coroutines.verify
22
22
*
23
23
* If any error occurs during loading, it fallbacks to [ServiceLoader], mostly to prevent R8 issues.
24
24
*/
25
-
26
25
internal object FastServiceLoader {
27
26
private const val PREFIX : String = " META-INF/services/"
28
27
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 )
31
29
32
30
internal fun <S > load (service : Class <S >, loader : ClassLoader ): List <S > {
33
31
if (! FAST_SERVICE_LOADER_ENABLED ) {
@@ -41,16 +39,14 @@ internal object FastServiceLoader {
41
39
}
42
40
}
43
41
42
+ // Visible for tests
44
43
internal fun <S > loadProviders (service : Class <S >, loader : ClassLoader ): List <S > {
45
44
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()
52
48
require(providers.isNotEmpty()) { " No providers were loaded with FastServiceLoader" }
53
- return providers
49
+ return providers.map { getProviderInstance(it, loader, service) }
54
50
}
55
51
56
52
private fun <S > getProviderInstance (name : String , loader : ClassLoader , service : Class <S >): S {
@@ -60,16 +56,22 @@ internal object FastServiceLoader {
60
56
}
61
57
62
58
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!
67
65
(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)
70
68
}
71
69
}
72
- } else emptyList()
70
+ }
71
+ // Regular path for everything elese
72
+ return BufferedReader (InputStreamReader (url.openStream())).use { reader ->
73
+ parseFile(reader)
74
+ }
73
75
}
74
76
75
77
private fun parseFile (r : BufferedReader ): List <String > {
0 commit comments