Developing a heavily XML-based Java-application, I recently encountered an interesting problem on Ubuntu Linux.
开发一个高度基于xml的java应用程序,我最近在Ubuntu Linux上遇到了一个有趣的问题。
My application, using the Java Plugin Framework, appears unable to convert a dom4j-created XML document to Batik's implementation of the SVG specification.
我的应用程序使用Java插件框架,似乎无法将dom4j创建的XML文档转换为Batik的SVG规范实现。
On the console, I learn that an error occurs:
在控制台,我知道发生了一个错误:
Exception in thread "AWT-EventQueue-0" java.lang.LinkageError: loader constraint violation in interface itable initialization: when resolving method "org.apache.batik.dom.svg.SVGOMDocument.createAttribute(Ljava/lang/String;)Lorg/w3c/dom/Attr;" the class loader (instance of org/java/plugin/standard/StandardPluginClassLoader) of the current class, org/apache/batik/dom/svg/SVGOMDocument, and the class loader (instance of <bootloader>) for interface org/w3c/dom/Document have different Class objects for the type org/w3c/dom/Attr used in the signature at org.apache.batik.dom.svg.SVGDOMImplementation.createDocument(SVGDOMImplementation.java:149) at org.dom4j.io.DOMWriter.createDomDocument(DOMWriter.java:361) at org.dom4j.io.DOMWriter.write(DOMWriter.java:138)
I figure that the problem is caused by a conflict between the original classloader from the JVM and the classloader deployed by the plugin framework.
我认为这个问题是由来自JVM的原始类加载器和由插件框架部署的类加载器之间的冲突造成的。
To my knowledge, it's not possible to specify a classloader for the framework to use. It might be possible to hack it, but I would prefer a less aggressive approach to solving this problem, since (for whatever reason) it only occurs on Linux systems.
据我所知,不可能为框架指定要使用的类加载器。可能有可能破解它,但我宁愿采用一种不那么激进的方法来解决这个问题,因为(无论出于什么原因)它只出现在Linux系统上。
Has one of you encountered such a problem and has any idea how to fix it or at least get to the core of the issue?
你们当中有谁遇到过这样的问题,并且知道如何解决这个问题,或者至少解决这个问题的核心问题?
5 个解决方案
#1
54
LinkageError is what you'll get in a classic case where you have a class C loaded by more than one classloader and those classes are being used together in the same code (compared, cast, etc). It doesn't matter if it is the same Class name or even if it's loaded from the identical jar - a Class from one classloader is always treated as a different Class if loaded from another classloader.
LinkageError是一个典型的例子,在这个例子中,一个类C被多个类加载器加载,并且这些类在相同的代码中一起使用(比较、转换等)。不管它是相同的类名,还是从相同的jar加载的——如果从另一个类加载器加载,来自一个类加载器的类总是被视为不同的类。
The message (which has improved a lot over the years) says:
这条信息(多年来已经改善了很多)是:
Exception in thread "AWT-EventQueue-0" java.lang.LinkageError:
loader constraint violation in interface itable initialization:
when resolving method "org.apache.batik.dom.svg.SVGOMDocument.createAttribute(Ljava/lang/String;)Lorg/w3c/dom/Attr;"
the class loader (instance of org/java/plugin/standard/StandardPluginClassLoader)
of the current class, org/apache/batik/dom/svg/SVGOMDocument,
and the class loader (instance of ) for interface org/w3c/dom/Document
have different Class objects for the type org/w3c/dom/Attr used in the signature
So, here the problem is in resolving the SVGOMDocument.createAttribute() method, which uses org.w3c.dom.Attr (part of the standard DOM library). But, the version of Attr loaded with Batik was loaded from a different classloader than the instance of Attr you're passing to the method.
因此,这里的问题在于解决SVGOMDocument.createAttribute()方法,该方法使用org.w3c.dom。Attr(标准DOM库的一部分)。但是,装载Batik的Attr版本是从一个不同的类加载器加载的,而不是您要传递给方法的Attr实例。
You'll see that Batik's version seems to be loaded from the Java plugin. And yours is being loaded from " ", which is most likely one of the built-in JVM loaders (boot classpath, ESOM, or classpath).
您将看到Batik的版本似乎是从Java插件中加载的。而您的正在从“”加载,这很可能是内置的JVM加载器(引导类路径、ESOM或类路径)。
The three prominent classloader models are:
三个突出的类加载器模型是:
- delegation (the default in the JDK - ask parent, then me)
- 委托(JDK中的默认值——问父方,然后问我)
- post-delegation (common in plugins, servlets, and places where you want isolation - ask me, then parent)
- 后委托(在插件、servlet和需要隔离的地方很常见——请先问我,然后再问父)
- sibling (common in dependency models like OSGi, Eclipse, etc)
- 同胞(在类似OSGi、Eclipse等依赖模型中很常见)
I don't know what delegation strategy the JPF classloader uses, but the key is that you want one version of the dom library to be loaded and everyone to source that class from the same location. That may mean removing it from the classpath and loading as a plugin, or preventing Batik from loading it, or something else.
我不知道JPF类加载器使用的是什么委托策略,但关键是您希望加载一个版本的dom库,并让每个人从相同的位置获取该类。这可能意味着要将它从类路径中删除并作为插件加载,或者防止Batik加载它,或者做其他事情。
#2
14
Sounds like a classloader hierarchy problem. I can't tell what type of environment your application is deployed in, but sometimes this problem can occur in a web environment - where the application server creates a hierarchy of classloaders, resembling something like:
听起来像是类加载器层次结构的问题。我不知道您的应用程序部署在什么类型的环境中,但是有时候这个问题可能出现在web环境中——在这个环境中,应用服务器创建了一个类加载器的层次结构,类似于:
javahome/lib - as root
appserver/lib - as child of root
webapp/WEB-INF/lib - as child of child of root
etc
javahome/lib -作为根appserver/lib -作为根webapp/WEB-INF/lib的子程序-作为根的子程序
Usually classloaders delegate loading to their parent classloader (this is known as "parent-first
"), and if that classloader cannot find the class, then the child classloader attempts to. For example, if a class deployed as a JAR in webapp/WEB-INF/lib tries to load a class, first it asks the classloader corresponding to appserver/lib to load the class (which in turn asks the classloader corresponding to javahome/lib to load the class), and if this lookup fails, then WEB-INF/lib is searched for a match to this class.
通常,类加载器将加载委托给它们的父类加载器(这被称为“父优先”),如果这个类加载器找不到类,那么子类加载器会尝试这样做。例如,如果一个类部署为JAR webapp / web - inf / lib试图装入一个类时,它首先要求appserver / lib对应的类加载器来加载类(反过来问javahome / lib对应的类加载器来加载类),如果该查找失败,那么web - inf / lib寻找一个匹配这个类。
In a web environment, you can run into problems with this hierarchy. For example, one mistake/problem I've run into before was when a class in WEB-INF/lib depended on a class deployed in appserver/lib, which in turn depended on a class deployed in WEB-INF/lib. This caused failures because while classloaders are able to delegate to the parent classloader, they cannot delegate back down the tree. So, the WEB-INF/lib classloader would ask appserver/lib classloader for a class, appserver/lib classloader would load that class and try to load the dependent class, and fail since it could not find that class in appserver/lib or javahome/lib.
在web环境中,您可能会遇到这种层次结构的问题。例如,我以前遇到过的一个错误/问题是WEB-INF/lib中的类依赖于部署在appserver/lib中的类,而这个类又依赖于部署在WEB-INF/lib中的类。这导致了失败,因为虽然类装入器可以委托给父类装入器,但是它们不能委托回树。因此,WEB-INF/lib类加载器会向appserver/lib类加载器请求一个类,appserver/lib类加载器会加载该类并尝试加载从属类,但失败了,因为它无法在appserver/lib或javahome/lib中找到该类。
So, while you may not be deploying your app in a web/app server environment, my too-long explanation might apply to you if your environment has a hierarchy of classloaders set up. Does it? Is JPF doing some sort of classloader magic to be able to implement it's plugin features?
因此,虽然您可能不会在web/应用服务器环境中部署应用程序,但如果您的环境设置了类加载器的层次结构,那么我的冗长解释可能适用于您。不是吗?JPF是否做了某种类加载程序魔法来实现它的插件特性?
#3
6
May be this will help someone because it works out pretty good for me. The issue can be solve by integrating your own dependencies. Follow this simple steps
也许这能帮助别人,因为这对我很有好处。这个问题可以通过集成您自己的依赖项来解决。按照这个简单的步骤
First check the error which should be like this :
- Method execution failed:
- 方法执行失败:
- java.lang.LinkageError: loader constraint violation:
- . lang。LinkageError:装入器约束违反:
- when resolving method "org.slf4j.impl.StaticLoggerBinder.getLoggerFactory()Lorg/slf4j/ILoggerFactory;"
- 当解决方法“org.slf4j.impl.StaticLoggerBinder.getLoggerFactory()Lorg slf4j / ILoggerFactory;”
- the class loader (instance of org/openmrs/module/ModuleClassLoader) of the current class, org/slf4j/LoggerFactory,
- 当前类的类装入器(org/openmrs/module/ModuleClassLoader的实例),
- and the class loader (instance of org/apache/catalina/loader/WebappClassLoader) for resolved class, org/slf4j/impl/StaticLoggerBinder,
- 以及解析类的类装入器(org/apache/catalina/loader/WebappClassLoader的实例),
- have different Class objects for the type taticLoggerBinder.getLoggerFactory()Lorg/slf4j/ILoggerFactory; used in the signature
- 为类型taticLoggerBinder.getLoggerFactory()Lorg/slf4j/ILoggerFactory拥有不同的类对象;签名中使用
-
See the two highlighted class. Google search for them like "StaticLoggerBinder.class jar download" & "LoggeraFactory.class jar download". This will show you first or in some case second link (Site is http://www.java2s.com ) which is one of the jar version you have included in your project. You can smartly identify it yourself, but we are addicted of google ;)
请参见突出显示的两个类。谷歌搜索它们,比如“StaticLoggerBinder”。类jar下载“&”LoggeraFactory。类jar下载”。这将向您展示first或在某些情况下second link(站点是http://www.java2s.com),这是您在项目中包含的jar版本之一。你可以自己聪明地识别它,但我们沉迷于谷歌;
-
After that you will know the jar file name, in my case it is like slf4j-log4j12-1.5.6.jar & slf4j-api-1.5.8
之后您将知道jar文件名,在我的例子中,它类似于slf4j-log4j12-1.5.6。jar & slf4j-api-1.5.8
- Now the latest version of this file is available here http://mvnrepository.com/ (actually all version till date, this is the site from where maven get your dependencies).
- 现在,这个文件的最新版本可以在http://mvnrepository.com/(实际上,直到日期的所有版本,这是maven获得依赖项的站点)中找到。
- Now add both file as a dependencies with the latest version (or keep both file version same, either chosen version is old). Following is the dependency you have to include in pom.xml
- 现在将这两个文件作为最新版本的依赖项添加(或者保持两个文件版本相同,或者选择的版本是旧的)。下面是您必须包含在pom.xml中的依赖项
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.7</version>
</dependency>
#4
5
Can you specify a class loader? If not, try specifying the context class loader like so:
可以指定类装入器吗?如果没有,请尝试指定上下文类装入器:
Thread thread = Thread.currentThread();
ClassLoader contextClassLoader = thread.getContextClassLoader();
try {
thread.setContextClassLoader(yourClassLoader);
callDom4j();
} finally {
thread.setContextClassLoader(contextClassLoader);
}
I'm not familiar with the Java Plugin Framework, but I write code for Eclipse, and I run into similar issues from time to time. I don't guarantee it'll fix it, but it's probably worth a shot.
我不熟悉Java插件框架,但是我为Eclipse编写代码,并且不时遇到类似的问题。我不能保证它会修复它,但它可能值得一试。
#5
3
The answers from Alex and Matt are very helpful. I could benefit from their analysis too.
艾利克斯和马特的回答很有帮助。我也可以从他们的分析中获益。
I had the same problem when using the Batik library in a Netbeans RCP framework, the Batik library being included as a "Library Wrapper Module". If some other module makes use of XML apis, and no dependency on Batik is needed and established for that module, the class loader constraint violation problem arises with similar error messages.
在Netbeans RCP框架中使用Batik库时,我遇到了同样的问题,Batik库被包含在“库包装模块”中。如果其他一些模块使用了XML api,并且不需要对Batik进行任何依赖,并且为该模块建立了类加载器约束违例问题,就会出现类似的错误消息。
In Netbeans, individual modules use dedicated class loaders, and the dependence relationship between modules implies suitable class loader delegation routing.
在Netbeans中,单个模块使用专用的类加载器,模块之间的依赖关系意味着合适的类加载器委托路由。
I could resolve the problem by simply omitting the xml-apis jar file from the Batik library bundle.
我可以通过从Batik库包中删除xml-api jar文件来解决这个问题。
#1
54
LinkageError is what you'll get in a classic case where you have a class C loaded by more than one classloader and those classes are being used together in the same code (compared, cast, etc). It doesn't matter if it is the same Class name or even if it's loaded from the identical jar - a Class from one classloader is always treated as a different Class if loaded from another classloader.
LinkageError是一个典型的例子,在这个例子中,一个类C被多个类加载器加载,并且这些类在相同的代码中一起使用(比较、转换等)。不管它是相同的类名,还是从相同的jar加载的——如果从另一个类加载器加载,来自一个类加载器的类总是被视为不同的类。
The message (which has improved a lot over the years) says:
这条信息(多年来已经改善了很多)是:
Exception in thread "AWT-EventQueue-0" java.lang.LinkageError:
loader constraint violation in interface itable initialization:
when resolving method "org.apache.batik.dom.svg.SVGOMDocument.createAttribute(Ljava/lang/String;)Lorg/w3c/dom/Attr;"
the class loader (instance of org/java/plugin/standard/StandardPluginClassLoader)
of the current class, org/apache/batik/dom/svg/SVGOMDocument,
and the class loader (instance of ) for interface org/w3c/dom/Document
have different Class objects for the type org/w3c/dom/Attr used in the signature
So, here the problem is in resolving the SVGOMDocument.createAttribute() method, which uses org.w3c.dom.Attr (part of the standard DOM library). But, the version of Attr loaded with Batik was loaded from a different classloader than the instance of Attr you're passing to the method.
因此,这里的问题在于解决SVGOMDocument.createAttribute()方法,该方法使用org.w3c.dom。Attr(标准DOM库的一部分)。但是,装载Batik的Attr版本是从一个不同的类加载器加载的,而不是您要传递给方法的Attr实例。
You'll see that Batik's version seems to be loaded from the Java plugin. And yours is being loaded from " ", which is most likely one of the built-in JVM loaders (boot classpath, ESOM, or classpath).
您将看到Batik的版本似乎是从Java插件中加载的。而您的正在从“”加载,这很可能是内置的JVM加载器(引导类路径、ESOM或类路径)。
The three prominent classloader models are:
三个突出的类加载器模型是:
- delegation (the default in the JDK - ask parent, then me)
- 委托(JDK中的默认值——问父方,然后问我)
- post-delegation (common in plugins, servlets, and places where you want isolation - ask me, then parent)
- 后委托(在插件、servlet和需要隔离的地方很常见——请先问我,然后再问父)
- sibling (common in dependency models like OSGi, Eclipse, etc)
- 同胞(在类似OSGi、Eclipse等依赖模型中很常见)
I don't know what delegation strategy the JPF classloader uses, but the key is that you want one version of the dom library to be loaded and everyone to source that class from the same location. That may mean removing it from the classpath and loading as a plugin, or preventing Batik from loading it, or something else.
我不知道JPF类加载器使用的是什么委托策略,但关键是您希望加载一个版本的dom库,并让每个人从相同的位置获取该类。这可能意味着要将它从类路径中删除并作为插件加载,或者防止Batik加载它,或者做其他事情。
#2
14
Sounds like a classloader hierarchy problem. I can't tell what type of environment your application is deployed in, but sometimes this problem can occur in a web environment - where the application server creates a hierarchy of classloaders, resembling something like:
听起来像是类加载器层次结构的问题。我不知道您的应用程序部署在什么类型的环境中,但是有时候这个问题可能出现在web环境中——在这个环境中,应用服务器创建了一个类加载器的层次结构,类似于:
javahome/lib - as root
appserver/lib - as child of root
webapp/WEB-INF/lib - as child of child of root
etc
javahome/lib -作为根appserver/lib -作为根webapp/WEB-INF/lib的子程序-作为根的子程序
Usually classloaders delegate loading to their parent classloader (this is known as "parent-first
"), and if that classloader cannot find the class, then the child classloader attempts to. For example, if a class deployed as a JAR in webapp/WEB-INF/lib tries to load a class, first it asks the classloader corresponding to appserver/lib to load the class (which in turn asks the classloader corresponding to javahome/lib to load the class), and if this lookup fails, then WEB-INF/lib is searched for a match to this class.
通常,类加载器将加载委托给它们的父类加载器(这被称为“父优先”),如果这个类加载器找不到类,那么子类加载器会尝试这样做。例如,如果一个类部署为JAR webapp / web - inf / lib试图装入一个类时,它首先要求appserver / lib对应的类加载器来加载类(反过来问javahome / lib对应的类加载器来加载类),如果该查找失败,那么web - inf / lib寻找一个匹配这个类。
In a web environment, you can run into problems with this hierarchy. For example, one mistake/problem I've run into before was when a class in WEB-INF/lib depended on a class deployed in appserver/lib, which in turn depended on a class deployed in WEB-INF/lib. This caused failures because while classloaders are able to delegate to the parent classloader, they cannot delegate back down the tree. So, the WEB-INF/lib classloader would ask appserver/lib classloader for a class, appserver/lib classloader would load that class and try to load the dependent class, and fail since it could not find that class in appserver/lib or javahome/lib.
在web环境中,您可能会遇到这种层次结构的问题。例如,我以前遇到过的一个错误/问题是WEB-INF/lib中的类依赖于部署在appserver/lib中的类,而这个类又依赖于部署在WEB-INF/lib中的类。这导致了失败,因为虽然类装入器可以委托给父类装入器,但是它们不能委托回树。因此,WEB-INF/lib类加载器会向appserver/lib类加载器请求一个类,appserver/lib类加载器会加载该类并尝试加载从属类,但失败了,因为它无法在appserver/lib或javahome/lib中找到该类。
So, while you may not be deploying your app in a web/app server environment, my too-long explanation might apply to you if your environment has a hierarchy of classloaders set up. Does it? Is JPF doing some sort of classloader magic to be able to implement it's plugin features?
因此,虽然您可能不会在web/应用服务器环境中部署应用程序,但如果您的环境设置了类加载器的层次结构,那么我的冗长解释可能适用于您。不是吗?JPF是否做了某种类加载程序魔法来实现它的插件特性?
#3
6
May be this will help someone because it works out pretty good for me. The issue can be solve by integrating your own dependencies. Follow this simple steps
也许这能帮助别人,因为这对我很有好处。这个问题可以通过集成您自己的依赖项来解决。按照这个简单的步骤
First check the error which should be like this :
- Method execution failed:
- 方法执行失败:
- java.lang.LinkageError: loader constraint violation:
- . lang。LinkageError:装入器约束违反:
- when resolving method "org.slf4j.impl.StaticLoggerBinder.getLoggerFactory()Lorg/slf4j/ILoggerFactory;"
- 当解决方法“org.slf4j.impl.StaticLoggerBinder.getLoggerFactory()Lorg slf4j / ILoggerFactory;”
- the class loader (instance of org/openmrs/module/ModuleClassLoader) of the current class, org/slf4j/LoggerFactory,
- 当前类的类装入器(org/openmrs/module/ModuleClassLoader的实例),
- and the class loader (instance of org/apache/catalina/loader/WebappClassLoader) for resolved class, org/slf4j/impl/StaticLoggerBinder,
- 以及解析类的类装入器(org/apache/catalina/loader/WebappClassLoader的实例),
- have different Class objects for the type taticLoggerBinder.getLoggerFactory()Lorg/slf4j/ILoggerFactory; used in the signature
- 为类型taticLoggerBinder.getLoggerFactory()Lorg/slf4j/ILoggerFactory拥有不同的类对象;签名中使用
-
See the two highlighted class. Google search for them like "StaticLoggerBinder.class jar download" & "LoggeraFactory.class jar download". This will show you first or in some case second link (Site is http://www.java2s.com ) which is one of the jar version you have included in your project. You can smartly identify it yourself, but we are addicted of google ;)
请参见突出显示的两个类。谷歌搜索它们,比如“StaticLoggerBinder”。类jar下载“&”LoggeraFactory。类jar下载”。这将向您展示first或在某些情况下second link(站点是http://www.java2s.com),这是您在项目中包含的jar版本之一。你可以自己聪明地识别它,但我们沉迷于谷歌;
-
After that you will know the jar file name, in my case it is like slf4j-log4j12-1.5.6.jar & slf4j-api-1.5.8
之后您将知道jar文件名,在我的例子中,它类似于slf4j-log4j12-1.5.6。jar & slf4j-api-1.5.8
- Now the latest version of this file is available here http://mvnrepository.com/ (actually all version till date, this is the site from where maven get your dependencies).
- 现在,这个文件的最新版本可以在http://mvnrepository.com/(实际上,直到日期的所有版本,这是maven获得依赖项的站点)中找到。
- Now add both file as a dependencies with the latest version (or keep both file version same, either chosen version is old). Following is the dependency you have to include in pom.xml
- 现在将这两个文件作为最新版本的依赖项添加(或者保持两个文件版本相同,或者选择的版本是旧的)。下面是您必须包含在pom.xml中的依赖项
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.7</version>
</dependency>
#4
5
Can you specify a class loader? If not, try specifying the context class loader like so:
可以指定类装入器吗?如果没有,请尝试指定上下文类装入器:
Thread thread = Thread.currentThread();
ClassLoader contextClassLoader = thread.getContextClassLoader();
try {
thread.setContextClassLoader(yourClassLoader);
callDom4j();
} finally {
thread.setContextClassLoader(contextClassLoader);
}
I'm not familiar with the Java Plugin Framework, but I write code for Eclipse, and I run into similar issues from time to time. I don't guarantee it'll fix it, but it's probably worth a shot.
我不熟悉Java插件框架,但是我为Eclipse编写代码,并且不时遇到类似的问题。我不能保证它会修复它,但它可能值得一试。
#5
3
The answers from Alex and Matt are very helpful. I could benefit from their analysis too.
艾利克斯和马特的回答很有帮助。我也可以从他们的分析中获益。
I had the same problem when using the Batik library in a Netbeans RCP framework, the Batik library being included as a "Library Wrapper Module". If some other module makes use of XML apis, and no dependency on Batik is needed and established for that module, the class loader constraint violation problem arises with similar error messages.
在Netbeans RCP框架中使用Batik库时,我遇到了同样的问题,Batik库被包含在“库包装模块”中。如果其他一些模块使用了XML api,并且不需要对Batik进行任何依赖,并且为该模块建立了类加载器约束违例问题,就会出现类似的错误消息。
In Netbeans, individual modules use dedicated class loaders, and the dependence relationship between modules implies suitable class loader delegation routing.
在Netbeans中,单个模块使用专用的类加载器,模块之间的依赖关系意味着合适的类加载器委托路由。
I could resolve the problem by simply omitting the xml-apis jar file from the Batik library bundle.
我可以通过从Batik库包中删除xml-api jar文件来解决这个问题。