Как бороться с LinkageErrors в Java?


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

мое приложение, используя Java Plugin Framework, не удается преобразовать dom4j-создан XML-документ в батик-это реализация спецификации 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.

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

5 52

5 ответов:

LinkageError-это то, что вы получите в классическом случае, когда у вас есть класс C, загруженный более чем одним загрузчиком классов, и эти классы используются вместе в одном коде (compared, cast и т. д.). Это не имеет значения, если это то же самое имя класса или даже если он загружен из идентичного 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.дом.Attr (часть стандартной библиотеки DOM). Но версия Attr, загруженная с помощью Batik, была загружена из другого загрузчика классов, чем экземпляр Attr, который вы передаете методу.

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

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

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

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

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

javahome/lib-как root
appserver / lib - как потомок root
веб-приложения/веб-инф/Либ - как дитя дитя корневой
и т. д.

обычно загрузчики классов делегируют загрузку своим родителям загрузчик классов (это называется"parent-first"), и если этот загрузчик классов не может найти класс, то дочерний загрузчик классов пытается. Например, если класс развернут как Jar в веб-приложения/веб-инф/Либ пытается загрузить класс, первый просит загрузчика классов, соответствующий серверу приложений/lib для нагрузки класса (который, в свою очередь, просит загрузчик классов, соответствующие каталог-Java/lib, чтобы загрузить класс), и если этот поиск завершается неудачей, то веб-инф/Либ ищется матч в этом классе.

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

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

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

сначала проверьте ошибку, которая должна быть такой:

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

  1. см. два выделенных класса. Поиск Google для них, как " StaticLoggerBinder.класс jar скачать " & " LoggeraFactory.скачать класс баночка". Это покажет вам первую или в некоторых случаях вторую ссылку (сайт 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. теперь добавьте оба файла в качестве зависимостей с последней версией (или сохраните обе версии файлов одинаковыми, либо выбранная версия старая). Ниже приводится зависимость, которую вы должны включить в ПФЛ.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

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

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

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

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

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

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

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