无法在码头 9.4.16.v20190411 中部署 WAR:收到 WEB-INF/lib 下 war 中的 jar 的 FileNotFoundException

Cannot deploy WAR in jetty 9.4.16.v20190411: receiving FileNotFoundException for jar that is in the war under WEB-INF/lib

我有一个嵌入式 Jetty 运行,最近不得不升级到版本 9.4.16.v20190411,但是 WAR 文件不再部署并显示以下错误消息:

2019-04-17 11:37:13.054:WARN:oejw.WebAppContext:main: Failed startup of context o.e.j.w.WebAppContext@657c8ad9{root,/,jar:file:///D:/SLX/Agent/webapps/root.war!/,UNAVAILABLE}{D:\SLX\Agent\webapps\root.war}
java.io.FileNotFoundException: JAR entry WEB-INF/lib/FastInfoset-1.2.15.jar!/ not found in D:\SLX\Agent\webapps\root.war
    at java.base/sun.net.www.protocol.jar.JarURLConnection.connect(JarURLConnection.java:147)
    at java.base/sun.net.www.protocol.jar.JarURLConnection.getJarFile(JarURLConnection.java:92)
    at org.eclipse.jetty.webapp.MetaInfConfiguration.getTlds(MetaInfConfiguration.java:438)
    at org.eclipse.jetty.webapp.MetaInfConfiguration.scanForTlds(MetaInfConfiguration.java:355)
    at org.eclipse.jetty.webapp.MetaInfConfiguration.scanJars(MetaInfConfiguration.java:173)
    at org.eclipse.jetty.webapp.MetaInfConfiguration.preConfigure(MetaInfConfiguration.java:107)
    at org.eclipse.jetty.webapp.WebAppContext.preConfigure(WebAppContext.java:506)
    at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:544)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:167)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:119)
    at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:113)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:167)
    at org.eclipse.jetty.server.Server.start(Server.java:418)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:110)
    at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:113)
    at org.eclipse.jetty.server.Server.doStart(Server.java:382)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at com.linxberg.timelogix.service.App.main(App.java:49)

我正在为嵌入式网络服务器使用以下启动代码:

public static void main( String[] args ) throws Exception
{
    File loc = findWebappsDir();

    if (loc == null)
        throw new FileNotFoundException("Could not find webapps directory.");


    DeploymentManager dm = new DeploymentManager();
    WebAppProvider wap = new WebAppProvider();

    //prefer THIS loader over child loaders, the opposite of 
    //what J2EE specs, but that doesn't allow our configuration overrides to work correctly
    wap.setParentLoaderPriority(true); 
    wap.setMonitoredDirectories(List.of(loc.getAbsolutePath()));
    wap.setScanInterval(30);
    dm.addAppProvider(wap);

    ContextHandlerCollection chc = new ContextHandlerCollection();

    server = new Server(8080);
    dm.setContexts(chc);

    server.addBean(dm);
    server.setHandler(chc);
    server.start();
}

请注意,该文件确实存在,尽管我怀疑它与码头扫描 tld 的方式有关(请注意,此应用程序根本不使用 JSP)。

在升级到 Jetty 9.x(来自 Jetty 6.x)时,您还升级了 Servlet 支持版本。

从 Servlet 2.5(在 Jetty 6 中)到 Servlet 3.1(在 Jetty 9 中)。

这意味着容器现在在启动 webapp 时有更多的事情要做。

现在影响您的是 javax.servlet.ServletContainerInitializer(在 Servlet 3.0 中)的引入。

Jetty 9.x 中存在许多 ServletContainerInitializer (SCI) 实现,每个实现都可以声明一个可选的 @HandlesTypes which will list what kinds of annotations and/or classes that SCI is interested in being notified about in the ServletContainerInitializer.onStartup(Set<Class<?>> c, ServletContext ctx)。这意味着在启动时,Jetty 必须扫描 webapp 容器 类(WEB-INF/classesWEB-INF/lib/*.jar)和服务器容器 jars(服务器类加载器)以找到所有匹配的 类声明 @HandlesTypes.

此扫描是 Servlet 3.0 的要求。

简而言之,如果WEB-INF/lib中也有jar文件,则无法阻止WAR文件的展开,因为Java不支持jar文件的嵌套解包。换句话说,您不能使用 JarFile 遍历另一个 JAR 文件中的 JAR 文件。 (堆栈跟踪中的 JarURLConnection 是由于调用 JarURLConnection.getJarFile() 允许遍历 JAR 文件内容,这是正确扫描 JAR 字节码所必需的)

替代选项:QuickStart ...

您可以选择使用 Jetty quickstart 并在构建时预先计算扫描并将其嵌入到您的 war 文件中,使用生成的快速入门而不是在运行时扫描内容。