类型 'org/eclipse/equinox/http/servlet/HttpServiceServlet'(当前帧,堆栈 [0])不可分配给 'javax/servlet/http/HttpServlet'

Type 'org/eclipse/equinox/http/servlet/HttpServiceServlet' (current frame, stack[0]) is not assignable to 'javax/servlet/http/HttpServlet'

我有一个非常奇怪的行为,有时它开始时没有错误,或者它会给我 java.lang.VerifyError:操作数堆栈上的类型错误

它是 AdoptOpenJDK 11.0.3 我也尝试了 11.0.9 没有改变,它是 Tomcat 9 中的 运行,我被迫将 JSP 2.2 与 Servlet 3.0 一起使用,因为它是非常旧的应用程序。

我有哪些调试选项?这是 JVM 错误?

类加载日志:

[157.897s][info   ][class,load] org.eclipse.equinox.http.servletbridge.internal.Activator source: file:/appservers/tomcat/webapps/app/work/eclipse/plugins/org.eclipse.equinox.http.servletbridge_1.1.100.v20180827-1235.jar
[157.899s][info   ][class,load] javax.servlet.Servlet source: file:/appservers/tomcat/webapps/app/work/eclipse/plugins/javax.servlet-3.0.0.v201112011016.jar
[157.899s][info   ][class,load] javax.servlet.ServletConfig source: file:/appservers/tomcat/webapps/app/work/eclipse/plugins/javax.servlet-3.0.0.v201112011016.jar
[157.899s][info   ][class,load] javax.servlet.GenericServlet source: file:/appservers/tomcat/webapps/app/work/eclipse/plugins/javax.servlet-3.0.0.v201112011016.jar
[157.899s][info   ][class,load] javax.servlet.http.HttpServlet source: file:/appservers/tomcat/webapps/app/work/eclipse/plugins/javax.servlet-3.0.0.v201112011016.jar
[157.899s][info   ][class,load] org.eclipse.equinox.http.servlet.internal.servlet.ProxyServlet source: file:/appservers/tomcat/webapps/app/work/eclipse/plugins/org.eclipse.equinox.http.servlet_1.2.0.v20150519-1816.jar
[157.899s][info   ][class,load] org.eclipse.equinox.http.servlet.HttpServiceServlet source: file:/appservers/tomcat/webapps/app/work/eclipse/plugins/org.eclipse.equinox.http.servlet_1.2.0.v20150519-1816.jar
[157.900s][info   ][class,load] org.eclipse.equinox.http.servlet.internal.Activator source: file:/appservers/tomcat/webapps/app/work/eclipse/plugins/org.eclipse.equinox.http.servlet_1.2.0.v20150519-1816.jar
[157.901s][info   ][class,load] org.osgi.service.http.HttpService source: file:/appservers/tomcat/webapps/app/work/eclipse/plugins/org.eclipse.osgi.services_3.7.100.v20180827-1536.jar
[157.901s][info   ][class,load] org.eclipse.equinox.http.servlet.ExtendedHttpService source: file:/appservers/tomcat/webapps/app/work/eclipse/plugins/org.eclipse.equinox.http.servlet_1.2.0.v20150519-1816.jar
[157.901s][info   ][class,load] java.lang.VerifyError source: jrt:/java.base

分隔文件中记录的错误:

!ENTRY org.eclipse.equinox.http.servletbridge 4 0 2020-12-18 08:29:43.240
!MESSAGE FrameworkEvent ERROR
!STACK 0
org.osgi.framework.BundleException: Error starting module.
    at org.eclipse.osgi.container.Module.doStart(Module.java:593)
    at org.eclipse.osgi.container.Module.start(Module.java:452)
    at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.incStartLevel(ModuleContainer.java:1685)
    at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.incStartLevel(ModuleContainer.java:1665)
    at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.doContainerStartLevel(ModuleContainer.java:1627)
    at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.dispatchEvent(ModuleContainer.java:1558)
    at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.dispatchEvent(ModuleContainer.java:1)
    at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:233)
    at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.run(EventManager.java:343)
