WAS 6.1 java.lang.VerifyError:违反了类加载约束
环境是Linux上的WAS 6.1,部署一个webapp,使用 来自 xercesImpl.jar 的类。
由于公司政策限制,该应用程序必须部署 设置:
Class Loader Order
Classes loaded with parent class loader first
-> Classes loaded with application class loader first
WAR class loader policy
Class loader for each WAR file in application
-> Single class loader for application
WAR 文件包含 xercesImpl.jar 的副本,与 编译应用程序时位于类路径中。
当启动 web 应用程序时,当 Spring 尝试解析其配置时,它 抛出:
java.lang.VerifyError: class loading constraint violated
(class: org/apache/xerces/jaxp/DocumentBuilderImpl
method: parse(Lorg/xml/sax/InputSource;)Lorg/w3c/dom/Document;)
到目前为止的分析
看来 WAS 提供了一个实现 org.apache.xerces.jaxp.DocumentBuilderImpl,因为我们可以删除 来自 WAR 文件的 xercesImpl.jar 仍然得到相同的错误(不是 类未找到异常)。因此 WAS 似乎正在解决引用 使用与我们的参考文献不兼容的自己的副本 编译好的类文件。但是,“xercesImpl.jar”的唯一其他实例 我可以找到(除了与我们的应用程序一起部署的副本之外)位于目录中 deploytool
,它似乎位于应用程序服务器之外。
我扫描了WAS中的所有jar(总共1300个),
for i in `find . -name \*.jar`; do jar tvf $i|grep -qi xerces && echo $i ; done
发现./java/jre/lib/xml.jar
包含org.apache.xerces.*中的所有类, 所以这很可能是类加载器解析引用的地方。
这是奇怪的部分:
如果我们更改为“父类加载器优先”,我们不会看到异常。 这与预期的行为背道而驰。我们期望与 “首先应用程序类加载器”它将使用我们的 xercesImpl.jar 提供,并且仅当我们设置“父类加载器”时才使用WAS的版本 第一”。这似乎与我们实际看到的相反。
问题:
类加载器委托设置如何与上述信息交互以导致观察到的行为?
The environment is WAS 6.1 on Linux, deploying a webapp that uses
classes from xercesImpl.jar.
Due to company policy restrictions, the app must be deployed with
settings:
Class Loader Order
Classes loaded with parent class loader first
-> Classes loaded with application class loader first
WAR class loader policy
Class loader for each WAR file in application
-> Single class loader for application
The WAR file contains a copy of xercesImpl.jar, the same one that
was in the classpath when the app was compiled.
When launching the webapp, when Spring tries to parse its configs, it
throws:
java.lang.VerifyError: class loading constraint violated
(class: org/apache/xerces/jaxp/DocumentBuilderImpl
method: parse(Lorg/xml/sax/InputSource;)Lorg/w3c/dom/Document;)
ANALYSIS SO FAR
It appears that WAS provides an implementation of
org.apache.xerces.jaxp.DocumentBuilderImpl, because we can remove
xercesImpl.jar from the WAR file and still get the same error (not
ClassNotFoundException). Thus WAS seems to be resolving the references
using its own copy that is incompatible with the references in our
compiled class files. However, the only other instance of 'xercesImpl.jar'
I can find (other than the copy deployed with our app) is in directorydeploytool
, which seems to be outside the app server.
I scanned all the jars in WAS (all 1300 of them) with
for i in `find . -name \*.jar`; do jar tvf $i|grep -qi xerces && echo $i ; done
and found that ./java/jre/lib/xml.jar
contains all the classes in org.apache.xerces.*
,
so this is likely where the classloader is resolving the reference.
HERE'S THE WEIRD PART:
If we change to "parent class loader first" we do not see the exception.
This goes counter to the expected behavior. We would expect that with
"application classloader first" it would use the xercesImpl.jar that we
provided, and use WAS's version only if we set "parent classloader
first". This appears to be backwards from what we actually see.
THE QUESTION:
How is the classloader delegation setting interacting with the above information to result in the observed behavior?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
您的 WAR 还包括 org.xml.sax 或 org.w3c.dom 类,然后您引用的应用程序外部的类也引用了这些类。这设置了一个场景,您的应用程序类加载器可以看到同一类的两个实例,这是一个链接错误。
例如,如果您的应用程序使用 javax.xml.bind.Unmarshaller.unmarshal(InputSource),则将从 JDK 加载 Unmarshaller,并且 Unmarshaller 类仅对 JDK InputSource 具有可见性。当您的应用程序创建其 InputSource 时,它将从 WAR 加载该类(因为“应用程序优先”策略),然后您的应用程序将尝试将 WAR InputSource 的实例传递给 JDK Unmarshaller,后者只能接受JDK 输入源。
有两种解决方案:
根据我的经验,链接错误很难追踪,因为 JVM 提供的有关导致添加链接的原因的信息很糟糕。我通常启用类加载器跟踪,重现问题,然后向后走,直到找到从应用程序外部加载的类,“听起来”它可能引用了已知存在于应用程序内部的类。
Your WAR is also including either org.xml.sax or org.w3c.dom classes, and then you're referencing a class that is outside your application that also references these classes. This sets up a scenario where your application class loader has visibility to two instances of the same class, which is a linkage error.
For example, if your application uses javax.xml.bind.Unmarshaller.unmarshal(InputSource), then Unmarshaller would be loaded from the JDK, and the Unmarshaller class only has visibility to the JDK InputSource. When your application creates its InputSource, it will load the class from the WAR (because "app first" policy), and then your application would attempt to pass an instance of the WAR InputSource to the JDK Unmarshaller, which can only accept an instance of the JDK InputSource.
There are two solutions:
In my experience, linkage errors are quite difficult to track down because JVMs give lousy information about what causes linkages to be added. I usually enable class loader trace, reproduce the problem, and then walk backwards until I find a class loaded from outside the application that "sounds like" it might reference a class that is known to exist inside the application.
我们的问题是在 WAS 8.5 上部署。
在我们的 Web 应用程序中,我们有一个由 cxf 生成的 Web 服务客户端。没有问题。
当我们添加 tika-parser 进行 mime 类型检测时,我们遇到了这个问题。
我们排除了三个依赖项:
一旦排除它们,我们的应用程序就会成功启动。
Our issue was deploying on WAS 8.5.
In our web application we have a web service client generated by cxf. No issues.
When we added in tika-parser for mime type detection, then we got this issue.
We excluded three dependencies:
Once they were excluded, our application started successfully.
可能为时已晚,但是我们解决了这个问题,删除了 server.xml 中的这一行:
jaxb-2.1
May be it's too late but, we solved this problem, removing this line in server.xml:
jaxb-2.1
实际情况如下:
应用程序的 pom.xml 文件和 WAS 上的 server.xml 文件中都定义了相同的库。因此,在 WAS 从应用程序的 pom.xml 文件创建的 war 文件加载类后,它会尝试从 server.xml 文件中指定的 WAS 上的共享库中的库加载相同的类。
如果您的应用程序被命名为 corrservo65-01,则将为应用程序在由如下路径定义的文件夹中定义 server.xml 文件:
/usr/WebSphere905/AppServer/profiles/node01/config/cells/dwauslwasapp06Cell01/nodes/dwauslwasapp06Node01/servers /corrservo65-01
您可以通过在 server.xml 文件中搜索 classloaders 元素来查看还从共享库加载哪些库。 classloaders 元素内部有libraries 元素。每个都定义一个库,ALSO从 WAS 上的共享库以及应用程序的 pom.xml 文件中定义的库加载。
例如,wsdl4j-1.6.2 在 pom.xml 文件中定义为依赖项:
下面是 WAS 从共享库加载的同一个库:
当您在 WAS 中将应用程序设置为parent_last 时,您还必须删除也可以从中引用共享库中找到的库。然后,您需要将这些库包含在应用程序的 pom.xml 文件中。这将导致仅基于 pom.xml 文件加载库。
然而,还有另一个可能的来源,那就是 WAS JDK。如果该库位于 WAS JDK 中,那么它将始终从那里加载。在这些情况下,您必须从 pom.xml 文件中删除依赖项或将范围设置为“provided”。将范围设置为“provided”将在本地编译应用程序时使用依赖项,但在部署到 WAS 时则不会。
最后,还有一个用于 WAS 的类加载器和一个用于 JDK 的类加载器。如果两者都包含相同的类,例如 javax.server.ServletContext,那么它们仍然会出现异常 - 即使该类在ear文件中不存在。
在我们的特定情况下,我们必须像这样设置WAS:类加载器使用parent_last,托管模块使用parent_first。
Here is what is going on:
The same libraries are defined in both the pom.xml file for the applications AND the server.xml file on WAS. So, after WAS loads the class from the war file created from the pom.xml file for the application, it then attempts to load the same class from the library in shared libraries on WAS that is specified in the server.xml file.
The server.xml file will defined for the application in a folder defined by a path like this if your application was named corrservo65-01:
/usr/WebSphere905/AppServer/profiles/node01/config/cells/dwauslwasapp06Cell01/nodes/dwauslwasapp06Node01/servers/corrservo65-01
You can see which libraries are also being loaded from shared libraries by searching for the classloaders element in the server.xml file. Inside of the classloaders element there are libraries elements. Each one defines a library that is ALSO being loaded from the shared libraries on WAS as well as the library defined in the pom.xml file of the application.
For example, wsdl4j-1.6.2 is defined as a dependency in the pom.xml file:
Here is the same library being loaded from the shared libraries by WAS:
When you set an application to parent_last in WAS then you have to also remove the references to libraries found in shared libraries from it as well. You then need to include those libraries in the pom.xml file of the application. This will cause ONLY the libraries to be loaded based on the pom.xml file.
However, there is another possible source and that is the WAS JDK. If the library is in the WAS JDK, then it will always load from there. In those cases, you must either remove the dependency from the pom.xml file or set the scope to "provided". Setting the scope to "provided" will use the dependency when the application is compiled locally, but not when it is deployed to WAS.
Lastly, there is a class loader for WAS and one for the JDK. If both contain the same class, say javax.server.ServletContext, then they will still the exception - even if the class does not exist in the ear file.
In our particular case, we had to set up WAS like this: Class loaders to use parent_last and Managed Modules to use parent_first.
禁用字节码验证
java.lang.VerifyError - 运行时未经检查的异常
一旦类文件被加载到Websphere JVM中,下一步就是字节码验证。在字节码验证期间,如果我们的类违反了JVM约束,就会出现此错误。
禁用字节码验证。转到
管理控制台
->
server1->
java和进程管理->进程定义->
JVM参数`在 JVM 参数中传递以下字符串
,然后在工作区中打开 ApplicationDeploymentDescriptor xml 文件,转到部署选项卡,为 war 以及第一个选项选择 PARENT_LAST。这会阻止 xml 验证错误。
Disable the Bytecode Verification
java.lang.VerifyError - Runtime Unchecked Exception
once class file is loaded in Websphere JVM, then byte code varification is the next process.during byte code verification if our class in violating the JVM constraints ,then this error appears.
disable bytecode verification. Go to
admin console
->
server1->
java and process management->process definition->
JVM arguments`And in JVM arguments pass the following string
and in workspace open the ApplicationDeploymentDescriptor xml file, go to deployment tab, select PARENT_LAST for war, as well as for first option. this stops the xml validations errors.
是的,这是正确的,这是您看到这种行为的唯一方式。我建议你看看“你真的有类加载器吗?”谈谈,因为你的问题没有单一或简短的答案。
http://www.slideshare.net/guestd56374/do-你真的得到了类加载器
http://www.parleys.com/#sl=2& st=5&id=1585
Yes, that's correct, it's the only way you can see such behaviour. I can suggest you yo take a look at the "Do you really get class loaders?" talk, as there is no single or short answer to your question.
http://www.slideshare.net/guestd56374/do-you-really-get-class-loaders
http://www.parleys.com/#sl=2&st=5&id=1585