it-swarm.com.ru

Имеете дело с "Xerces ад" в Java / Maven?

В моем офисе простого упоминания Word Xerces достаточно, чтобы вызвать убийственную ярость от разработчиков. Беглый взгляд на другие вопросы Xerces по SO, похоже, указывает на то, что почти все пользователи Maven в какой-то момент "тронуты" этой проблемой. К сожалению, понимание проблемы требует немного знаний об истории Xerces ...

История

  • Xerces - наиболее широко используемый синтаксический анализатор XML в экосистеме Java. Почти каждая библиотека или фреймворк, написанные на Java, в некоторой степени используют Xerces (транзитивно, если не напрямую).

  • Банки Xerces, включенные в официальные двоичные файлы , до сих пор не имеют версий. Например, файл jar реализации Xerces 2.11.0 называется xercesImpl.jar, а не xercesImpl-2.11.0.jar.

  • Команда Xerces не использует Maven , что означает, что они не загружают официальный релиз в Maven Central .

  • Раньше Xerces был выпущен как один jar (xerces.jar), но был разделен на два jar, один из которых содержал API (xml-apis.jar), а другой содержал реализации этих API (xercesImpl.jar). Многие старые POM Maven по-прежнему объявляют зависимость от xerces.jar. В некоторый момент в прошлом Xerces также выпускался как xmlParserAPIs.jar, от которого также зависят некоторые более старые POM.

  • Версии, назначаемые банкам xml-apis и xercesImpl теми, кто развертывает свои банки в репозиториях Maven, часто отличаются. Например, xml-apis может иметь версию 1.3.03, а xercesImpl - версию 2.8.0, даже если обе версии Xerces 2.8.0. Это связано с тем, что люди часто помечают банку xml-apis версией спецификаций, которые он реализует. Существует очень хорошая, но неполная разбивка этого здесь .

  • Чтобы усложнить ситуацию, Xerces - это синтаксический анализатор XML, используемый в эталонной реализации API-интерфейса Java для обработки XML (JAXP), включенного в JRE. Классы реализации переупаковываются в пространстве имен com.Sun.*, что делает опасным прямой доступ к ним, поскольку они могут быть недоступны в некоторых JRE. Однако не все функциональные возможности Xerces предоставляются через API Java.* и javax.*; например, нет API, который предоставляет сериализацию Xerces.

  • Добавляя к путанице, почти все контейнеры сервлетов (JBoss, Jetty, Glassfish, Tomcat и т.д.) Поставляются с Xerces в одной или нескольких папках /lib.

Проблемы

Решение конфликта

По некоторым - или, возможно, по всем - причинам, изложенным выше, многие организации публикуют и используют пользовательские сборки Xerces в своих POM. На самом деле это не проблема, если у вас небольшое приложение и вы используете только Maven Central, но это быстро становится проблемой для корпоративного программного обеспечения, где Artifactory или Nexus проксирует несколько репозиториев (JBoss, Hibernate и т.д.):

xml-apis proxied by Artifactory

Например, организация А может опубликовать xml-apis как:

<groupId>org.Apache.xerces</groupId>
<artifactId>xml-apis</artifactId>
<version>2.9.1</version>

Между тем организация B может опубликовать то же jar, что и:

<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
<version>1.3.04</version>

Хотя B jar является более низкой версией, чем A jar, Maven не знает, что это один и тот же артефакт, потому что у них разные groupIds. Таким образом, он не может выполнять разрешение конфликтов, и оба jars будут включены в качестве разрешенных зависимостей:

resolved dependencies with multiple xml-apis

Classloader Hell

Как упоминалось выше, JRE поставляется с Xerces в JAXP RI. Хотя было бы неплохо пометить все зависимости Xerces Maven как <exclusion>s или как <provided>, сторонний код, от которого вы зависите, может работать или не работать с версией, предоставленной в JAXP JDK, который вы используете. Кроме того, у вас есть контейнеры Xerces, отправленные в ваш контейнер сервлетов для борьбы. Это оставляет вам несколько вариантов: вы удаляете версию сервлета и надеетесь, что ваш контейнер работает на версии JAXP? Лучше ли оставить версию сервлета и надеяться, что фреймворки ваших приложений будут работать на версии сервлета? Если одному или двум из неразрешенных конфликтов, описанных выше, удастся проникнуть в ваш продукт (это легко случится в большой организации), вы быстро окажетесь в аду загрузчика классов, задаваясь вопросом, какую версию Xerces выбирает загрузчик классов во время выполнения и действительно ли она выберет одну и ту же банку в Windows и Linux (вероятно, нет).

