JAXB недоступен на Tomcat 9 и Java 9/10

TLDR: на Java 9/10 веб-приложение в Tomcat не имеет доступа к JAXB, даже если его ссылочная реализация присутствует на пути к классу.

редактировать: нет, это не дубликат как разрешить java.ленг.NoClassDefFoundError: javax / xml / bind/JAXBException в Java 9 - как вы можете сказать что я пробовал раздел, я уже пробовал предлагаемые решения.

в Ситуация

у нас есть веб-приложение, которое работает на Tomcat и зависит от JAXB. Во время нашей миграции на Java 9 мы решили добавить реализация ссылки JAXB как регулярная зависимость.

все работало при запуске приложения из IDE со встроенным Tomcat, но при работе на реальном экземпляре Tomcat, я получаю эту ошибку:

Caused by: java.lang.RuntimeException: javax.xml.bind.JAXBException:
    Implementation of JAXB-API has not been found on module path or classpath.
 - with linked exception:
[java.lang.ClassNotFoundException: com.sun.xml.internal.bind.v2.ContextFactory]
    at [... our-code ...]
Caused by: javax.xml.bind.JAXBException: Implementation of JAXB-API has not been found on module path or classpath.
    at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:278) ~[jaxb-api-2.3.0.jar:2.3.0]
    at javax.xml.bind.ContextFinder.find(ContextFinder.java:421) ~[jaxb-api-2.3.0.jar:2.3.0]
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:721) ~[jaxb-api-2.3.0.jar:2.3.0]
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:662) ~[jaxb-api-2.3.0.jar:2.3.0]
    at [... our-code ...]
Caused by: java.lang.ClassNotFoundException: com.sun.xml.internal.bind.v2.ContextFactory
    at jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582) ~[?:?]
    at jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:190) ~[?:?]
    at java.lang.ClassLoader.loadClass(ClassLoader.java:499) ~[?:?]
    at javax.xml.bind.ServiceLoaderUtil.nullSafeLoadClass(ServiceLoaderUtil.java:122) ~[jaxb-api-2.3.0.jar:2.3.0]
    at javax.xml.bind.ServiceLoaderUtil.safeLoadClass(ServiceLoaderUtil.java:155) ~[jaxb-api-2.3.0.jar:2.3.0]
    at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:276) ~[jaxb-api-2.3.0.jar:2.3.0]
    at javax.xml.bind.ContextFinder.find(ContextFinder.java:421) ~[jaxb-api-2.3.0.jar:2.3.0]
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:721) ~[jaxb-api-2.3.0.jar:2.3.0]
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:662) ~[jaxb-api-2.3.0.jar:2.3.0]
    at [... our-code ...]

Примечание:

реализация JAXB-API не имеет найден на пути к модулю или пути к классу.

это соответствующие файлы в webapps/$app/WEB-INF/lib:

jaxb-api-2.3.0.jar
jaxb-core-2.3.0.jar
jaxb-impl-2.3.0.jar

что здесь происходит?

что я пробовал

добавление банок в Tomca CLASSPATH

возможно, это помогает добавить банки в путь класса Tomcat в setenv.sh?

CLASSPATH=
    .../webapps/$app/WEB-INF/lib/jaxb-api-2.3.0.jar:
    .../webapps/$app/WEB-INF/lib/jaxb-impl-2.3.0.jar:
    .../webapps/$app/WEB-INF/lib/jaxb-core-2.3.0.jar:
    .../webapps/$app/WEB-INF/lib/javax.activation-1.2.0.jar

Неа:

