Fixing a Bug in Java

I discovered a bug in how Java handles file paths on Windows that has existed for 22 years. I reported the bug, JDK-8262277, then I submitted a pull request fixing the bug which got accepted. I also submitted pull requests to Spring (which were accepted for version 5.3.5) working around the bug so users of that framework don’t need to wait for the JDK 17 release and for everyone to upgrade to it.

Encountering the Bug

My project is a Spring Boot-based command-line interface (CLI) application that’s intended to be run on any platform that Java can run on, including Windows, macOS, and Linux. The application accesses files and the path to which it writes those files is configurable using type-safe configuration properties, like this:

import lombok.Getter;
import lombok.Setter;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "myapp")
@Getter
@Setter
public class MyAppProperties {
    private Path settingsDirectory;
}

The value is provided in application.yml:

myapp.settings-directory: ${user.home}/.my-application

This works fine on Linux and MacOS. However, on Windows, it fails. Even this alternative, equivalent setting fails:

myapp.settings-directory: file://${user.home}/.my-application

I had stumbled upon the already reported “PathEditor does not handle absolute path on Windows” bug reported to Spring Framework two years earlier.

Of course, now that I discovered the issue, I need an easy way to reproduce the problem in my continuous integration run tests; otherwise, how could I tell for sure that it was solved and would never recur? Since I only had access to Linux platform test runners, I went down another rabbit hole: Testing a Java application on Windows without Windows.

Fixing the Problems in Spring

After a few debugging sessions, I realized that there were actually two problems.

The first, simpler problem is that Spring incorrectly handled “file:” URLs when converting values to Paths to non-existent files on Windows. I submitted a PR and that fix will be in Spring Framework 5.3.5.

The second, more interesting problem, was that ClassLoader.getResource can throw IllegalArgumentException. According to the Javadoc, ClassLoader.getResource should never throw IllegalArgumentException, so it’s not surprising the Spring wasn’t catching the exception and doing the right thing. I submitted a PR to Spring Framework so it would catch the exception and work correctly, and that change will also be in Spring Framework 5.3.5. However, that’s a workaround – the core bug in Java still existed.

Fixing the Bug in Java

According to the javadoc for the ClassLoader.getResource and ClassLoader.findResource methods, neither should throw IllegalArgumentException – they should return null if the resource can’t be resolved.

I managed to reduce the steps necessary to demonstrate this bug to just one line runnable in jshell, allowing the bug to be easily observed on any platform (not just Windows):

new java.net.URLClassLoader(new java.net.URL[]{new java.net.URL("https://repo1.maven.org/anything-that-ends-with-slash/")}).findResource("c:/windows");

With this information, I reported the bug to the Oracle OpenJDK project using their bug reporting process. The resulting bug is JDK-8262277: URLClassLoader.getResource throws undocumented IllegalArgumentException. Notably, this bug has existing since at least Java 1.2 meaning that this issue has existed for at least 22 years!

I also wanted to fix the bug so neither I nor anyone else would ever encounter this obscure bug ever again. I submitted a pull request to the OpenJDK project making the 2 line change and also adding tests to confirm the issue is resolved and does not recur due to a regression at some point in the future.

After a few rounds of review, OpenJDK merged the pull request, permanently fixing this issue as of Java 17.

Takeaways

  • The process for reporting bugs to Java is onerous and not user-friendly, probably explaining why some bugs, such as the one I encountered, go unreported. One has to go to https://bugreport.java.com/, accept some conditions, then fill out a long, multifield plaintext form. Markdown is not supported, nor is there a way to attach files. Then, some indeterminate amount of time later, an Oracle engineer will review the information and might create the issue in the official bug tracker giving it a JDK-XXX identifier, or the engineer may email the reporter asking for more information. In my case, a few exchanges with the engineer were necessary because Oracle’s email scanner kept mangling the Java code I sent the engineer to reproduce the issue. It’s pretty bad that Oracle’s email scanner mangles code when they tell reporters to send them code via email.
  • It’s worth the time to report bugs to projects, even when the process for doing so isn’t as easy as it should be. I’m positive that countless people have experienced this bug over the 22+ years it existed, but no one reported it. If they had, it certainly would have been fixed already and who knows how much time developers, users, administrators, etc would have saved over the years.
  • Fixing bugs in any software (as long as the source is available) is possible. That includes even seemingly intimidating, big projects like Java.

CC BY-SA 4.0 Fixing a Bug in Java by Craig Andrews is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.