Решения?

Мы попытались пометить все зависимости Xerces Maven как <provided> или как <exclusion>, но это сложно реализовать (особенно в большой команде), учитывая, что у артефактов очень много псевдонимов (xml-apis, xerces, xercesImpl, xmlParserAPIs и т.д.). Кроме того, наши сторонние библиотеки/фреймворки могут не работать на версии JAXP или версии, предоставляемой контейнером сервлета.

Как мы можем лучше всего решить эту проблему с Maven? Должны ли мы осуществлять такой детальный контроль над нашими зависимостями, а затем полагаться на многоуровневую загрузку классов? Есть ли способ глобально исключить все зависимости Xerces и заставить все наши фреймворки/библиотеки использовать версию JAXP?


UPDATE: Джошуа Спивак загрузил исправленную версию сценариев сборки Xerces в XERCESJ-1454 , что позволяет загружать в Maven Central. Проголосуйте/посмотрите/внесите свой вклад в эту проблему, и давайте решим эту проблему раз и навсегда.

673
Justin Garrick

Существует 2.11.0 JAR (и исходные JAR!) Xerces в Maven Central с 20 февраля 2013 года! Смотрите Xerces в Maven Central . Интересно, почему они не решили https://issues.Apache.org/jira/browse/XERCESJ-1454 ...

Я использовал:

<dependency>
    <groupId>xerces</groupId>
    <artifactId>xercesImpl</artifactId>
    <version>2.11.0</version>
</dependency>

и все зависимости исправлены - даже правильный xml-apis-1.4.01!

И что самое важное (и что не было очевидно в прошлом) - JAR в Maven Central - это тот же JAR, что и в официальном Xerces-J-bin.2.11.0.Zip дистрибутиве .

Однако я не смог найти версию xml-schema-1.1-beta - она ​​не может быть версией classifier- Maven из-за дополнительных зависимостей.

102
Grzegorz Grzybek

Честно говоря, почти все, с чем мы столкнулись, прекрасно работает с версией JAXP, поэтому мы всегда исключаемxml-apis и xercesImpl.

62
jtahlborn

Вы можете использовать плагин Maven Enforcer с правилом запрещенных зависимостей. Это позволит вам запретить все псевдонимы, которые вы не хотите, и разрешить только тот, который вы хотите. Эти правила не удастся собрать maven вашего проекта при нарушении. Кроме того, если это правило применяется ко всем проектам на предприятии, вы можете поместить конфигурацию плагина в корпоративный родительский модуль.

увидеть:

42
Travis Schneeberger

Я знаю, что это не дает точного ответа на этот вопрос, но для пользователей из Google, которые используют Gradle для управления своими зависимостями:

Мне удалось избавиться от всех проблем xerces/Java8 с Gradle следующим образом:

configurations {
    all*.exclude group: 'xml-apis'
    all*.exclude group: 'xerces'
}
27
netmikey

Я думаю, есть один вопрос, на который вам нужно ответить:

Существует ли xerces * .jar, с которым все в вашем приложении может жить?

Если нет, то вы в основном испорчены и должны использовать что-то вроде OSGI, что позволяет загружать разные версии библиотеки одновременно. Имейте в виду, что это в основном заменяет проблемы с jar-версиями на проблемы с загрузчиком классов ...

Если такая версия существует, вы можете заставить свой репозиторий возвращать эту версию для всех видов зависимостей. Это уродливый хак, и он в конечном итоге будет иметь одну и ту же реализацию xerces в вашем classpath несколько раз, но лучше, чем иметь несколько разных версий xerces.

Вы можете исключить каждую зависимость от xerces и добавить ее в версию, которую хотите использовать.

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

Для версии, содержащейся в вашей среде выполнения, вы должны убедиться, что она либо будет удалена из пути к классам приложения, либо файлы jar приложения будут рассмотрены в первую очередь для загрузки классов, прежде чем будет рассмотрена папка lib на сервере.

Итак, чтобы подвести итог: это беспорядок, и это не изменится.

16
Jens Schauder

Вы должны сначала отладить, чтобы помочь определить свой уровень ада XML. На мой взгляд, первым шагом является добавление

-Djavax.xml.parsers.SAXParserFactory=com.Sun.org.Apache.xerces.internal.jaxp.SAXParserFactoryImpl
-Djavax.xml.transform.TransformerFactory=com.Sun.org.Apache.xalan.internal.xsltc.trax.TransformerFactoryImpl
-Djavax.xml.parsers.DocumentBuilderFactory=com.Sun.org.Apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl

