Как бороться с LinkageErrors в Java? - PullRequest
56 голосов
/ 28 октября 2008

Разрабатывая Java-приложение, основанное на XML, я недавно столкнулся с интересной проблемой в Ubuntu Linux.

Мое приложение, использующее Java Plugin Framework , похоже, не может преобразовать XML-документ, созданный dom4j , в реализацию батика спецификации SVG.

На консоли узнаю, что происходит ошибка:

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)

Я полагаю, что проблема вызвана конфликтом между исходным загрузчиком классов из JVM и загрузчиком классов, развернутым с помощью инфраструктуры плагинов.

Насколько мне известно, невозможно указать загрузчик классов для используемой платформы. Возможно, это можно взломать, но я бы предпочел менее агрессивный подход к решению этой проблемы, поскольку (по любой причине) это происходит только в системах Linux.

Кто-нибудь из вас сталкивался с такой проблемой и знает, как ее исправить или, по крайней мере, добраться до сути проблемы?

Ответы [ 6 ]

60 голосов
/ 29 октября 2008

LinkageError - это то, что вы получите в классическом случае, когда у вас есть класс C, загруженный более чем одним загрузчиком классов, и эти классы используются вместе в одном коде (сравнение, приведение и т. Д.). Неважно, является ли оно тем же именем класса или даже загружено из идентичного jar - класс из одного загрузчика классов всегда обрабатывается как другой класс, если загружается из другого загрузчика классов.

Сообщение (которое значительно улучшилось за эти годы) гласит:

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

Итак, здесь проблема заключается в разрешении метода SVGOMDocument.createAttribute (), который использует org.w3c.dom.Attr (часть стандартной библиотеки DOM). Но версия Attr, загруженная с помощью Batik, была загружена из загрузчика классов, отличного от экземпляра Attr, который вы передаете методу.

Вы увидите, что версия Batik, похоже, загружена из плагина Java. И ваш загружается из "", который, скорее всего, является одним из встроенных загрузчиков JVM (boot classpath, ESOM или classpath).

Три известные модели загрузчиков классов:

  • делегирование (по умолчанию в JDK - спросите родителя, затем меня)
  • пост-делегирование (часто встречается в плагинах, сервлетах и ​​местах, где вы хотите изолировать - спросите меня, затем родитель)
  • брат (обычно в моделях зависимостей, таких как OSGi, Eclipse и т. Д.)

Я не знаю, какую стратегию делегирования использует загрузчик классов JPF, но суть в том, что вы хотите, чтобы одна версия библиотеки dom была загружена и каждый мог получать этот класс из одного места. Это может означать удаление его из пути к классам и загрузка в качестве плагина, или запрет Батику загрузить его, или что-то еще.

17 голосов
/ 28 октября 2008

Походит на проблему иерархии загрузчика классов. Я не могу сказать, в какой среде развертывается ваше приложение, но иногда эта проблема может возникать в веб-среде - где сервер приложений создает иерархию загрузчиков классов, напоминающую что-то вроде:

javahome / lib - от имени root
appserver / lib - как дочерний элемент root
webapp / WEB-INF / lib - как дочерний элемент root
и т.д.

Обычно загрузчики классов делегируют загрузку своему родительскому загрузчику классов (это известно как "parent-first"), и если этот загрузчик классов не может найти класс, тогда попытается дочерний загрузчик классов. Например, если класс, развернутый как JAR в webapp / WEB-INF / lib, пытается загрузить класс, сначала он запрашивает загрузчик класса, соответствующий appserver / lib, для загрузки класса (который, в свою очередь, запрашивает загрузчик классов, соответствующий javahome / lib загрузить класс), и если этот поиск не удался, то выполняется поиск соответствия этому классу в WEB-INF / lib.

В веб-среде вы можете столкнуться с проблемами в этой иерархии. Например, одна ошибка / проблема, с которой я сталкивался ранее, заключалась в том, что класс в WEB-INF / lib зависел от класса, развернутого в appserver / lib, который, в свою очередь, зависел от класса, развернутого в WEB-INF / lib. Это вызвало сбои, потому что, хотя загрузчики классов могут делегировать родительскому загрузчику классов, они не могут делегировать обратно по дереву. Таким образом, загрузчик классов WEB-INF / lib запросит класс appserver / lib для класса, applover / lib загрузчик классов загрузит этот класс и попытается загрузить зависимый класс, и потерпит неудачу, так как не может найти этот класс в appserver / lib или javahome /lib.

