IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> 启动 tomcat,发现终端有非常多的 WARNING: Failed to scan xxx java.io.FileNotFoundException: xxx -> 正文阅读

[游戏开发]启动 tomcat,发现终端有非常多的 WARNING: Failed to scan xxx java.io.FileNotFoundException: xxx

Details In Errors

Apr 26, 2022 9:32:29 PM org.apache.tomcat.util.scan.StandardJarScanner processURLs
WARNING: Failed to scan [file:/Users/xxx/.m2/repository/org/apache/derby/derby/10.12.1.1/derbyLocale_zh_TW.jar] from classloader hierarchy
java.io.FileNotFoundException: /Users/xxx/.m2/repository/org/apache/derby/derby/10.12.1.1/derbyLocale_zh_TW.jar (No such file or directory)
	at java.util.zip.ZipFile.open(Native Method)
	at java.util.zip.ZipFile.<init>(ZipFile.java:225)
	at java.util.zip.ZipFile.<init>(ZipFile.java:155)
	at java.util.jar.JarFile.<init>(JarFile.java:167)
	at java.util.jar.JarFile.<init>(JarFile.java:131)
	at org.apache.tomcat.util.compat.JreCompat.jarFileNewInstance(JreCompat.java:209)
	at org.apache.tomcat.util.scan.JarFileUrlJar.<init>(JarFileUrlJar.java:65)
	at org.apache.tomcat.util.scan.JarFactory.newInstance(JarFactory.java:49)
	at org.apache.tomcat.util.scan.StandardJarScanner.process(StandardJarScanner.java:387)
	at org.apache.tomcat.util.scan.StandardJarScanner.processURLs(StandardJarScanner.java:322)
	at org.apache.tomcat.util.scan.StandardJarScanner.doScanClassPath(StandardJarScanner.java:272)
	at org.apache.tomcat.util.scan.StandardJarScanner.scan(StandardJarScanner.java:235)
	at org.apache.catalina.startup.ContextConfig.processJarsForWebFragments(ContextConfig.java:1863)
	at org.apache.catalina.startup.ContextConfig.webConfig(ContextConfig.java:1079)
	at org.apache.catalina.startup.ContextConfig.configureStart(ContextConfig.java:779)
	at org.apache.catalina.startup.ContextConfig.lifecycleEvent(ContextConfig.java:299)
	at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:123)
	at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5130)
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
	at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1427)
	at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1417)
	at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
	at java.util.concurrent.FutureTask.run(FutureTask.java)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)

How to resolve

  1. google 了非常多,但是都没能找到个精确的答案和回答
    1. 多数回答需要用户禁用 scanManifest,但是为什么?以及这些文件包哪里来的不知道
    2. 查询了这些包的依赖,通过 mvn dependency 查看,并未找到,说明并不是实际引入依赖进来的。
    3. 尝试设置了,但是结果无效,实际设置有问题,后面会有回答
  2. 最终通过了 debug tomcat 源码发现了点点端倪

具体报错代码行: 相关具体的代码行已经补充背景色

	at org.apache.tomcat.util.scan.StandardJarScanner.process(StandardJarScanner.java:387)
	at org.apache.tomcat.util.scan.StandardJarScanner.processURLs(StandardJarScanner.java:322)
	at org.apache.tomcat.util.scan.StandardJarScanner.doScanClassPath(StandardJarScanner.java:272)
	at org.apache.tomcat.util.scan.StandardJarScanner.scan(StandardJarScanner.java:235)
	at org.apache.catalina.startup.ContextConfig.processJarsForWebFragments(ContextConfig.java:1863)

具体的代码 debug 过程?

1. 在 debug 过程中,发现实际 tomcat 去扫描的入口为:org.apache.tomcat.util.scan.StandardJarScanner#doScanClassPath