Caused by: java.lang.VerifyError: Bad type on operand stack
Exception Details:
  Location:
    org/eclipse/equinox/http/servletbridge/internal/Activator.start(Lorg/osgi/framework/BundleContext;)V @15: invokestatic
  Reason:
    Type 'org/eclipse/equinox/http/servlet/HttpServiceServlet' (current frame, stack[0]) is not assignable to 'javax/servlet/http/HttpServlet'
  Current Frame:
    bci: @15
    flags: { }
    locals: { 'org/eclipse/equinox/http/servletbridge/internal/Activator', 'org/osgi/framework/BundleContext' }
    stack: { 'org/eclipse/equinox/http/servlet/HttpServiceServlet' }
  Bytecode:
    0000000: 2abb 0012 59b7 0021 b500 1f2a b400 1fb8
    0000010: 0022 b1                                

    at java.base/java.lang.Class.getDeclaredConstructors0(Native Method)
    at java.base/java.lang.Class.privateGetDeclaredConstructors(Class.java:3137)
    at java.base/java.lang.Class.getConstructor0(Class.java:3342)
    at java.base/java.lang.Class.newInstance(Class.java:556)
    at org.eclipse.osgi.internal.framework.BundleContextImpl.loadBundleActivator(BundleContextImpl.java:766)
    at org.eclipse.osgi.internal.framework.BundleContextImpl.start(BundleContextImpl.java:719)
    at org.eclipse.osgi.internal.framework.EquinoxBundle.startWorker0(EquinoxBundle.java:1005)
    at org.eclipse.osgi.internal.framework.EquinoxBundle$EquinoxModule.startWorker(EquinoxBundle.java:357)
    at org.eclipse.osgi.container.Module.doStart(Module.java:584)
    ... 8 more
Root exception:
java.lang.VerifyError: Bad type on operand stack
Exception Details:
  Location:
    org/eclipse/equinox/http/servletbridge/internal/Activator.start(Lorg/osgi/framework/BundleContext;)V @15: invokestatic
  Reason:
    Type 'org/eclipse/equinox/http/servlet/HttpServiceServlet' (current frame, stack[0]) is not assignable to 'javax/servlet/http/HttpServlet'
  Current Frame:
    bci: @15
    flags: { }
    locals: { 'org/eclipse/equinox/http/servletbridge/internal/Activator', 'org/osgi/framework/BundleContext' }
    stack: { 'org/eclipse/equinox/http/servlet/HttpServiceServlet' }
  Bytecode:
    0000000: 2abb 0012 59b7 0021 b500 1f2a b400 1fb8
    0000010: 0022 b1                                

    at java.base/java.lang.Class.getDeclaredConstructors0(Native Method)
    at java.base/java.lang.Class.privateGetDeclaredConstructors(Class.java:3137)
    at java.base/java.lang.Class.getConstructor0(Class.java:3342)
    at java.base/java.lang.Class.newInstance(Class.java:556)
    at org.eclipse.osgi.internal.framework.BundleContextImpl.loadBundleActivator(BundleContextImpl.java:766)
    at org.eclipse.osgi.internal.framework.BundleContextImpl.start(BundleContextImpl.java:719)
    at org.eclipse.osgi.internal.framework.EquinoxBundle.startWorker0(EquinoxBundle.java:1005)
    at org.eclipse.osgi.internal.framework.EquinoxBundle$EquinoxModule.startWorker(EquinoxBundle.java:357)
    at org.eclipse.osgi.container.Module.doStart(Module.java:584)
    at org.eclipse.osgi.container.Module.start(Module.java:452)
    at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.incStartLevel(ModuleContainer.java:1685)
    at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.incStartLevel(ModuleContainer.java:1665)
    at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.doContainerStartLevel(ModuleContainer.java:1627)
    at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.dispatchEvent(ModuleContainer.java:1558)
    at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.dispatchEvent(ModuleContainer.java:1)
    at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:233)
    at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.run(EventManager.java:343)