Итак, хотя вы, возможно, не развертываете свое приложение в среде веб-сервера / приложения, мое слишком длинное объяснение может быть применимо к вам, если в вашей среде настроена иерархия загрузчиков классов. Является ли? JPF делает что-то вроде магии загрузчика классов, чтобы иметь возможность реализовать функции плагина?

6 голосов
/ 25 июня 2014

Может быть, это кому-нибудь поможет, потому что у меня это получается довольно хорошо. Проблема может быть решена путем интеграции ваших собственных зависимостей. Следуйте этим простым шагам

Сначала проверьте ошибку, которая должна выглядеть следующим образом:

  • Ошибка выполнения метода:
  • java.lang.LinkageError: нарушение ограничения загрузчика:
  • при разрешении метода "org.slf4j.impl. StaticLoggerBinder .getLoggerFactory () Lorg / slf4j / ILoggerFactory;"
  • загрузчик классов (экземпляр org / openmrs / module / ModuleClassLoader) текущего класса, org / slf4j / LoggerFactory ,
  • и загрузчик классов (экземпляр org / apache / catalina / loader / WebappClassLoader) для разрешенного класса, org / slf4j / impl / StaticLoggerBinder ,
  • имеют разные объекты Class для типа taticLoggerBinder.getLoggerFactory () Lorg / slf4j / ILoggerFactory; используется в подписи

  1. См. Два выделенных класса. Поищите их в Google, например "Загрузка jar-файла StaticLoggerBinder.class" и "Загрузка jar-файла LoggeraFactory.class" Это покажет вам первую или, в некоторых случаях, вторую ссылку (Сайт http://www.java2s.com), которая является одной из версий jar, включенных в ваш проект. Вы можете определить это сами, но мы зависим от Google;)

  2. После этого вы узнаете имя файла jar, в моем случае это похоже на slf4j-log4j12-1.5.6.jar & slf4j-api-1.5.8

  3. Теперь последняя версия этого файла доступна здесь http://mvnrepository.com/ (фактически вся версия до даты, это сайт, откуда maven получает ваши зависимости).
  4. Теперь добавьте оба файла как зависимости с последней версией (или оставьте обе версии файлов одинаковыми, либо выбранная версия устарела). Ниже приведена зависимость, которую вы должны включить в 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>

How to get dependecy definition from Maven Site

5 голосов
/ 28 октября 2008

Можете ли вы указать загрузчик классов? Если нет, попробуйте указать загрузчик класса контекста следующим образом:

Thread thread = Thread.currentThread();
ClassLoader contextClassLoader = thread.getContextClassLoader();
try {
    thread.setContextClassLoader(yourClassLoader);
    callDom4j();
} finally {
    thread.setContextClassLoader(contextClassLoader);
}

Я не знаком с платформой Java Plugin Framework, но я пишу код для Eclipse и время от времени сталкиваюсь с подобными проблемами. Я не гарантирую, что это все исправит, но, вероятно, стоит попробовать.

3 голосов
/ 23 ноября 2009

Ответы Алекса и Мэтта очень полезны. Я мог бы извлечь выгоду из их анализа тоже.

У меня была такая же проблема при использовании библиотеки Batik в среде RCP Netbeans, библиотека Batik включена в качестве «модуля обертки библиотеки». Если какой-то другой модуль использует XML API, и для этого модуля не требуется и не устанавливается зависимость от Batik, возникает проблема нарушения ограничения загрузчика классов с аналогичными сообщениями об ошибках.

В Netbeans отдельные модули используют выделенные загрузчики классов, а отношение зависимости между модулями подразумевает подходящую маршрутизацию делегирования загрузчика классов.

Я мог бы решить эту проблему, просто опустив jar-файл xml-apis из пакета библиотеки Batik.

0 голосов
/ 18 февраля 2019

Как указано в в этом вопросе , включение -verbose:class сделает журнал JVM информацией обо всех загружаемых классах, что может быть невероятно полезно, чтобы понять, откуда приходят классы в более сложных сценариях и приложения.

Вывод, который вы получаете, выглядит примерно так (скопировано с этого вопроса):

[Opened /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/sunrsasign.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/jsse.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/jce.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/charsets.jar]
[Loaded java.lang.Object from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.io.Serializable from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.lang.Comparable from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.lang.CharSequence from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.lang.String from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...