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/baris 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#getJarClassPathchanged from something like:to
(usage of
PathAPIs)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-Pathmanifest 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#getJarClassPathusesFileSystems.getDefault().getPath(elt)(whereeltis each entry in thatClass-Pathattribute). ThisgetPathAPI 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-Pathentries is ourxxx-dev.jarfile 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
getRawPathis 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 thisifblock 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 ofgetRawPathseems 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 IOExceptionincom.sun.tools.javac.file.FSInfois throwing an exception (java.nio.file.InvalidPathException) which isn’t declared in its throws clause. I think it should just catch thisjava.nio.file.InvalidPathExceptionand rethrow it as anIOExceptionso that callers of thisgetJarClassPathcan then catch it and just log it and move on like in the case ofLocations.addJarClassPathhere 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!