The DefaultResourceLoader throws a FileNotFoundException when an application runs in native mode and attempts to load resources from an external jar /data/app.jar via resourceLoader.getResource("jar:file:/data/app.jar!/").

Stacktrace:

java.lang.IllegalStateException: Failed to execute ApplicationRunner
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:761) ~[de.darkatra.resourceloaderissue.ResourceLoaderIssueApplication:3.0.1]
    at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:748) ~[de.darkatra.resourceloaderissue.ResourceLoaderIssueApplication:3.0.1]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[de.darkatra.resourceloaderissue.ResourceLoaderIssueApplication:3.0.1]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302) ~[de.darkatra.resourceloaderissue.ResourceLoaderIssueApplication:3.0.1]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291) ~[de.darkatra.resourceloaderissue.ResourceLoaderIssueApplication:3.0.1]
    at de.darkatra.resourceloaderissue.ResourceLoaderIssueApplication.main(ResourceLoaderIssueApplication.java:21) ~[de.darkatra.resourceloaderissue.ResourceLoaderIssueApplication:na]
 Caused by: java.io.FileNotFoundException: class path resource [jar:file:/data/app.jar!/] cannot be resolved to URL because it does not exist
    at org.springframework.core.io.ClassPathResource.getURL(ClassPathResource.java:226) ~[de.darkatra.resourceloaderissue.ResourceLoaderIssueApplication:6.0.3]
    at de.darkatra.resourceloaderissue.ResourceLoaderIssueApplication.run(ResourceLoaderIssueApplication.java:32) ~[de.darkatra.resourceloaderissue.ResourceLoaderIssueApplication:na]
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:758) ~[de.darkatra.resourceloaderissue.ResourceLoaderIssueApplication:3.0.1]
    ... 5 common frames omitted

It seems like the DefaultResourceLoader mistakenly thinks that the resource is a ClassPathResource as, when running on the JVM, the loader correctly returns a UrlResource.

Reproducer with details: https://github.com/DarkAtra/spring-native-resource-loader-issue Affected Spring Boot versions: 3.0.0, 3.0.1

Comment From: bclozel

I believe this is a known limitation of GraalVM, the "jar" protocol is not supported at runtime in a native image with the default options. Trying to resolve a URL with a "jar" protocol will throw an exception with the following message:

malformed URL, message:Accessing an URL protocol that was not enabled. The URL protocol jar is not tested and might not work as expected. It can be enabled by adding the --enable-url-protocols=jar option to the native-image command.

Because the exception itself is a MalformedUrlException, we can't really tune the resource loader to report it, as it implements fallback behavior for other cases.

You can configure your application to build with the following option:

graalvmNative {
  nativeBuild {
    binaries {
      main {
        buildArgs.add('--enable-url-protocols=jar')
      }
    }
  }
}

Or you can also choose to deal with a JarFile instance directly:

Resource resource = this.resourceLoader.getResource("file:/data/app.jar");
try (JarFile jar = new JarFile(resource.getFile())) {
    //...
}

I've added this information to the Spring Boot with GraalVM wiki page. I'm closing this issue as I don't think we can improve the situation in Spring Framework.