smallrye / smallrye-common

Common utilities for SmallRye
Apache License 2.0
21 stars 24 forks source link

Doesn't work in Spring Boot jar #289

Closed magicprinc closed 6 months ago

magicprinc commented 6 months ago

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: https://github.com/smallrye/smallrye-config/issues/1101

PS: more advanced research and fix are welcome 🙏

magicprinc commented 6 months ago

Fixed version: https://github.com/magicprinc/smallrye-common/blob/main/classloader/src/main/java/io/smallrye/common/classloader/ClassPathUtils.java

magicprinc commented 6 months ago

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

magicprinc commented 6 months ago

@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
dmlloyd commented 6 months ago

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.

magicprinc commented 6 months ago

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.

dmlloyd commented 6 months ago

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.