quarkus: Hot reload fails with InvalidPathException on Windows
Starting with Quarkus 0.21.0 hot reload failes with InvalidPathException (at least) on Windows.
When a Quarkus application runs in dev mode, i. e. using mvn quarkus:dev, a REST call will trigger a hot reload, if one of the source files has changed before. That worked like a charm up to version 0.20.0. With 0.21.0 the REST response contains an error message:
`<header>
Error restarting Quarkus
<div class="exception-message">java.nio.file.InvalidPathException: Illegal char <:> at index 2: /C:/Users/dw/.m2/repository/io/quarkus/arc/arc/0.21.0/arc-0.21.0.jar
</div> </header>`As the message complains about the ‘:’ in the file path, I guess, the problem arises on Windows only.
The bug can easily be reproduced:
- Use any of the applications from the guide, e. g getting-started
- Make sure that quarkus.version in pom.xml is 0.21.0
- Build the application and start dev mode (mvn clean package quarkus:dev)
- Make some code change
- Call a REST endpoint (curl http://localhost:8080/hello)
My environment:
- Windows 10 Pro
- AdoptOpenJDK 8 (jdk8u202-b08) and 11 (jdk-11.0.3+7)
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 1
- Comments: 79 (53 by maintainers)
Commits related to this issue
- Fix invalid path on Windows Fixes #3592 — committed to dmlloyd/quarkus by dmlloyd 5 years ago
- Fix invalid path on Windows Fixes #3592 — committed to dmlloyd/quarkus by dmlloyd 5 years ago
- Fix invalid path on Windows Fixes #3592 — committed to dmlloyd/quarkus by dmlloyd 5 years ago
- Fix invalid path on Windows Fixes #3592 — committed to dmlloyd/quarkus by dmlloyd 5 years ago
- Correctly add class path entries Fixes #3592 — committed to dmlloyd/quarkus by dmlloyd 5 years ago
- Correctly add class path entries Fixes #3592 — committed to dmlloyd/quarkus by dmlloyd 5 years ago
- Correctly add class path entries Fixes #3592 — committed to dmlloyd/quarkus by dmlloyd 5 years ago
- Correctly add class path entries Fixes #3592 — committed to dmlloyd/quarkus by dmlloyd 5 years ago
- Correctly add class path entries Fixes #3592 — committed to quarkusio/quarkus by dmlloyd 5 years ago
I get the same error with 0.23.1 in Windows 10 and OpenJDK 11 with a fresh generated quarkus application. I just switched the version to 0.23.1: java.nio.file.InvalidPathException: Illegal char <:> at index 2: /C:/Users/Marcel/.m2/repository/io/quarkus/quarkus-netty/0.23.1/quarkus-netty-0.23.1.jar at java.base/sun.nio.fs.WindowsPathParser.normalize(WindowsPathParser.java:182) at java.base/sun.nio.fs.WindowsPathParser.parse(WindowsPathParser.java:153) at java.base/sun.nio.fs.WindowsPathParser.parse(WindowsPathParser.java:77) at java.base/sun.nio.fs.WindowsPath.parse(WindowsPath.java:92) at java.base/sun.nio.fs.WindowsFileSystem.getPath(WindowsFileSystem.java:229) at jdk.compiler/com.sun.tools.javac.file.FSInfo.getJarClassPath(FSInfo.java:112) at jdk.compiler/com.sun.tools.javac.file.CacheFSInfo.getJarClassPath(CacheFSInfo.java:93) at jdk.compiler/com.sun.tools.javac.file.Locations$SearchPath.addJarClassPath(Locations.java:423) at jdk.compiler/com.sun.tools.javac.file.Locations$SearchPath.addFile(Locations.java:413) at jdk.compiler/com.sun.tools.javac.file.Locations$SearchPath.addFiles(Locations.java:345) at jdk.compiler/com.sun.tools.javac.file.Locations$SearchPath.addFiles(Locations.java:352) at jdk.compiler/com.sun.tools.javac.file.Locations$SimpleLocationHandler.setPaths(Locations.java:724) at jdk.compiler/com.sun.tools.javac.file.Locations.setLocation(Locations.java:2098) at jdk.compiler/com.sun.tools.javac.file.JavacFileManager.setLocation(JavacFileManager.java:927) at io.quarkus.dev.JavaCompilationProvider.compile(JavaCompilationProvider.java:48) at io.quarkus.dev.ClassLoaderCompiler.compile(ClassLoaderCompiler.java:157) at io.quarkus.dev.RuntimeUpdatesProcessor.checkForChangedClasses(RuntimeUpdatesProc
The JDK bug is https://bugs.openjdk.java.net/browse/JDK-8232925 (thanks @jaikiran!)
Excellent! Thank you for testing this out. I’ll send a PR with that fix in a while.
I was reading some (java)docs of File/URL and there’s a very small mention about “standard case for drive letters” on Windows OS here[1]. So it does seem that the drive letter is playing a role here. I have a fix for it here https://github.com/jaikiran/quarkus/commit/57d31637bb3d41a8785e254c21c34eaacc935003. I just need to see how to deliver that to you for a quick test with instructions.
[1] https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/File.html#getCanonicalPath()
I submitted a JDK bug for the issue where it can’t understand drive letters in the URI. I don’t have a bug ID yet though.
@geoand - Yes, I’m experimenting a few ways to get past this, one of them being what @dmlloyd just suggested 😃 I’ll update this thread once I have something.
That’s expected/normal. This is a relative URI (even though it’s an absolute path… a relative URI is one with no “scheme” component), so according to the JAR specification, this is allowed.
Yes, it handles the URI encoding for us, essentially creating a relative URI as the JAR file specification demands. Note that the block you mentioned is not meant to be entered for the
/C:
case, it’s meant to be entered in theC:
case (becauseC:/foo/bar
is syntactically indistinguishable from an absolute URI, which is disallowed; by prepending a/
we make it into a relative URI).I think they will: the JAR specification does clearly state that the URI is relative. They should be treating the string as a URI rather than a filesystem path; that’s the beginning and end of the problem AFAICT.
@dmlloyd, I was able to get my hands on a Windows system and I was able to reproduce this consistently and narrow this down. An important higher level detail - this is reproducible only on recent (definitely on Java 11 but probably since Java 9?) JDK versions. It probably is because the code in recent JDK versions of
com.sun.tools.javac.file.FSInfo#getJarClassPath
changed from something like:to
(usage of
Path
APIs)More details follow:
(previously in this issue, you commented):
From what I see in the JDK code, yes, this I believe is what’s happening.
(previously in this issue, you commented):
Partially correct. What seems to be happening is - there’s one (and just one I think) of those 100+ jars on the classpath which has a
Class-Path
manifest attribute of the form:Class-Path: /C:/Users/Administrator/.m2/repository/io/quarkus/arc/arc/0.23.2/arc-0.23.2.jar /C:/Users/Administrator/.m2/repository/io/quarkus/quarkus-arc/0.23.2/quarkus-arc-0.23.2.jar
(rest of the entries snipped, since the pattern is similar)
As you rightly note - (in recent versions of JDK), the
com.sun.tools.javac.file.FSInfo#getJarClassPath
usesFileSystems.getDefault().getPath(elt)
(whereelt
is each entry in thatClass-Path
attribute). ThisgetPath
API doesn’t support the path parsing if it starts with the/
character like above.The only (I think) jar file which has this kind of
Class-Path
entries is ourxxx-dev.jar
file that we generate in the dev mojo in the code here https://github.com/quarkusio/quarkus/blob/master/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java#L495.I’m not an expert when it comes to the URI API (it has always confused me and I have had to keep going back to experiemnts to understand how it behaves), but I’ve read the linked comment here https://github.com/quarkusio/quarkus/issues/1673#issuecomment-477205320. I’m not too sure why the
getRawPath
is used. From what I have debugged so far on Windows, thegetRawPath()
actually returns (at least on Windows) a value of the form/C:/Users/Administrator/.m2/repository/io/quarkus/arc/arc/0.23.2/arc-0.23.2.jar
(notice the starting/
). Because of that, I haven’t seen it ever enter thisif
block here https://github.com/quarkusio/quarkus/blob/master/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java#L497. So, that Windows check is virtually a no-op. But of course, that isn’t causing the problem here - the usage ofgetRawPath
seems to be the root cause. Are we using it so as to let it handle the space characters (by encoding it)?Keeping all this aside, I think this is a bug in the JDK code itself. Specifically, the
public List<Path> getJarClassPath(Path file) throws IOException
incom.sun.tools.javac.file.FSInfo
is throwing an exception (java.nio.file.InvalidPathException
) which isn’t declared in its throws clause. I think it should just catch thisjava.nio.file.InvalidPathException
and rethrow it as anIOException
so that callers of thisgetJarClassPath
can then catch it and just log it and move on like in the case ofLocations.addJarClassPath
here https://github.com/openjdk/jdk/blob/master/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java#L425. This is an internal API of the JDK, so I don’t know if this will be accepted as a bug, but I’ll raise it in the OpenJDK compiler-dev list and ask for inputs.Problem still exists at Quarkus 0.21.2.
Error restarting Quarkus java.nio.file.InvalidPathException: Illegal char <:> at index 2: /C:/Users/Barbaros/.m2/repository/io/quarkus/arc/arc/0.21.2/arc-0.21.2.jar
java.nio.file.InvalidPathException: Illegal char <:> at index 2: /C:/Users/Barbaros/.m2/repository/io/quarkus/arc/arc/0.21.2/arc-0.21.2.jar at java.base/sun.nio.fs.WindowsPathParser.normalize(WindowsPathParser.java:182) at java.base/sun.nio.fs.WindowsPathParser.parse(WindowsPathParser.java:153) at java.base/sun.nio.fs.WindowsPathParser.parse(WindowsPathParser.java:77) at java.base/sun.nio.fs.WindowsPath.parse(WindowsPath.java:92) at java.base/sun.nio.fs.WindowsFileSystem.getPath(WindowsFileSystem.java:229) at jdk.compiler/com.sun.tools.javac.file.FSInfo.getJarClassPath(FSInfo.java:112) at jdk.compiler/com.sun.tools.javac.file.CacheFSInfo.getJarClassPath(CacheFSInfo.java:93) at jdk.compiler/com.sun.tools.javac.file.Locations$SearchPath.addJarClassPath(Locations.java:423) at jdk.compiler/com.sun.tools.javac.file.Locations$SearchPath.addFile(Locations.java:413) at jdk.compiler/com.sun.tools.javac.file.Locations$SearchPath.addFiles(Locations.java:345) at jdk.compiler/com.sun.tools.javac.file.Locations$SearchPath.addFiles(Locations.java:352) at jdk.compiler/com.sun.tools.javac.file.Locations$SimpleLocationHandler.setPaths(Locations.java:724) at jdk.compiler/com.sun.tools.javac.file.Locations.setLocation(Locations.java:2098) at jdk.compiler/com.sun.tools.javac.file.JavacFileManager.setLocation(JavacFileManager.java:927) at io.quarkus.dev.JavaCompilationProvider.compile(JavaCompilationProvider.java:48) at io.quarkus.dev.ClassLoaderCompiler.compile(ClassLoaderCompiler.java:156) at io.quarkus.dev.RuntimeUpdatesProcessor.checkForChangedClasses(RuntimeUpdatesProcessor.java:164) at io.quarkus.dev.RuntimeUpdatesProcessor.doScan(RuntimeUpdatesProcessor.java:103) at io.quarkus.undertow.deployment.devmode.UndertowHotReplacementSetup.handleHotDeploymentRequest(UndertowHotReplacementSetup.java:72) at io.quarkus.undertow.deployment.devmode.UndertowHotReplacementSetup$1$1.handleRequest(UndertowHotReplacementSetup.java:61) at io.undertow.server.Connectors.executeRootHandler(Connectors.java:376) at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:830) at io.quarkus.runtime.CleanableExecutor$CleaningRunnable.run(CleanableExecutor.java:224) at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35) at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2011) at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1535) at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1426) at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29) at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29) at java.base/java.lang.Thread.run(Thread.java:834) at org.jboss.threads.JBossThread.run(JBossThread.java:479)
Tested in;
No, I don’t think it’s the same issue as #3561. Looks like a different one.
@dirkweil could you provide the full stacktrace? No Windows box here 😃.
Thanks!