Use "file" scheme absolute URIs for "Class-Path" entries we create in our -dev.jar #5808
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
(This PR will need a review and approval from @dmlloyd.)
Fixes #5359
Background:
jar
files in Java allow theMANIFEST.MF
file to contain aClass-Path
manifest attribute. The specific of this attribute states that relative URI is allowed here. We (Quarkus) currently set this up in theio.quarkus.maven.DevMojo#addToClassPaths
, (only) for our<app>-dev.jar
file. This jar file gets used in a few of different places/ways:<app>-dev.jar
is used to launch the dev mode using thejava
command:java -jar ... <app>-dev.jar
The
io.quarkus.dev.ClassLoaderCompiler
parses theClass-Path
manfiest attribute of this jar and uses it to setup classpath for triggering programatic compilation of the application, during hot deployment. This programatic compilation involves passing the classpath to thejavax.tools.JavacCompiler
.When the jar file paths (parsed out of
#2
above) are passed to thejavax.tools.JavacCompiler
, the implementation in OpenJDK, internally goes ahead and parses each of those passed jar files (again) for its ownClass-Path
attribute parsing and adding that path to the compilation. As part of Work around an issue in JDK which causes hot deployment compilation to fail #4527, we explicitly avoid passing the<app>-dev.jar
to the programatic compilation throughJavacCompiler
since although Quarkus sets up thisClass-Path
attribute correctly in that jar file, due to a bug in the JDK it ends up causing issues (on Windows).Current state:
With our current upstream, things work fine with Java 8, 11 and 13 for *nix systems. However, on Windows
#1
(above) is broken starting Java 13 (as stated in #5359). Again at this point, based on discussions on OpenJDK list, this still doesn't appear to be a Quarkus fault (http://mail.openjdk.java.net/pipermail/core-libs-dev/2019-November/063566.html).Proposal:
As noted in that OpenJDK dev mailing list, it's now considered a valid thing to use an absolute URI in the
Class-Path
attribute if that entry is offile
protocol/scheme and the "context jar" (in our case it would be the<app>-dev.jar
) is loaded from the filesystem (https://docs.oracle.com/en/java/javase/13/docs/specs/jar/jar.html#class-path-attribute). Note that, although this is documented in the specification only starting Java 13, this works (as an implementation detail) even in previous versions of Java (like Java 8 and 11).This PR now uses a absolute
file
scheme URI path when it creates theClass-Path
for the<app>-dev.jar
. Furthermore, theClassLoaderCompiler
, which deals with hot deployment, now first checks if entries that it finds are absolutefile
scheme URIs, in which case it treats it as a filesystem path and uses it, instead of trying to resolve a relative URI.Encoding:
The original code in
DevMojo
made sure that the encoding was done correctly while writing out theClass-Path
entry. I've verified that the changed code when writing out thefile
scheme URI does indeed write out encoded path. More specifically, I have verified thatfinal URI uri = file.toPath().toAbsolutePath().toUri();
contains an encoded path (ex: space characters are replaced with%20
), so when thaturi.toString()
is used to write out the entry, the encoded path gets added to theClass-Path
attribute.Additionally, the previous code had an explicit check to see if the file is a directory and if so, it added a
/
(if it wasn't present) to the classpath entry. I've verified that this isn't needed with the changed code since theURI
returned byfile.toPath().toAbsolutePath().toUri()
already does that.Testing:
The good part - We already have
io.quarkus.maven.it.DevMojoIT
test case inintegration-tests/maven
which has a good coverage for testing thequarkus:dev
launching and hot replacement feature (both of which are impacted by this issue/change). So why wasn't this caught earlier? Our CI doesn't run against Java 13 as far as I know. In fact, I just ran thisDevMojoIT
test on latest upstream against a Windows OS using Java 13 and it fails exactly as what's reported in (#5359):After the change that's proposed in this PR, I have run this
DevMojoIT
test against the same Windows OS setup using Java 8, 11 and 13. All passed fine for this test. So that gives us a bit of confidence that this won't/shouldn't break anything obvious with dev mode and hot replacement on Windows.I have also run this against a local *nix setup with Java 13, just to make sure it doesn't cause any issues there. The test passed here too.
Future:
If this PR does get approved and merged, I don't know if we will go back to using relative URIs in the
Class-Path
for our<app>-dev.jar
, especially sincefile
scheme absolute URIs are now allowed at least in the context of how we use dev mode jar. @dmlloyd is a better person to decide that.But for now, I don't see any other workaround to get Quarkus usable across Java 8, 11 and 13, without this change.