smallrye / smallrye-common

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

Support multiple nesting of JAR files #293

Closed dmlloyd closed 6 months ago

dmlloyd commented 6 months ago

Fixes #289

dmlloyd commented 6 months ago

One thing I am unsure of is whether it is correct to treat URLs of form jar:file:///some/path/something.jar as valid (current behavior) or invalid.

magicprinc commented 6 months ago

TestApp still fails 😭 https://github.com/magicprinc/SmallRyeConfig-SpringBoot/tree/fix2

I have put your classes inside the app and removed jar with original classes from dependencies. You can debug and see details.

Exception in thread "main" java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    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)
    ... 8 more
Caused by: java.nio.file.ProviderNotFoundException: Provider not found
    at java.base/java.nio.file.FileSystems.newFileSystem(FileSystems.java:545)
    at java.base/java.nio.file.FileSystems.newFileSystem(FileSystems.java:400)
    at io.smallrye.common.classloader.ClassPathUtils.processAsJarPath(ClassPathUtils.java:163)
    at io.smallrye.common.classloader.ClassPathUtils.processAsJarPath(ClassPathUtils.java:169)
    at io.smallrye.common.classloader.ClassPathUtils.processAsPath(ClassPathUtils.java:149)
    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)
    ... 9 more

FAILURE: Build failed with an exception.
dmlloyd commented 6 months ago

Do you know what URL was input into the method?

magicprinc commented 6 months ago

jar:file:/D:/TEMP/SmallRyeConfig-SpringBoot/build/libs/SmallRyeConfig-SpringBoot-1.0.jar!/BOOT-INF/classes!/application.properties

BTW if you simply replace lastIndexOf with indexOf - everything works (spring magic 🤷‍♀️)

dmlloyd commented 6 months ago

Well, at least I was correct about it being a two-level nesting. I have an idea for a fix, but I'm going to add some additional unit tests first.

magicprinc commented 6 months ago

With IntelliJ IDEA you can debug jar file: you build spring jar with gradle bootJar, then you add Run/Debug configuration - JAR Application with path to your jar (e.g. D:/TEMP/SmallRyeConfig-SpringBoot/build/libs/SmallRyeConfig-SpringBoot-1.0.jar)

magicprinc commented 6 months ago

https://github.com/magicprinc/SmallRyeConfig-SpringBoot/tree/fixBundled

url: jar:file:/D:/TEMP/SmallRyeConfig-SpringBoot/build/libs/SmallRyeConfig-SpringBoot-1.0.jar!/BOOT-INF/classes!/application.properties

file: file:/D:/TEMP/SmallRyeConfig-SpringBoot/build/libs/SmallRyeConfig-SpringBoot-1.0.jar!/BOOT-INF/classes!/application.properties

jar: D:\TEMP\SmallRyeConfig-SpringBoot\build\libs\SmallRyeConfig-SpringBoot-1.0.jar

localPath: /BOOT-INF/classes!/application.properties


io.smallrye.common.classloader.ClassPathUtils#readStream

url: jar:file:///D:/TEMP/SmallRyeConfig-SpringBoot/build/libs/SmallRyeConfig-SpringBoot-1.0.jar!/BOOT-INF/classes!/application.properties

can be read

LOG [main] DEBUG io.smallrye.config - SRCFG01006: Loaded ConfigSource PropertiesConfigSource[source=jar:file:///D:/TEMP/SmallRyeConfig-SpringBoot/build/libs/SmallRyeConfig-SpringBoot-1.0.jar!/BOOT-INF/classes!/application.properties] with ordinal 100

dmlloyd commented 6 months ago

Can you determine whether the sub-entry /BOOT-INF/classes inside of file:///D:/TEMP/SmallRyeConfig-SpringBoot/build/libs/SmallRyeConfig-SpringBoot-1.0.jar is a JAR directory, or is it a nested JAR file? I am assuming that ! means to treat that entry as a JAR file but Spring Boot might not agree with that, but this would be an easy way to check.

magicprinc commented 6 months ago

image

Usual jar/zip directory

magicprinc commented 6 months ago

image

original/as-is dependency jars in a usual jar/zip directory

magicprinc commented 6 months ago

image

META-INF has an extra folder for itself :-)

- "dependencies":
  - "BOOT-INF/lib/"
- "spring-boot-loader":
  - "org/"
- "snapshot-dependencies":
- "application":
  - "BOOT-INF/classes/"
  - "BOOT-INF/classpath.idx"
  - "BOOT-INF/layers.idx"
  - "META-INF/"
dmlloyd commented 6 months ago

OK, this version should be able to detect whether a nested entry is a JAR or a directory and act accordingly.

dmlloyd commented 6 months ago

One more fix to prevent wrong path lookups if the ! is followed by a /.

magicprinc commented 6 months ago

Works!

jarPath: D:\TEMP\SmallRyeConfig-SpringBoot\build\libs\SmallRyeConfig-SpringBoot-1.0.jar path: /BOOT-INF/classes!/application.properties → localPath: /BOOT-INF/classes path: /BOOT-INF/classes!/application.properties

The code is quite complicated, so I hope you would have enough unit tests 😅

magicprinc commented 6 months ago

BTW, sorry for a noob question... Why do you need such heavy machinery (FileSystems, path parsing) As far as I know Spring uses only ClassLoader.getResources, .getResourceAsStream, etc

E.g. https://github.com/spring-projects/spring-framework/blob/main/spring-core/src/main/java/org/springframework/core/io/ClassPathResource.java

dmlloyd commented 6 months ago

That's a more complex question than it seems to be ;)

There are several answers though:

magicprinc commented 6 months ago

When is it possible to have snapshot/RC version in maven repo? 😥🙏

PS: IDEA recommends flipping couple of .equals → p -> "jar".equals(p.getScheme()) → "file".equals(fileUrl.getProtocol()

dmlloyd commented 6 months ago

I'm just doing some final testing with Quarkus to make sure nothing has broken.

dmlloyd commented 6 months ago

Looks good.