Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spring PathMatchingResourcePatternResolver does not return the subdirectory of a URL resource. #10943

Closed
nielsbasjes opened this issue Jul 23, 2020 · 13 comments · Fixed by #10973
Labels
area/spring Issues relating to the Spring integration kind/bug Something isn't working
Milestone

Comments

@nielsbasjes
Copy link

Describe the bug
I use org.springframework.core.io.support.PathMatchingResourcePatternResolver to find resources packaged inside a jar which are in a subdirectory.

When I do this in a normal Java application the resource that is found correctly looks like this:

URL [jar:file:/home/nbasjes/.m2/repository/org/acme/resourcelib/1.0-SNAPSHOT/resourcelib-1.0-SNAPSHOT.jar!/Content/world.txt]

yet when I run the exact same code in a Quarkus application it finds the file yet the returned resource does not have the subdirectory of the file.

URL [jar:file:/home/nbasjes/.m2/repository/org/acme/resourcelib/1.0-SNAPSHOT/resourcelib-1.0-SNAPSHOT.jar!/world.txt]

So the difference is that in the Quarkus setup the resource is not reported in the actual subdirectory it is really stored in.
Thus: loading the resource will fail with a file not found.

Expected behavior
The located resources are found in the correct place.

Actual behavior
The subdirectory name is missing in the path of the found resources.

To Reproduce
https://github.com/nielsbasjes/BugReport-SpringQuarkus-ResourceLoading

Environment (please complete the following information):

  • Output of uname -a or ver: Linux lw7-06766 4.4.0-185-generic #215-Ubuntu SMP Mon Jun 8 21:53:19 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
  • Output of java -version: openjdk version "1.8.0_252" OpenJDK Runtime Environment (build 1.8.0_252-b09) OpenJDK 64-Bit Server VM GraalVM CE 20.1.0 (build 25.252-b09-jvmci-20.1-b02, mixed mode)
  • GraalVM version (if different from Java):
  • Quarkus version or git rev: 1.6.1.Final
  • Build tool (ie. output of mvnw --version or gradlew --version): Apache Maven 3.6.2

Additional context
This was reported as a bug in the library I have written: nielsbasjes/yauaa#216

@nielsbasjes nielsbasjes added the kind/bug Something isn't working label Jul 23, 2020
@quarkusbot quarkusbot added the area/spring Issues relating to the Spring integration label Jul 23, 2020
@quarkusbot
Copy link

/cc @geoand

@nielsbasjes
Copy link
Author

I am unsure if this problem is a SpringFramework, a Quarkus or perhaps in the combination of the two.
So I have submitted this problem to both projects:

@geoand
Copy link
Contributor

geoand commented Jul 23, 2020

I would need to look into the source of that specific Spring class to see what it does, but in general this is not one of the classes we support / test.

One more question, in what mode does this happen? When running the application via java -jar ... or via mvn quarkus:dev?

@nielsbasjes
Copy link
Author

This problem happens when doing:

It finds URL [jar:file:/home/nbasjes/.m2/repository/org/acme/resourcelib/1.0-SNAPSHOT/resourcelib-1.0-SNAPSHOT.jar!/hello.txt] in those two.

It works as intended when doing:

  • mvn clean package -DskipTests=true
    java -jar target/getting-started-1.0-SNAPSHOT-runner.jar
    and then going to http://0.0.0.0:8080/hello

It finds URL [jar:file:/home/nbasjes/workspace/Prive/BugReports/QuarkusResourceLoading/quarkus-test/target/lib/org.acme.resourcelib-1.0-SNAPSHOT.jar!/Content/world.txt]

Notable difference is that in the first two the jar file is loaded from the local maven repository. In the last case it is loaded from the target/lib of the project that is built.

@geoand
Copy link
Contributor

geoand commented Jul 24, 2020

I'll take a look, it might be some subtle misbehavior of the Quarkus Classloader

@geoand
Copy link
Contributor

geoand commented Jul 24, 2020

@stuartwdouglas it seems like the Quarkus ClassLoader behaves differently than the URLClassLoader on getResources() when the path ends with / - basically the Quarkus CL strips the slash and the returned URLs that don't contain it - which then trips PathMatchingResourcePatternResolver.

Do you remember why sanitizeName strips the ending slash? Should I just remove that sanitization and see what happens in CI?

I am not really worried about PathMatchingResourcePatternResolver (which we don't support), the reason I brought this up is because of the different behavior of the Quarkus Classloader and the system classloader (thus getting different behavior in dev / test mode vs prod mode).

@nielsbasjes
Copy link
Author

I updated my reproduction project: I move the resources one directory level deeper.
/Content/world.txt --> /Content/deeper/world.txt

Now the Java test shows
URL [jar:file:/home/nbasjes/.m2/repository/org/acme/resourcelib/1.0-SNAPSHOT/resourcelib-1.0-SNAPSHOT.jar!/Content/deeper/world.txt]

Which contains the correct /Content/deeper/world.txt

The Quarkus variant shows

URL [jar:file:/home/nbasjes/.m2/repository/org/acme/resourcelib/1.0-SNAPSHOT/resourcelib-1.0-SNAPSHOT.jar!/deeper/world.txt]

Which contains the incorrect /deeper/world.txt

So apparently the first directory level is stripped.
@geoand Does this effect match what you described?

@geoand
Copy link
Contributor

geoand commented Jul 24, 2020

@geoand Does this effect match what you described?

Pretty much yeah

stuartwdouglas added a commit to stuartwdouglas/quarkus that referenced this issue Jul 27, 2020
URLClassLoader will add a trailing / to the
URL if the passed in name contains a /.

This makes our behaviour mirror this.

It also adds a check that if the name ends with
a / we only return directory entries.

Fixes quarkusio#10943
@stuartwdouglas
Copy link
Member

@geoand it looks like at some point we added this explicitly for file system directories: 4e58a7d (although the message for that commit seems to be the opposite to what the commit actually does).

I checked UrlClassLoader and the presence or absence of a '/' seems to depend on what is passed in, if the resource name has a trailing slash then it will be present, otherwise it won't. I have opened a PR to attempt to duplicate this behaviour.

stuartwdouglas added a commit to stuartwdouglas/quarkus that referenced this issue Jul 27, 2020
URLClassLoader will add a trailing / to the
URL if the passed in name contains a /.

This makes our behaviour mirror this.

It also adds a check that if the name ends with
a / we only return directory entries.

Fixes quarkusio#10943
@geoand
Copy link
Contributor

geoand commented Jul 27, 2020

Thanks @stuartwdouglas

@geoand
Copy link
Contributor

geoand commented Jul 27, 2020

I'm wondering if a dev mode test should be added based on the reproducer from this issue.

My thinking is that if we are testing the use of PathMatchingResourcePatternResolver, then we should be covering a good amount of integration code out there. WDYT?

@stuartwdouglas
Copy link
Member

I have opened #11009 that adds a test around the URL behaviour.

@geoand
Copy link
Contributor

geoand commented Jul 28, 2020

Excellent, thanks

@gsmet gsmet added this to the 1.7.0 - master milestone Jul 28, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/spring Issues relating to the Spring integration kind/bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants