[~ # ~] tldr [~ # ~] : On Java 9/10, une application web dans Tomcat n'a pas accès à JAXB même si son implémentation de référence est présente sur le chemin de classe.
Edit : Non, ce n'est pas un doublon de Comment résoudre Java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException in = Java 9 - comme vous pouvez le voir dans la section Ce que j'ai essayé, j'ai déjà essayé les solutions proposées.
Nous avons une application Web qui fonctionne sur Tomcat et dépend de JAXB. Lors de notre migration vers Java 9 nous avons opté pour l'ajout de l'implémentation de référence JAXB en tant que dépendance régulière .
Tout a fonctionné lors du lancement de l'application à partir du IDE avec Tomcat intégré , mais lors de son exécution sur une véritable instance de Tomcat, j'obtiens cette erreur:
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 ...]
Remarque:
L'implémentation de JAXB-API est introuvable sur le chemin du module ou le chemin de classe.
Ce sont les fichiers pertinents dans webapps/$app/WEB-INF/lib
:
jaxb-api-2.3.0.jar
jaxb-core-2.3.0.jar
jaxb-impl-2.3.0.jar
Qu'est-ce qui se passe ici?
CLASSPATH
de TomcaIl est peut-être utile d'ajouter les fichiers JAR au chemin d'accès aux classes de Tomcat dans 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
Nan:
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]
C'est clairement la même classe, donc apparemment il a été chargé par deux chargeurs de classe. Je soupçonne le chargeur de classe système et le chargeur de classe de l'application , mais pourquoi le chargement de JAXBContext
serait-il délégué au chargeur de classe système une fois mais pas toujours? Il semble que le comportement de délégation du chargeur de classe de l'application change pendant l'exécution du programme.
Je ne veux pas vraiment ajouter Java.xml.bind, mais je l'ai quand même essayé en ajoutant ceci à catalina.sh
:
JDK_Java_OPTIONS="$JDK_Java_OPTIONS --add-modules=Java.xml.bind"
Ne fonctionne pas non plus, cependant:
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 ...]
Mis à part les différentes classes et traces de pile, cela correspond à ce qui s'est produit précédemment: la classe JAXBContextImpl
a été chargée deux fois, une fois depuis Java.xml.bind (doit avoir été le chargeur de classe système) et une autre fois (je suppose par le chargeur de l'application du JAR).
Recherche dans la base de données de bogues de Tomcat J'ai trouvé # 62559 . Serait-ce la même erreur?
lib
Après conseils donnés sur la liste de diffusion des utilisateurs de Tomcat , j'ai ajouté les JAR JAXB à Tomcat's CATALINA_BASE/lib
, mais a la même erreur que dans le dossier lib de l'application.
D'abord quelques faits aléatoires:
JAXBContext::newInstance
utilisera le chargeur de classe de contexte du thread lors de la recherche de l'implémentation JAXB - c'est le cas même si vous appelez newInstance(Class...)
(on pourrait penser à tort qu'il utilise le chargeur des instances de classe fournies)Voici donc ce qui s'est passé sur Java 8:
Java 9 entre - le piano s'arrête de jouer et tout le monde baisse son scotch:
La solution consiste à s'assurer que JAXB utilise le bon chargeur de classe. Nous connaissons trois façons:
Thread.getCurrentThread().setContextClassLoader(this.getClass().getClassLoader());
mais ce n'est pas vraiment une bonne idéeJAXBContext::newInstance
( Javadoc from Java EE 7 ) qui prend également un chargeur de classe et passe le chargeur correct, bien que cela nécessite une refactorisationNous avons utilisé la troisième option et refactorisé vers la variante acceptant les packages de JAXBContext::newInstance
. Travail subalterne, mais a résolu le problème.
L'utilisateur curlals a fourni l'information essentielle, mais a supprimé sa réponse. J'espère que ce n'est pas parce que j'ai demandé quelques modifications. Tout crédit/karma devrait leur revenir! @curlals: Si vous restaurez et modifiez votre réponse, je l'accepterai et la voterai davantage.
Essayez ce qui suit et ses dépendances. Voir un référentiel Maven pour la dernière version .
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.0.1</version>
</dependency>
Il contient également les descripteurs Java Service Loader. Voir tilisation de JAXB dans Java 9 +