Affects: 5.3.13
ClasspathResource.getFile()
returns null
when called from inside a Boot Jar and referencing a file inside that Jar. It works fine when the unpacked App is launched.
It would be nice to have consistent behaviour here - perhaps getFile()
should simply throw an exception in this case?
(also, perhaps it's time to add a getPath()
to the API, which could return a jar:file:
java.nio.Path
in this scenario, and deprecate the getFile()
method? The suggested replacement being getPath().toFile()
)
Comment From: snicoll
@crizzis this works as expected. ClasspathResource.getFile()
is returning a File
and an entry in a zip isn't one. Resource provides you ways to read the content of the entry, regardless of it being a file or a zip entry.
Comment From: jhoeller
I'm wondering why getFile()
would ever return null
though? @crizzis could you elaborate on how you arrived there since the implementation is really always meant to return a File
or throw an exception, and as far as I see our standard implementations strictly follow that guideline?
Comment From: jhoeller
As for java.nio.file.Path
, that's generally filesystem-bound just like java.io.File
, the main difference being the abstraction over different kinds of file systems. It's not really meant as an accessor for resources in a jar file. We expose getURI()
as a common denominator for any kind of resource already, whereas getFile()
is a special-purpose integration method for existing java.io.File
based code. That said, we accept a java.nio.file.Path
as input for building a FileSystemResource
, which we see as more common. Integrators with Spring's Resource abstraction should rather remain InputStream and/or URI based, avoiding getFile()
as far as they can already, and therefore not really dealing with java.nio.file.Path
either.
Comment From: crizzis
@jhoeller My bad, I now revisited the broken implementation and realized it was actually a java.io.FileNotFoundException: class path resource [addAudienceRequestInterceptor.js] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/platform-users.jar!/BOOT-INF/classes!/addAudienceRequestInterceptor.js
@snicoll I'm not asking for ClasspathResource
to return a File
when referencing a JAR entry. I'm asking for it to behave consistently regardless of the packaging (or lack thereof) of the Application. If the consistent behavior is to always throw an exception, then so be it.
Otherwise, you get a false sense of security using getFile()
on a ClasspathResource
that even the integration tests (which are typically run against the non-packaged code) fail to dispel. You only realize something is wrong after actually packaging and running your JAR.
Comment From: snicoll
I'm not asking for ClasspathResource to return a File when referencing a JAR entry. I'm asking for it to behave consistently regardless of the packaging (or lack thereof) of the Application
I have no idea how you're expecting us to do that. A ClassPathResource that was built from an exploded directory isn't the same as a ClassPathResource that came from a zip entry. The Resource
abstraction is providing a isFile
. It may be true
, it may be false
. The javadoc of getFile
states:
Throws: java.io.FileNotFoundException – if the resource cannot be resolved as absolute file path, i.e. if the resource is not available in a file system
The javadoc of getFile
also hints you at getInputStream
. If the resource you have at hand isn't a file, then it will throw the exception, matching the Javadoc element that I've described. You can check upfront using isFile
.
Comment From: crizzis
@snicoll
I have no idea how you're expecting us to do that.
Ummm...
public class ClassPathResource extends AbstractFileResolvingResource {
...
@Override
public File getFile() throws IOException {
throw new UnsupportedOperationException("Classpath resources cannot be resolved to files");
}
}
A ClassPathResource that was built from an exploded directory isn't the same as a ClassPathResource that came from a zip entry
Well, yeah, this is the crux of the problem. That you cannot 'extract' a File
from it should be a limitation of a ClasspathResource
IMHO, to avoid the problems I had described. I cannot see the benefit of having any isFile
implementation for a ClasspathResource
if its contract is best-effort-based, but maybe I'm missing something.