非常有趣。这里发生的是您的 class 正在加载(BundleActivator),然后它正在使用 JVM class 加载程序进行验证,但验证失败。它抱怨堆栈的顶部是 HttpServiceServlet 并且不能分配给 javax.servlet.http.HttpServlet.

您可能正在使用更新版本的 JakartaEE 规范,它从 javax.命名空间到 jakartaee。命名空间;那会给出看到的错误。您正在使用的二进制文件版本可能是针对规范的旧版本编译的,并且正在尝试 运行 它在规范的较新版本中。如果你只使用与你需要 运行 反对的版本相对应的旧版本,你应该没问题。

该应用程序很老,有很多技术债务,但我们被迫每年更新它....(最好的方法是“不要碰 运行 系统”)

所以我调试了整个 OSGI 包 classloader 并发现它以两种不同的方式解决了 javax.servlet 的问题,一种是新版本,另一种是包 javax.servlet-3.0。为什么 osgi 随机重启,我不知道,我没有调试它,它纯粹是魔法。我认为它是一个 osgi 错误,因为它应该抛出一个错误,显示有两个依赖链,修改 meta-inf 导致有时抛出依赖链错误。

关于问题:
当“Caused by: java.lang.VerifyError: Bad type on operand stack”发生时,它会尝试将 class 的 old/new 版本分配给具有相同 class 的另一个版本显然是不同的字节码。

org.osgi.service.http -> Tomcat 的 javax.servlet(服务器-api.jar) javax.servlet -> javax.servlet 捆绑包

示例:
ClassA(V1.0)
ClassA(V1.1)

Class B 使用 Class A(V1.1)
Class C 使用 Class A(V1.0)
Class C 获取 Class B 的引用并使用 Class A(V1.0)
调用方法 Class A(V1.0) != Class A(V1.1) 将抛出 java.lang.VerifyError:操作数堆栈类型错误

我是如何解决的:

我们有自己的包,可以导入、导出 javax.servlet,这会导致此 javax.servlet 切换有时会因 java.lang.VerifyError 而失败。我删除了 javax.servlet 导入,因为 org.osgi.service.http 确实导入了 javax.servlet,就是这样,现在它似乎可以工作了。

2021 年 7 月 1 日更新: 所以经过很长一段时间,我在调试整个 osgi 插件/解析器/加载机制等后解决了错误。我发现 Bundle/Plugin 是在运行时动态创建的,也就是“org.eclipse.equinox.servletbridge.extensionbundle_1 .3.0.jar”。 从 “org.eclipse.equinox.servletbridge”插件。

来自 OSGI 文档:

If an OSGi servlet is to be loaded, the ServletBridge which passes on the queries to the respective OSGi Servlet is called by Tomcat. For the OSGi servlets, which are actually only conventional HttpServlets, also to know or use the servlet API, there exists a special Equinox bundle "org.eclipse.equinox.servlet.bridge.extensionbundle", which defines or releases the servlet API or the Tomcat packages in its manifest file. This bundle is generated by the ServletBridge at runtime at every deploy of the plug-ins, i.e. each time Tomcat is started. In order to add extensions, that is to release additional Tomcat packages or to use another servlet API version, this bundle must be patched. Here, in the Incubator bundle "org.eclipse.equinox.servletbridge" there exists the possibility of generating a separate extension bundle with the help of an Ant script.

所以我创建了一个虚拟文件“org.eclipse.equinox.servletbridge.extensionbundle_block_deployment.dummy”并复制到插件文件夹中。 这样它就阻止了另一个“javax.servlet.http”的创建和注册 代码: org.eclipse.equinox.servletbridge.FrameworkLauncher.deployExtensionBundle()

现在错误消失了,因为它仅从 javax.servlet_3.1.0.v201410161800.jar[=11 检索“javax.servlet.http”classes =]

我无法删除 javax.servlet_3.1.0.v201410161800.jar,因为其他插件需要它并受插件名称绑定,而不是清单文件中的包导入。