smallrye / smallrye-common

Common utilities for SmallRye

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Doesn't work in Spring Boot jar

magicprinc opened this issue · comments

Spring BootJar has complex layout and special spring ClassLoader to make it work
SmallRye Config doesn't work in Spring Application packed as spring bootJar.

I have found, the problem is in your class
io.smallrye.common.classloader.ClassPathUtils

Full explanation and fix:
smallrye/smallrye-config#1101

PS: more advanced research and fix are welcome 🙏

Example application
Branch main shows the bug.
Branch fix shows the fix.
https://github.com/magicprinc/SmallRyeConfig-SpringBoot

@radcortez @dmlloyd Sorry to bother you 🙏
But the situation is critical. SmallRye Config doesn't work in a typical Spring Boot application (bundled as spring boot jar).
Could it be fixed in the near future?

git clone https://github.com/magicprinc/SmallRyeConfig-SpringBoot.git
cd SmallRyeConfig-SpringBoot
./gradlew runBootJar

> Task :runBootJar FAILED
Exception in thread "main" java.lang.reflect.InvocationTargetException
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:118)
        at java.base/java.lang.reflect.Method.invoke(Method.java:580)
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:108)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:65)
Caused by: java.lang.ExceptionInInitializerError
        at fink.demo.smallryeconfigspringboot.ExampleApplication.main(ExampleApplication.java:13)
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
        ... 5 more
Caused by: java.io.UncheckedIOException: Failed to read C:\TEMP\test\SmallRyeConfig-SpringBoot\build\libs\SmallRyeConfig-SpringBoot-1.0.jar!\BOOT-INF\classes
        at io.smallrye.common.classloader.ClassPathUtils.processAsPath(ClassPathUtils.java:146)
        at io.smallrye.common.classloader.ClassPathUtils.consumeAsPath(ClassPathUtils.java:102)
        at io.smallrye.common.classloader.ClassPathUtils.consumeAsPaths(ClassPathUtils.java:86)
        at io.smallrye.config.AbstractLocationConfigSourceLoader.tryClassPath(AbstractLocationConfigSourceLoader.java:141)
        at io.smallrye.config.AbstractLocationConfigSourceLoader.loadConfigSources(AbstractLocationConfigSourceLoader.java:104)
        at io.smallrye.config.AbstractLocationConfigSourceLoader.loadConfigSources(AbstractLocationConfigSourceLoader.java:87)
        at io.smallrye.config.PropertiesConfigSourceProvider.<init>(PropertiesConfigSourceProvider.java:37)
        at fink.config.spring.SmallRyeConfigAutoConf.<clinit>(SmallRyeConfigAutoConf.java:75)
        ... 7 more
Caused by: java.nio.file.NoSuchFileException: C:\TEMP\test\SmallRyeConfig-SpringBoot\build\libs\SmallRyeConfig-SpringBoot-1.0.jar!\BOOT-INF\classes
        at jdk.zipfs/jdk.nio.zipfs.ZipFileSystem.<init>(ZipFileSystem.java:166)
        at jdk.zipfs/jdk.nio.zipfs.ZipFileSystemProvider.getZipFileSystem(ZipFileSystemProvider.java:125)
        at jdk.zipfs/jdk.nio.zipfs.ZipFileSystemProvider.newFileSystem(ZipFileSystemProvider.java:120)
        at java.base/java.nio.file.FileSystems.newFileSystem(FileSystems.java:528)
        at java.base/java.nio.file.FileSystems.newFileSystem(FileSystems.java:400)
        at io.smallrye.common.classloader.ClassPathUtils.processAsPath(ClassPathUtils.java:139)
        ... 14 more

FAILURE: Build failed with an exception.

(Not the best fix at all, but it helps)

git checkout fix
./gradlew runBootJar

⇒ works! App starts.

curl localhost:8080/key/java.version

The bug reports do not have a lot to make it clear exactly what is going on in your case. However based on what I know of Sprint Boot (which isn't much) and the stack traces, I wonder if the problem relates to multiple levels of JAR nesting?

I did find some minor problems in the processAsPath code (including the indexOf problem you found) but it's definitely unclear as to whether it could have caused your issue. I will be putting up a PR in a few minutes and you can test it out.

The nesting is not our problem: Spring Boot ClassLoader (class org.springframework.boot.loader.LaunchedURLClassLoader) handles it.
If you run spring boot jar
java -jar application.jar
You actually run spring boot loader classes, which install special ClassLoader, which can handle jar in jar and runs your application class.

Sure, the class loader aspect is fine, but the code that is failing in your stack trace also would need knowledge of how to handle this case, and currently it does not. Let me know if PR #293 fixes the issue for you.