1. 第一个addAll方法将找到的URL路径添加到classPathUrlsToProcess 这个链表当中。
2. 然后processURLs对队列当中的URL再进一步的处理。
3. 并且第一次循环的时候 Arrays.asList(((URLClassLoader) classLoader).getURLs()) 结果为空,第二次循环便有了 376 个值。 注意此处的不同项目依赖不同,总体的量也即 376 这个数字会不一样。?

protected void doScanClassPath(JarScanType scanType, ServletContext context,
            JarScannerCallback callback, Set<URL> processedURLs) {
        ...

        Deque<URL> classPathUrlsToProcess = new LinkedList<>();

        while (classLoader != null && classLoader != stopLoader) {
            if (classLoader instanceof URLClassLoader) {
                if (isWebapp) {
                    isWebapp = isWebappClassLoader(classLoader);
                }

                classPathUrlsToProcess.addAll(
                        Arrays.asList(((URLClassLoader) classLoader).getURLs()));

                processURLs(scanType, callback, processedURLs, isWebapp, classPathUrlsToProcess);
            }
            classLoader = classLoader.getParent();
        }

        ...
    }

相关截图如下:

2. 查看进一步处理方法:org.apache.tomcat.util.scan.StandardJarScanner#processURLs

1. 注意到 所有的文件依赖路径也即 URL 都是从 classPathUrlsToProcess当中捞出
2. 从此处即可看出, tomcat 是要将对应的 刚才类加载扫出来的jar 包都要进行处理。

3. 所以此处还并不是主要处理的地方,继续往下看。?

protected void processURLs(JarScanType scanType, JarScannerCallback callback,
            Set<URL> processedURLs, boolean isWebapp, Deque<URL> classPathUrlsToProcess) {

	...
  while (!classPathUrlsToProcess.isEmpty()) {
      URL url = classPathUrlsToProcess.pop();

      if (processedURLs.contains(url)) {
          // Skip this URL it has already been processed
          continue;
      }

      ClassPathEntry cpe = new ClassPathEntry(url);

      // JARs are scanned unless the filter says not to.
      // Directories are scanned for pluggability scans or
      // if scanAllDirectories is enabled unless the
      // filter says not to.
      if ((cpe.isJar() ||
              scanType == JarScanType.PLUGGABILITY ||
              isScanAllDirectories()) &&
                      getJarScanFilter().check(scanType,
                              cpe.getName())) {
          if (log.isDebugEnabled()) {
              log.debug(sm.getString("jarScan.classloaderJarScan", url));
          }
          try {
              processedURLs.add(url);

              process(scanType, callback, url, null, isWebapp, classPathUrlsToProcess);
          } catch (IOException ioe) {
              log.warn(sm.getString("jarScan.classloaderFail", url), ioe);
          }
      } else {
          // JAR / directory has been skipped
          if (log.isTraceEnabled()) {
              log.trace(sm.getString("jarScan.classloaderJarNoScan", url));
          }
      }
  }
}

?相关截图如下:

3. 查看进一步处理方法:org.apache.tomcat.util.scan.StandardJarScanner#process

1. 可以发现,每一次 jar 包都要被处理,也即一个 jar 包就是一个 URL 资源
2. 注意到,这个 process 是嵌套在一个上一步方法的循环当中,所以可以知道的是, try (Jar jar = JarFactory.newInstance(url)) 并不是抛出错误的地方,不然的话,怎么会有那么多的文件没找到呢!
3. 而isScanManifest()条件判断则是 Boolean 判断,不涉及相关具体处理逻辑,所以对应的额具体处理方法就在 processManifest(jar, isWebapp, classPathUrlsToProcess) 中?

