I have the following snippet working properly in a JVM setup:
hello:
message: classpath:hello/world.txt
@ConfigurationProperties("hello")
data class Config(val message: Resource)
@Configuration
@EnableConfigurationProperties(Config::class)
@ImportRuntimeHints(ResourceConfigRegistrar::class)
class GreeterService(private val config: Config) {
private val logger = KotlinLogging.logger { }
@PostConstruct
fun greet() {
logger.info { config.message.file.readText() }
}
}
internal class ResourceConfigRegistrar : RuntimeHintsRegistrar {
override fun registerHints(hints: RuntimeHints, classLoader: ClassLoader?) {
hints.resources().registerResource(ClassPathResource("hello/world.txt"))
}
}
However, for native image it doesn't with the error message: java.io.FileNotFoundException: class path resource [hello/world.txt] cannot be resolved to absolute file path because it does not reside in the file system: resource:/hello/world.txt
When changing the code to the following, it works as intended:
hello:
message: /hello/world.txt
@ConfigurationProperties("hello")
data class Config(val message: String)
@Configuration
@EnableConfigurationProperties(Config::class)
@ImportRuntimeHints(ResourceConfigRegistrar::class)
class GreeterService(private val config: Config) {
private val logger = KotlinLogging.logger { }
@PostConstruct
fun greet() {
logger.info { javaClass.getResourceAsStream(config.message)!!.bufferedReader().readText() }
}
}
internal class ResourceConfigRegistrar : RuntimeHintsRegistrar {
override fun registerHints(hints: RuntimeHints, classLoader: ClassLoader?) {
hints.resources().registerResource(ClassPathResource("hello/world.txt"))
}
}
As I like to use Resource as an abstraction in my code, I think this should work in the native image case as well.
Eventually ClassLoader.getSystemResource(this.absolutePath) or ClassLoader.getSystemClassLoader(); is called by the Spring Resource resolving mechanism. When I do remember correctly, this do not work in native image.
I therefore tried using ClassPathResource("hello/world.txt") directly, also tried ClassPathResource("hello/world.txt", javaClass.classLoader) but all with the same result in native image.
I pushed a sample to cmdjulian/spring-playground on branch resource-loading-native-image. You can run it by executing ./gradlew nativeCompile && build/native/nativeCompile/notes.
Comment From: cmdjulian
Turns out, ResourceConfigRegistrar is actually not needed
Comment From: mhalbritter
When using .getFile(), you assume that a File is available, which is not the case for resources in a native image. The correct way to to it is to use .getInputStream, which returns an InputStream abstraction, which works for all resources (in the file system, in a JAR file, in a native-image, ...).
This code works:
logger.info { config.message.inputStream.readAllBytes().decodeToString() }