в командной строке. Если это работает, тогда начните исключать библиотеки. Если нет, то добавьте

-Djaxp.debug=1

в командной строке.

6
Derek Bennett

Есть еще один вариант, который здесь не исследовался: объявление зависимостей Xerces в Maven как необязательно:

<dependency>
   <groupId>xerces</groupId>
   <artifactId>xercesImpl</artifactId>
   <version>...</version>
   <optional>true</optional>
</dependency>

По сути, это заставляет всех иждивенцев объявлять их версию Xerces, иначе их проект не скомпилируется. Если они хотят переопределить эту зависимость, они могут это сделать, но тогда у них будет потенциальная проблема.

Это создает сильный стимул для последующих проектов:

  • Примите активное решение. Они идут с той же версией Xerces или используют что-то еще?
  • На самом деле тестируйте их анализ (например, через модульное тестирование) и загрузку классов, а также не загромождайте их путь к классам.

Не все разработчики отслеживают вновь введенные зависимости (например, с помощью mvn dependency:tree). Такой подход немедленно доведет дело до их сведения.

Это хорошо работает в нашей организации. До его введения мы жили в том же аду, который описывает ОП.

6
Daniel

Каждый проект maven должен прекратиться в зависимости от xerces, вероятно, нет. XML API и Impl были частью Java с 1.4. Нет необходимости зависеть от xerces или XML API, все равно что сказать, что вы зависите от Java или Swing. Это неявно.

Если бы я был начальником репозитория Maven, я бы написал скрипт для рекурсивного удаления зависимостей xerces и написал бы read me, который говорит, что для этого репо требуется Java 1.4.

Все, что действительно ломается, потому что оно ссылается на Xerces напрямую через org. Для импорта Apache требуется исправление кода, чтобы довести его до уровня Java 1.4 (и с 2002 года), или решение на уровне JVM через одобренные библиотеки, а не в Maven.

3
teknopaul

По-видимому, xerces:xml-apis:1.4.01 больше не находится в maven central, на что, однако, ссылается xerces:xercesImpl:2.11.0.

Это работает для меня:

<dependency>
  <groupId>xerces</groupId>
  <artifactId>xercesImpl</artifactId>
  <version>2.11.0</version>
  <exclusions>
    <exclusion>
      <groupId>xerces</groupId>
      <artifactId>xml-apis</artifactId>
    </exclusion>
  </exclusions>
</dependency>
<dependency>
  <groupId>xml-apis</groupId>
  <artifactId>xml-apis</artifactId>
  <version>1.4.01</version>
</dependency>
2
thrau

Мой друг, это очень просто, вот пример:

<dependency>
            <groupId>xalan</groupId>
            <artifactId>xalan</artifactId>
            <version>2.7.2</version>
            <scope>${my-scope}</scope>
            <exclusions>
                <exclusion>
                    <groupId>xml-apis</groupId>
                    <artifactId>xml-apis</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

И если вы хотите проверить в терминале (в этом примере консоль Windows), что у вашего maven-дерева нет проблем:

mvn dependency:tree -Dverbose | grep --color=always '(.* conflict\|^' | less -r
2
Eduardo

Что поможет, кроме исключения, это модульные зависимости.

С одной плоской загрузкой классов (автономное приложение) или полуиерархическая (JBoss AS/EAP 5.x) это было проблемой.

Но с модульными структурами, такими как OSGi и JBoss Modules , это больше не такая большая боль. Библиотеки могут использовать любую библиотеку, какую захотят, независимо друг от друга.

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

Хорошим примером модулей JBoss в действии, естественно, является JBoss AS 7 / EAP 6 / WildFly 8 , для которого он был изначально разработан.

Пример определения модуля:

<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="org.jboss.msc">
    <main-class name="org.jboss.msc.Version"/>
    <properties>
        <property name="my.property" value="foo"/>
    </properties>
    <resources>
        <resource-root path="jboss-msc-1.0.1.GA.jar"/>
    </resources>
    <dependencies>
        <module name="javax.api"/>
        <module name="org.jboss.logging"/>
        <module name="org.jboss.modules"/>
        <!-- Optional deps -->
        <module name="javax.inject.api" optional="true"/>
        <module name="org.jboss.threads" optional="true"/>
    </dependencies>
</module>

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

Обратите внимание, что есть работа по модульности ведется для Java 8 , но AFAIK, в первую очередь, для модульной работы самой JRE, не уверен, будет ли она применима в приложения.

2
Ondra Žižka