Reading files in resource path from jar artifact
I recently had to read some files from the resources folder in a spring + kotlin project, during the development period I did not encounter any problems, my headache started after the artifact was wrapped …
In debug mode, you can read the file using this:
import java.nio.file.Files/**
* Created by: jonathan
* DateTime: 2018-05-02 10:38
**/
@Configuration
class InDebugMode(final val loader: ResourceLoader)
{
init
{
val file = loader.getResource("classpath:/example.txt").file
Files.lines(file.toPath()).forEach { println(it) }
}
}
Output:
line 1
line 2
line 3
...
We could use these lines for anything.
Great, no? NOPE!
See what happens when I run the application from jar file:
ERROR 7526 ---[main] o.s.boot.SpringApplication: Application run failedorg.springframework.beans.factory.BeanCreationException: Error creating bean with name 'inDebugMode' defined in URL [jar:file:/home/user/work/demo-app/target/demo-app.jar!/BOOT-INF/classes!/org/ixfun/demo/get/file/InDebugMode.class]: Bean instantiation via constructor failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate
[org.ixfun.demo.get.file.InDebugMode$$EnhancerBySpringCGLIB$$88d0d6eb]: Constructor threw exception; nested exception is java.io.FileNotFoundException: class path resource [example.txt] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/home/user/work/demo-app/target/demo-app.jar!/BOOT-INF/classes!/example.txt
Cool…
The most interesting is this structure [ /home/user/work/demo-app/target/demo-app.jar!/BOOT-INF/classes!/example.txt ] exists !
The big problem is the method of consuming the files.
The spring context is not contained within the method and the application try to use the absolute path to build a file by ( Files interface)… and fail :|
I can read the files using the good and old InputStream and BufferedReader :
package org.ixfun.demo.get.file
import org.springframework.context.annotation.Configuration
import org.springframework.context.support.ClassPathXmlApplicationContext
import java.io.BufferedReader
import java.io.InputStreamReader
/**
* Created by: jonathan
* DateTime: 2018-05-02 10:38
**/
@Configuration
class InJarMode
{
private val appContext = ClassPathXmlApplicationContext()
init
{
val file = appContext.getResource("classpath:/example.txt")
if (file.isReadable)
{
val im = file.inputStream
val br = BufferedReader(InputStreamReader(im))
br.lines().forEach { println(it) }
br.close()
}
}
}
This way the solution works in both cases.