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+
сначала некоторые случайные факты:
- , если не учитывая загрузчик класса,
JAXBContext::newInstance
использовать загрузчик классов контекста потока при поиске реализации JAXB-это так, даже если вы вызываетеnewInstance(Class...)
(можно ошибочно подумать, что он использует загрузчик предоставленных экземпляров класса) - Tomcat строит небольшая иерархия загрузчика классов для отдельных веб-приложений друг от друга
- , не полагаясь на модуль java.XML.bind, в Java 9 классы JAXB не загружаются загрузчиком bootstrap или system class
вот что произошло на 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: если вы восстановите и отредактируете свой ответ, я приму и поддержу его.