java.io.IOError: java.io.FileNotFoundException: Invalid file path 的问题定位


1 环境

环境 版本
OS window 10
jdk JDK-8u331
SpringBoot 2.0.0.RELEASE

2 现象

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

org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.io.IOError: java.io.FileNotFoundException: Invalid file path
 at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:982)
 at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
 at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
 at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
 at javax.servlet.http.HttpServlet.service(HttpServlet.java:707)
 at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
 at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
 at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:85)
 at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129)
 at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55)
 at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
 at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
 at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
 at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:110)
 at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
 at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
 at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
 at com.lcn29.framework.web.filter.CustomTraceFilter.doFilter(CustomTraceFilter.java:116)
 at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
 at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
 at com.lcn29.framework.tracer.sofa.springmvc.SpringMvcSofaTracerFilter.doFilter(SpringMvcSofaTracerFilter.java:153)
 at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
 at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
 at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
 at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
 at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
 at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
 at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:106)
 at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
 at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
 at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
 at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84)
 at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
 at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
 at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:131)
 at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
 at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
 at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
 at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
 at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)
 at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)
 at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
 at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
 at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
 at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:292)
 at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:81)
 at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:138)
 at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135)
 at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
 at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
 at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:272)
 at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81)
 at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:104)
 at io.undertow.server.Connectors.executeRootHandler(Connectors.java:211)
 at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:809)
 at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
 at java.lang.Thread.run(Unknown Source)

后续无论发送多少次请求, 则都是抛出另一个异常:
nested exception is java.lang.NoClassDefFoundError: Could not initialize class org.xnio.channels.Channels

org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.NoClassDefFoundError: Could not initialize class org.xnio.channels.Channels
 at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:982)
 at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
 at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
 at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
 at javax.servlet.http.HttpServlet.service(HttpServlet.java:707)
 at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
 at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
 at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:85)
 at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129)
 at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55)
 at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
 at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
 at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
 at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:110)
 at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
 at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
 at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
 at com.lcn29.framework.web.filter.CustomTraceFilter.doFilter(CustomTraceFilter.java:116)
 at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
 at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
 at com.lcn29.framework.tracer.sofa.springmvc.SpringMvcSofaTracerFilter.doFilter(SpringMvcSofaTracerFilter.java:153)
 at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
 at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
 at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
 at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
 at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
 at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
 at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:106)
 at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
 at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
 at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
 at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84)
 at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
 at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
 at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:131)
 at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
 at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
 at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
 at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
 at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)
 at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)
 at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
 at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
 at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
 at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:292)
 at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:81)
 at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:138)
 at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135)
 at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
 at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
 at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:272)
 at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81)
 at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:104)
 at io.undertow.server.Connectors.executeRootHandler(Connectors.java:211)
 at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:809)
 at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
 at java.lang.Thread.run(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("os.name", "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\null.sys。

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

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

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

最终在 StackOverflow 的 “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. 在系统属性中添加 jdk.io.File.enableADS, 值为 false, 停用这个新特性即可
  2. JDK-8u333 会对这种情况进行修复, 也就是更换 JDK 版本

到此第一个问题解决了。

4 第二个问题

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

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

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

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

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

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

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

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

5 参考

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


  目录