protected void process(JarScanType scanType, JarScannerCallback callback,
            URL url, String webappPath, boolean isWebapp, Deque<URL> classPathUrlsToProcess)
            throws IOException {

        if (log.isTraceEnabled()) {
            log.trace(sm.getString("jarScan.jarUrlStart", url));
        }

        if ("jar".equals(url.getProtocol()) || url.getPath().endsWith(Constants.JAR_EXT)) {
            try (Jar jar = JarFactory.newInstance(url)) {
                if (isScanManifest()) {
                    processManifest(jar, isWebapp, classPathUrlsToProcess);
                }
                callback.scan(jar, webappPath, isWebapp);
            }
        } else if ("file".equals(url.getProtocol())) {
            File f;
            try {
                f = new File(url.toURI());
                if (f.isFile() && isScanAllFiles()) {
                    // Treat this file as a JAR
                    URL jarURL = UriUtil.buildJarUrl(f);
                    try (Jar jar = JarFactory.newInstance(jarURL)) {
                        if (isScanManifest()) {
                            processManifest(jar, isWebapp, classPathUrlsToProcess);
                        }
                        callback.scan(jar, webappPath, isWebapp);
                    }
                } else if (f.isDirectory()) {
                    if (scanType == JarScanType.PLUGGABILITY) {
                        callback.scan(f, webappPath, isWebapp);
                    } else {
                        File metainf = new File(f.getAbsoluteFile() + File.separator + "META-INF");
                        if (metainf.isDirectory()) {
                            callback.scan(f, webappPath, isWebapp);
                        }
                    }
                }
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                // Wrap the exception and re-throw
                throw new IOException(t);
            }
        }
    }

?相关截图:

4. 查看进一步处理方法:org.apache.tomcat.util.scan.StandardJarScanner#processManifest

该方法里处理了几个重要的操作
1. 获取 jar 包的 "META-INF/MANIFEST.MF" 文件

2. 获取 MANIFEST.MF 文件中的 Class-Path 属性
3. 获取当前 JAR 包路径
4. 将当前的 jar 包路径拼接好 刚才的第二步获取到的 Class-path 属性的 jar 包路径,并把最终的额结果再次塞回到了 classPathUrlsToProgress 链表中 这也是一直我们在外面查找那些凭空多出来的 derbyLocale 文件找不到相关依赖的原因在这。?

相关截图:

查看对应的 多余的 derby 的 MANIFEST.MF 文件,可以发现如下内容: Class-Path: derbyLocale_cs.jar derbyLocale_de_DE.jar derbyLocale_es.ja r derbyLocale_fr.jar derbyLocale_hu.jar derbyLocale_it.jar derbyLocal e_ja_JP.jar derbyLocale_ko_KR.jar derbyLocale_pl.jar derbyLocale_pt_B R.jar derbyLocale_ru.jar derbyLocale_zh_CN.jar derbyLocale_zh_TW.jar?

相关截图:

综上所述

可以得到,当前的多出这些多余的 jar 包路径,就是因为 scanManifest 属性 为 true,所以才进行了 扫描多余 jar 包步骤,并且拼接后的 路径在本地是没有的,所以就报错了。?

解决办法?

?1. 解压缩对应的 jar包,将对应的 class-path 属性删除掉(注意,这个前提是这个 jar 包依赖不影响打项目原有依赖),然后再打包回去

有比较大的问题在于:这个 jar 包是特殊的,除非编译源码重新修订,否则在当前的 所有公共仓库都有对应的 class-path,显然不能这样做

2.?如果找不到包,那就去下载这个对应的包去放在这个路径下

问题在于这个对应不需要的包越来越多情况下,会导致发包的文件大小更大,还有可能引入其他的 jar 包冲突,虽然本次debug 中没有?

3.?通过对比查找相关资料,发现这个WARNING 是 tomcat 高版本导致的,也即恰巧是 8.5.1 以上的版本,所以降低 tomcat 版本也可以解决

4. 最正确的作法:;将对应的 org.apache.tomcat.util.scan.StandardJarScanner#scanManifest 的属性默认设置为 false 即可!?

如何设置,请根据个人的所对应的项目进行适配,将scanManifest 属性变更为 false 即可了!

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-05-08 08:26:50  更:2022-05-08 08:27:18 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 11:36:03-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码