Skip to content

Commit 585b42b

Browse files
Implement service loader
1 parent 60a45ef commit 585b42b

3 files changed

Lines changed: 71 additions & 7 deletions

File tree

convertible-core/src/main/kotlin/pro/vlprojects/convertible/core/processor/ConvertibleProcessor.kt

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,17 @@ import com.google.devtools.ksp.validate
1212
import com.squareup.kotlinpoet.FileSpec
1313
import pro.vlprojects.convertible.core.annotation.Convertible
1414
import pro.vlprojects.convertible.core.definition.ConvertibleDefinition
15-
import pro.vlprojects.convertible.core.strategy.ConvertibleStrategy
15+
import pro.vlprojects.convertible.core.strategy.ConvertibleStrategyLoader
1616
import java.io.OutputStreamWriter
17-
import java.util.ServiceLoader
1817

1918
class ConvertibleProcessor(
2019
private val generator: CodeGenerator,
2120
private val logger: KSPLogger,
2221
) : SymbolProcessor {
2322

24-
private val strategies = ServiceLoader
25-
.load(ConvertibleStrategy::class.java)
26-
.toList()
27-
.onEach { strategy -> logger.info("Loaded strategy: ${strategy.javaClass.canonicalName}") }
23+
private val strategies = ConvertibleStrategyLoader(logger)
24+
.load()
25+
.also { logger.info("${it.size} strategies found for code generating") }
2826

2927
private val definitions = mutableListOf<ConvertibleDefinition>()
3028
private val visitor = ConvertibleVisitor(definitions)
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package pro.vlprojects.convertible.core.strategy
2+
3+
import com.google.devtools.ksp.processing.KSPLogger
4+
import java.net.URL
5+
6+
class ConvertibleStrategyLoader(
7+
private val logger: KSPLogger,
8+
) {
9+
10+
companion object {
11+
const val RESOURCE_NAME = "META-INF/services/pro.vlprojects.convertible.core.strategy.ConvertibleStrategy"
12+
}
13+
14+
fun load(): List<ConvertibleStrategy> {
15+
16+
val classLoader = Thread.currentThread().contextClassLoader
17+
val result = mutableListOf<ConvertibleStrategy>()
18+
19+
readResources(classLoader)
20+
.forEach { resource ->
21+
logger.info("Found resource: $resource")
22+
23+
readClassNames(resource)
24+
.forEach { className ->
25+
logger.info("Found class: $className")
26+
27+
runCatching { createStrategy(className, classLoader) }
28+
.onFailure { logger.error("Failed to create strategy for class $className. Details: ${it.message}") }
29+
.onSuccess {
30+
result.add(it)
31+
logger.info("Created strategy for class $className")
32+
}
33+
}
34+
}
35+
36+
return result
37+
}
38+
39+
private fun readResources(classLoader: ClassLoader) = classLoader
40+
.runCatching { getResources(RESOURCE_NAME) }
41+
.onFailure { logger.error("Failed to read resources $RESOURCE_NAME. Details: ${it.message}") }
42+
.getOrNull()
43+
?.toList()
44+
?: emptyList()
45+
46+
private fun readClassNames(resource: URL) = resource
47+
.runCatching {
48+
openStream()
49+
.bufferedReader()
50+
.readLines()
51+
.map { it.trim() }
52+
.filter { it.isNotBlank() && it.startsWith("#").not() }
53+
}
54+
.onFailure { logger.error("Failed to read resource $resource. Details: ${it.message}") }
55+
.getOrDefault(emptyList())
56+
57+
private fun createStrategy(className: String, classLoader: ClassLoader): ConvertibleStrategy {
58+
val clazz = Class.forName(className, true, classLoader)
59+
60+
require(ConvertibleStrategy::class.java.isAssignableFrom(clazz)) {
61+
"Class $className is not assignable to ${ConvertibleStrategy::class.java.canonicalName}"
62+
}
63+
64+
return clazz.getDeclaredConstructor().newInstance() as ConvertibleStrategy
65+
}
66+
}

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
group=pro.vlprojects
2-
version=0.0.5
2+
version=0.0.6
33
kotlin.version=2.1.21
44
kotlin.code.style=official

0 commit comments

Comments
 (0)