美文网首页
Tomcat WARNING: Failed to scan,

Tomcat WARNING: Failed to scan,

作者: 程序员札记 | 来源:发表于2023-09-27 13:23 被阅读0次

接上文 ,这是一个完全tomcat 启动的warning


2023-09-27 20:44:45 startup_process.py[line:172] INFO 27-Sep-2023 20:44:45.182 WARNING [main] org.apache.tomcat.util.scan.StandardJarScanner.processURLs Failed to scan [file:/ebay/app/Tomcat/lib/._tomcat-util-scan.jar] from classloader hierarchy
2023-09-27 20:44:45 startup_process.py[line:172] INFO   java.util.zip.ZipException: error in opening zip file
2023-09-27 20:44:45 startup_process.py[line:172] INFO       at java.util.zip.ZipFile.open(Native Method)
2023-09-27 20:44:45 startup_process.py[line:172] INFO       at java.util.zip.ZipFile.<init>(ZipFile.java:233)
2023-09-27 20:44:45 startup_process.py[line:172] INFO       at java.util.zip.ZipFile.<init>(ZipFile.java:162)
2023-09-27 20:44:45 startup_process.py[line:172] INFO       at java.util.jar.JarFile.<init>(JarFile.java:173)
2023-09-27 20:44:45 startup_process.py[line:172] INFO       at java.util.jar.JarFile.<init>(JarFile.java:137)
2023-09-27 20:44:45 startup_process.py[line:172] INFO       at org.apache.tomcat.util.compat.JreCompat.jarFileNewInstance(JreCompat.java:256)
2023-09-27 20:44:45 startup_process.py[line:172] INFO       at org.apache.tomcat.util.scan.JarFileUrlJar.<init>(JarFileUrlJar.java:65)

这是 embeded tomcat 的warning

java.io.FileNotFoundException: D:\.m2\repository\org\bytedeco\javacpp-presets\hdf5-platform\1.10.3-1.4.3\hdf5.jar (系统找不到指定的文件。)
    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:166)
    at java.util.jar.JarFile.<init>(JarFile.java:130)
    at org.apache.tomcat.util.compat.JreCompat.jarFileNewInstance(JreCompat.java:188)
    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:374)
    at org.apache.tomcat.util.scan.StandardJarScanner.processURLs(StandardJarScanner.java:309)
    at org.apache.tomcat.util.scan.StandardJarScanner.doScanClassPath(StandardJarScanner.java:266)
    at org.apache.tomcat.util.scan.StandardJarScanner.scan(StandardJarScanner.java:229)

原因分析

其实问题就是出Manifest文件中的classpath,通过分析代码我们知道tomcat除了加载了我们maven管理的jar包之外,还会对jar中的manifest文件进行分析,如果其中存在classpath,他会将其中的内容也添加jar包依赖中,并对这些jar包进行加载。

解决方案

方案一:
删除Manifest中的classpath或者删除Manifest文件,这样就避免了加载不存在的jar包。但是每次maven更新的时候可能会覆盖掉你的修改,导致异常再次出现。

方案二:
按照加载提示的路径,将对应jar包复制过去并改名去掉版本号,但这样会造成jar冗余,同样的jar会加载两个。

方案三:
通过对比查找相关资料,发现这个WARNING 是 tomcat 高版本导致的,也即恰巧是 8.5.1 以上的版本,所以降低 tomcat 版本也可以解决。降级tomcat版本,使用8.5.0 或以下版本。8.5.0版本中不会对manifest进行分析加载,这样也就不会出现我们的异常了。

方案四
增加一下代码设置不扫描Manifest文件。将对应的 org.apache.tomcat.util.scan.StandardJarScanner#scanManifest 的属性默认设置为 false 即可!

细节请看上文。

具体的代码 debug 过程

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

  2. 第一个addAll方法将找到的URL路径添加到classPathUrlsToProcess 这个链表当中。

  3. 然后processURLs对队列当中的URL再进一步的处理。

  4. 并且第一次循环的时候 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();
        }
 
        ...
    }

相关截图如下:

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

  2. 注意到 所有的文件依赖路径也即 URL 都是从 classPathUrlsToProcess当中捞出

  3. 从此处即可看出, tomcat 是要将对应的 刚才类加载扫出来的jar 包都要进行处理。

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

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));
          }
      }
  }
}

相关截图如下:

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

  2. 可以发现,每一次 jar 包都要被处理,也即一个 jar 包就是一个 URL 资源

  3. 注意到,这个 process 是嵌套在一个上一步方法的循环当中,所以可以知道的是, try (Jar jar = JarFactory.newInstance(url)) 并不是抛出错误的地方,不然的话,怎么会有那么多的文件没找到呢!

  4. 而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);
            }
        }
    }

相关截图:

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

该方法里处理了几个重要的操作

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

相关截图:

image.png image.png

查看对应的 多余的 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

相关截图:


image.png

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

image.png

相关文章

网友评论

      本文标题:Tomcat WARNING: Failed to scan,

      本文链接:https://www.haomeiwen.com/subject/ecekbdtx.html