Caused by: javax.xml.bind.JAXBException: ClassCastException: attempting to cast
jar:file:.../webapps/$app/WEB-INF/lib/jaxb-api-2.3.0.jar!/javax/xml/bind/JAXBContext.class to
jar:file:.../webapps/$app/WEB-INF/lib/jaxb-api-2.3.0.jar!/javax/xml/bind/JAXBContext.class.
Please make sure that you are specifying the proper ClassLoader.    
    at javax.xml.bind.ContextFinder.handleClassCastException(ContextFinder.java:157) ~[jaxb-api-2.3.0.jar:2.3.0]
    at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:300) ~[jaxb-api-2.3.0.jar:2.3.0]
    at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:286) ~[jaxb-api-2.3.0.jar:2.3.0]
    at javax.xml.bind.ContextFinder.find(ContextFinder.java:409) ~[jaxb-api-2.3.0.jar:2.3.0]
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:721) ~[jaxb-api-2.3.0.jar:2.3.0]
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:662) ~[jaxb-api-2.3.0.jar:2.3.0]
    at de.disy.gis.webmapserver.factory.DefaultWmsRequestFactory.initializeCommandExtractor(DefaultWmsRequestFactory.java:103) ~[cadenza-gis-webmapserver-7.7-SNAPSHOT.jar:7.6]
    at de.disy.gis.webmapserver.factory.DefaultWmsRequestFactory.lambda$new(DefaultWmsRequestFactory.java:87) ~[cadenza-gis-webmapserver-7.7-SNAPSHOT.jar:7.6]

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

добавление модуля

я действительно не хочу добавлять java.XML.bind, но я все равно попробовал, добавив это в catalina.sh:

JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-modules=java.xml.bind"

не работает, хотя:

Caused by: java.lang.ClassCastException:
java.xml.bind/com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl
cannot be cast to com.sun.xml.bind.v2.runtime.JAXBContextImpl
    at [... our-code ...]

помимо другого класса и трассировки стека, это соответствует тому, что произошло ранее: класс JAXBContextImpl был загружен дважды, один раз из java.XML.bind (должно быть, загрузчик системного класса) и еще один раз (я предполагаю, загрузчиком приложения из банки).

поиск ошибок

поиск базы данных ошибок Tomcat нашел #62559. Это может быть тот же ошибка?

добавить банки в Tomcat это lib

после советы, данные в списке рассылки пользователя Tomcat, я добавил банки JAXB к Tomcat's CATALINA_BASE/lib каталог, но получил ту же ошибку, что и в папке lib приложения.

2 ответов


попробуйте следующее и его зависимости. Вижу Maven репозиторий для последней версии.

<dependency>
  <groupId>org.glassfish.jaxb</groupId>
  <artifactId>jaxb-runtime</artifactId>
  <version>2.3.0.1</version>
</dependency>

Он также содержит дескрипторы загрузчика служб Java. См.использование JAXB в Java 9+


сначала некоторые случайные факты:

вот что произошло на Java 8:

  • мы не передаем загрузчик классов в JAXB (oops), поэтому он использует загрузчик классов контекста потока
  • наша гипотеза заключается в том, что Tomcat явно не устанавливает загрузчик классов контекста, и поэтому он будет тем же самым, который загрузил Tomcat: системный класс погрузчик
  • это Денди, потому что загрузчик системного класса видит весь JDK и, следовательно, реализацию JAXB, включенную в него

входит Java 9-пианино перестает играть, и все ставят свой скотч:

  • добавлено JAXB как регулярная зависимость и поэтому он загружается загрузчиком классов веб-приложения
  • так же, как и на Java 8, JAXB ищет загрузчик системного класса, хотя и не может видеть загрузчик приложения (только наоборот)
  • JAXB не удается найти реализацию и идет брюхом вверх

решение состоит в том, чтобы убедиться, что JAXB использует правильный загрузчик классов. Мы знаем три способа:--5-->

  • вызов Thread.getCurrentThread().setContextClassLoader(this.getClass().getClassLoader()); но это не очень хорошая идея
  • создать контекст сельсин, но для этого требуется JAX-WS, и это похоже на замену одного зла другим
  • используйте пакет, принимающий вариант JAXBContext::newInstance, что также берет загрузчик класса и передает правильный загрузчик, хотя для этого требуется некоторый рефакторинг

Примечание: пользователей curlals предоставил критическую часть информации, но удалил их ответ. Надеюсь, это было не потому, что я попросил несколько правок. Все кредиты / карма должны идти к ним! @curlals: если вы восстановите и отредактируете свой ответ, я приму и поддержу его.