【开发问题记录】: : Invalid file path 的问题定位

时间:2025-02-22 15:24:39

1 环境

os : window 10
jdk : JDK-8u331
SpringBoot: 2.0.

2 现象

一个 SpringBoot 的项目, 使用了 undertown 作为 web 容器的项目。
项目启动成功后, 第一次发起 http 请求接口, 任意一个接口都可以, 会直接抛出如下异常:
nested exception is : : Invalid file path

: Handler dispatch failed; nested exception is : : Invalid file path
 at (:982)
 at (:901)
 at (:970)
 at (:872)
 at (:707)
 at (:846)
 at (:790)
 at (:85)
 at $(:129)
 at (:55)
 at (:107)
 at (:61)
 at $(:131)
 at (:110)
 at (:107)
 at (:61)
 at $(:131)
 at com.(:116)
 at (:61)
 at $(:131)
 at com.(:153)
 at (:61)
 at $(:131)
 at (:197)
 at (:107)
 at (:61)
 at $(:131)
 at (:106)
 at (:107)
 at (:61)
 at $(:131)
 at (:84)
 at (:62)
 at (:36)
 at (:131)
 at (:57)
 at (:43)
 at (:46)
 at (:64)
 at (:60)
 at (:77)
 at (:43)
 at (:43)
 at (:43)
 at (:292)
 at $100(:81)
 at $(:138)
 at $(:135)
 at $(:48)
 at $(:43)
 at (:272)
 at $000(:81)
 at $(:104)
 at (:211)
 at $(:809)
 at (Unknown Source)
 at $(Unknown Source)
 at (Unknown Source)

后续无论发送多少次请求, 则都是抛出另一个异常
nested exception is : Could not initialize class

: Handler dispatch failed; nested exception is : Could not initialize class 
 at (:982)
 at (:901)
 at (:970)
 at (:872)
 at (:707)
 at (:846)
 at (:790)
 at (:85)
 at $(:129)
 at (:55)
 at (:107)
 at (:61)
 at $(:131)
 at (:110)
 at (:107)
 at (:61)
 at $(:131)
 at com.(:116)
 at (:61)
 at $(:131)
 at com.(:153)
 at (:61)
 at $(:131)
 at (:197)
 at (:107)
 at (:61)
 at $(:131)
 at (:106)
 at (:107)
 at (:61)
 at $(:131)
 at (:84)
 at (:62)
 at (:36)
 at (:131)
 at (:57)
 at (:43)
 at (:46)
 at (:64)
 at (:60)
 at (:77)
 at (:43)
 at (:43)
 at (:43)
 at (:292)
 at $100(:81)
 at $(:138)
 at $(:135)
 at $(:48)
 at $(:43)
 at (:272)
 at $000(:81)
 at $(:104)
 at (:211)
 at $(:809)
 at (Unknown Source)
 at $(Unknown Source)
 at (Unknown Source)

这里面有 2 个问题

  1. 请求进来后, 为什么会导致异常
  2. 第一次异常为 FileNotFoundException, 第二次及后续相同的请求, 抛出的则是 NoClassDefFoundError, 2 种不同的异常, 第一次特殊了

3 第一个问题

通过异常日志定位到出现异常的地方为

public final class Conduits {

    static {
        NULL_FILE_CHANNEL = AccessController.doPrivileged(new PrivilegedAction<FileChannel>() {
            public FileChannel run() {
                final String osName = System.getProperty("", "unknown").toLowerCase(Locale.US);
                try {
                    if (osName.contains("windows")) {
                        // 抛出异常的位置
                        return new FileOutputStream("NUL:").getChannel();
                    } else {
                        return new FileOutputStream("/dev/null").getChannel();
                    }
                } catch (FileNotFoundException e) {
                    throw new IOError(e);
                }
            }
        });
    }
}

抛出异常的位置 new FileOutputStream(“NUL:”).getChannel()。 一行代码, 大体的逻辑为: 在 windows 系统中, 打开 NUL: 这个文件, 获取到其通道。
经过查询, 这里的 NUL:, 所在的文件路径为 C:\Windows\System32\drivers\。

 文件的作用: It allows a user to trivially throw away program output, or to supply empty input. 

和 Linux 的 /dev/null作用类似, 可以接收任意的输入, 不产生输出。Linux 中经常将一下无用的日志都指向这个位置!

初步怀疑是 这个文件有异常, 从网上下载了一个正常的, 进行替换后, 还是同样的问题, 也就是 文件正常。
排除是 文件的问题后。 猜测是否为 new FileOutput(“NUL:”), JDK 在实现上有问题。

最终在 * 的 "Java: FileOutputStream(“NUL:”) not working after Java upgrade " 这篇文章中找到了问题的解决方案。
同时顺藤摸瓜定位到官网中, 找到了问题所在, 的确是 JDK-8u331 这个版本的一个 bug。
bug 详情可以看这里 “JDK-8285445 : cannot open file “NUL:””

总结一下, JDK-8u331 中尝试避免去访问 ADS (alternate data streams), 而在代码实现上同时造成了 JDK 对设备文件的访问受阻。

解决方案:

  1. 在系统属性中添加 , 值为 false, 停用这个新特性即可
  2. JDK-8u333 会对这种情况进行修复, 也就是更换 JDK 版本

到此第一个问题解决了。

4 第二个问题

第二个问题, 为什么第二次及后面报的异常会和第一次的不同。

在上面的流程中, 2 次请求出现不同的异常:

第一次请求抛出的是 FileNotFoundException
第二次后的所有请求抛出的是 NoClassDefFoundError

NoClassDefFoundError 异常可以简单的理解为 Java 编译期能够找得到对应的 Class, 而到了运行时, 找不到合适的 Class。
抛出这个异常的线程会直接被停止, 即使 try-catch 住了异常, 线程也会被终止掉。

导致 NoClassDefFoundError 的原因有总的来说有 2 种

  1. 运行中找不到对应的类 (可能是 classpath 中没有这个 class, 存在多个类加载器等情况), 抛出的异常一般为 : 没有找到的类的路径
  2. 类加载中初始化失败, 即 class 的 <clinit>(),也就是类的静态属性和静态代码出现了异常, 抛出的异常一般为 NoClassDefFoundError: Could not initialize class 类的路径

前者是真的在运行中没有找到这个类, 后者是加载类失败。

结合上面出现异常的位置的代码,可以很快的知道第二次及后续出现的异常是因为第一次加载 Conduits 类执行其静态代码块失败了, 后续加载这个 Conduits 类, 直接不加载, 抛出 NoClasseDefFoundError 这个异常了

5 参考

: NUL: (系统找不到指定的文件。)
Java 运行时发生 NoClassDefFoundError: Could not initialize class 的解决方法
Java: FileOutputStream(“NUL:”) not working after Java upgrade
JDK-8285445 : cannot open file “NUL:”