Affects: 5.3.17
I might have found an issue with classpath scanning of resources in a Bazel environment.
Here is some sample code:
URL resource1 = getClass().getResource("/files/resource1.txt");
LOG.debug("URL for '/files/resource1.text' is: {}", resource1);
URL resource2 = getClass().getResource("/files/resource2.txt");
LOG.debug("URL for '/files/resource2.text' is: {}", resource2);
final PathMatchingResourcePatternResolver resolver =
new PathMatchingResourcePatternResolver(MyBuilderCommand.class.getClassLoader());
Resource[] result = resolver.getResources("classpath*:/files/*.txt");
if (result.length == 0) {
LOG.error("No results returned from classpath for 'classpath*:/files/*.txt'");
}
In all cases, accessing via getClass().getResource(..)
always works. It's the resolver.getResources("classpath*:/files/*.txt")
call that's failing in some cases.
Here are steps to reproduce:
git clone git@github.com:salesforce/bazel-java-builder-template.git
# works as expected
bazel run :mybuilder -- --current-target=foo -i BUILD -o /var/tmp/ --verbose
# does not work
bazel run :mybuilder2 -- --current-target=foo -i BUILD -o /var/tmp/ --verbose
# building a all-in-one-jar also doesn't work
bazel-bin/mybuilder2_deploy.jar
java -jar bazel-bin/mybuilder2_deploy.jar --current-target=foo -i BUILD -o /var/tmp/ --verbose
The difference is only in packaging. So I believe it must be something within PathMatchingResourcePatternResolver
Comment From: bclozel
Sorry about the delay. I'm not familiar with Bazel. I assume that the issue is about how Bazel is structuring the JAR or the classpath. We can only consider this as a bug if we manage to reproduce the issue with a vanilla Java setup as we don't have explicit support for Bazel. Could you share a sample project (something we can git clone and run) that reproduces the problem, without Bazel being involved?
Comment From: guw
In the last step an "all-in-one" jar is produced. That works without Bazel.
java -jar bazel-bin/mybuilder2_deploy.jar --current-target=foo -i BUILD -o /var/tmp/ --verbose
It reproduces the problem.
Comment From: sbrannen
Hi @guw,
As Brian mentioned, please provide a sample project that reproduces the problem.
Example command-line arguments for a project that we cannot access are not sufficient for us to reproduce the issue.
Comment From: guw
@sbrannen Does git clone git@github.com:salesforce/bazel-java-builder-template.git
work for you? License is BSD-3 Clause so should be ok I hope.
Comment From: bclozel
Running the first command succeeds but creates an invalid fat JAR, as there is no manifest in it.
I'm getting the following stacktrace when running the second command:
java.lang.IllegalStateException: Resources not loaded correctly from classpath!
at com.salesforce.bazel.javabuilder.mybuilder.MyBuilderCommand.doResourceCollection(MyBuilderCommand.java:121)
at com.salesforce.bazel.javabuilder.mybuilder.MyBuilderCommand.call(MyBuilderCommand.java:60)
at com.salesforce.bazel.javabuilder.mybuilder.MyBuilderCommand.call(MyBuilderCommand.java:29)
at picocli.CommandLine.executeUserObject(CommandLine.java:1783)
at picocli.CommandLine.access$900(CommandLine.java:145)
at picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2150)
at picocli.CommandLine$RunLast.handle(CommandLine.java:2144)
at picocli.CommandLine$RunLast.handle(CommandLine.java:2108)
at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:1975)
at picocli.CommandLine.execute(CommandLine.java:1904)
at com.salesforce.bazel.javabuilder.mybuilder.MyBuilderInvoker$MyBuilderProcessor.processRequest(MyBuilderInvoker.java:22)
at com.salesforce.bazel.javabuilder.worker.GenericWorker.run(GenericWorker.java:106)
at com.salesforce.bazel.javabuilder.mybuilder.MyBuilderInvoker.main(MyBuilderInvoker.java:29)
Unfortunately, we can't justify spending time debugging your project if you're not investing yourself enough time to produce a minimal example (here, a vanilla java project just involving PathMatchingResourcePatternResolver
). I'm closing this issue as a result but we can reopen it if we can get a repro project for this problem.
Comment From: guw
@bclozel Apologies for the direct ping. Do you know if a directory entry in a jar is required?
We have a jar with this content: - META-INF/MANIFEST.MF - xsd/one.xsd - xsd/two.xsd
Spring is not finding the XSD files because there is no xsd/
directory entry inside the jar. The jar is produced by Bazel rules_pkg
. Question is, whether such jars should work in principle or are considered invalid.
Comment From: bclozel
I can't speak for the entire Java ecosystem but I can tell the following:
- the official zip spec doesn't make those folder entries mandatories or even mention them
- many tools add "0 length" file entries for folders ending with "/"
- the JDK
jar
tool does exactly that - when scanning for files in a location,
PathMatchingResourcePatternResolver
resolves a parent folder location and lists files in it - I would expect any tool creating a JAR file to follow the behavior and format of the JDK
jar
tool.
Comment From: guw
Thanks for confirming. I can see in the code now why it works that way.
ClassLoader.getResource
is called with the "root" directory (which is the directory immediately preceding the pattern). The getResource
call required the directory entry to exist in the jar file.
https://github.com/spring-projects/spring-framework/blob/ed5ab77397e773cfce3b17d00ceb8e84b0581aab/spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java#L539