TL;DR: на Java 9/10 веб-приложение в Tomcat не имеет доступа к JAXB, хотя его ссылочная реализация присутствует на пути к классу.
Edit: Нет, это не дубликат. Как разрешить java.lang.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
Что здесь происходит?
Что я пробовал
Добавление JAR в Tomca CLASSPATH
Может быть, это помогает добавить setenv.sh
в путь класса 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$0(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 (должен был быть загрузчиком системного класса) и еще один раз (я предполагаю, что загрузчик приложений из JAR).
Поиск ошибок
Поиск базы данных ошибок Tomcat я нашел # 62559. Может быть, такая же ошибка?
Добавление JAR в Tomcat lib
Следуя советам, приведенным в списке рассылки пользователей Tomcat, я добавил JAXB JAR в CATALINA_BASE/lib
Tomcat CATALINA_BASE/lib
, но получил ту же ошибку, что и в папке приложения lib.