JAXB在Tomcat 9和Java 9/10上不可用 [英] JAXB not available on Tomcat 9 and Java 9/10

查看:1573
本文介绍了JAXB在Tomcat 9和Java 9/10上不可用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

TLDR :在Java 9/10上,Tomcat中的Web应用程序无法访问JAXB,即使它的引用实现存在于类路径中。

TLDR: On Java 9/10, a web app in Tomcat has no access to JAXB even though its reference implementation is present on the class path.

修改:不,这不是如何解决java.lang.NoClassDefFoundError的副本:Java 9中的javax / xml / bind / JAXBException - 正如您可以通过我尝试的内容部分所述,我已经尝试了所提出的解决方案。

Edit: No, this is not a duplicate of How to resolve java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException in Java 9 - as you can tell by the What I tried section, I already tried the proposed solutions.

我们有一个在Tomcat上运行并依赖于JAXB的Web应用程序。在迁移到Java 9期间,我们选择将 JAXB参考实现添加为常规依赖项

We have a web app that runs on Tomcat and depends on JAXB. During our migration to Java 9 we opted for adding the JAXB reference implementation as a regular dependency.

从IDE启动应用程序时一切正常嵌入式Tomcat ,但在真正的Tomcat实例上运行时,我收到此错误:

Everything worked when launching the app from the IDE with embedded Tomcat, but when running it on a real Tomcat instance, I get this error:

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的实现。

Implementation of JAXB-API has not been found on module path or classpath.

这些是 webapps / $ app / WEB-INF / lib

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

这里发生了什么?

setenv.sh 中将JAR添加到Tomcat的类路径可能会有帮助吗?

Maybe it helps to add the JARs to Tomcat's class path in 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

Nope:

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 加载到系统类加载器一次但不总是?在程序运行时,几乎看起来app的类加载器的委托行为发生了变化。

That's clearly the same class, so apparently it has been loaded by two class loaders. I suspect the system class loader and the app's class loader, but why would loading JAXBContext be delegated to the system class loader once but not always? It almost looks as if the delegation behavior of the app's class loader changes while the program runs.

我真的不想添加 java.xml.bind ,但我还是尝试将它添加到 catalina.sh

I don't really want to add java.xml.bind, but I tried it anyways by adding this to 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的应用程序加载器)。

Apart from the different class and stack trace, this is in line with what happened earlier: The class JAXBContextImpl was loaded twice, once from java.xml.bind (must have been the system class loader) and one other time (I assume by the app's loader from the JAR).

搜索Tomcat的bug数据库我找到了 #62559 。可能是同样的错误吗?

Searching Tomcat's bug database I found #62559. Could that be the same error?

关注,我将JAXB JAR添加到Tomcat的 CATALINA_BASE / lib 目录,但得到的错误与在应用程序的lib文件夹中。

Following advice given on the Tomcat user mailing list, I added the JAXB JARs to Tomcat's CATALINA_BASE/lib directory, but got the same error as in the application's lib folder.

推荐答案

分析



首先是一些随机的事实:

Analysis

First some random facts:

  • if not given a class loader, JAXBContext::newInstance will use the thread's context class loader when looking for the JAXB implementation - this is the case even if you call newInstance(Class...) (one might mistakenly think it uses the provided class instances' loader)
  • Tomcat builds a small class loader hierarchy to separate web applications from one another
  • by not relying on the module java.xml.bind, in Java 9, JAXB classes are not loaded by the bootstrap or system class loader

所以这是Java 8上发生的事情:

So here's what happened on Java 8:


  • 我们没有将类加载器传递给JAXB(oops),因此它使用线程的上下文类加载器

  • 我们的猜想是Tomcat没有显式设置上下文类加载器,所以它最终将是加载Tomcat的同一个:系统类加载器

  • 这是花花公子,因为系统类加载器看到整个JDK,因此其中包含JAXB实现< /升i>
  • we don't pass a class loader to JAXB (oops), so it uses the thread's context class loader
  • our conjecture is that Tomcat does not explicitly set the context class loader and so it will end up being the same one that loaded Tomcat: the system class loader
  • that's dandy because the system class loader sees the entire JDK and hence the JAXB implementation included therein

Java 9进入 - 钢琴停止播放,每个人放下苏格兰威士忌:

Java 9 enters - the piano stops playing and everybody puts down their scotch:


  • 我们添加了 JAXB作为常规依赖关系,因此它由Web应用程序的类加载器加载

  • 就像在Java 8上一样,JAXB搜索系统类加载器,然而那个人看不到应用程序的加载器(只是反过来)

  • JAXB无法找到实施并且无法实现

  • we added JAXB as a regular dependency and so it is loaded by the web app's class loader
  • just as on Java 8, JAXB searches the system class loader, though, and that one can't see the app's loader (only the other way around)
  • JAXB fails to find the implementation and goes belly up

解决方案是确保JAXB使用正确的类加载器。我们知道三种方式:

The solution is to make sure JAXB uses the right class loader. We know of three ways:


  • 调用 Thread.getCurrentThread()。setContextClassLoader(this.getClass()。getClassLoader ()); 但这不是一个好主意

  • 创建一个上下文解析器,但这需要JAX-WS,这就像用另一个邪恶替换一个邪恶

  • 使用 JAXBContext :: newInstance 的包接受变体,它也接受类加载器并传递正确的加载器,尽管这需要一些重构

  • call Thread.getCurrentThread().setContextClassLoader(this.getClass().getClassLoader()); but that's not really a good idea
  • create a context resolver, but that requires JAX-WS and that feels like replacing one evil with another
  • use the package-accepting variant of JAXBContext::newInstance that also takes a class loader and pass the correct loader, although that requires some refactoring

我们使用第三个选项并重构为 JAXBContext :: newInstance 的包接受变体。但是修复了问题。

We used the third option and refactored towards the package-accepting variant of JAXBContext::newInstance. Menial work, but fixed the problem.

用户 curlals 提供了关键信息,但删除了他们的答案。我希望不是因为我要求进行一些编辑。所有的信用/业力应该归他们所有! @curlals:如果你恢复并编辑你的答案,我会接受并赞成它。

User curlals provided the critical piece of information, but deleted their answer. I hope it was not because I asked for a few edits. All credit/karma should go to them! @curlals: If you restore and edit your answer, I will accept and upvote it.

这篇关于JAXB在Tomcat 9和Java 9/10上不可用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