代码之家  ›  专栏  ›  技术社区  ›  Nicolai Parlog

JAXB在Tomcat 9和java9/10上不可用

  •  15
  • Nicolai Parlog  · 技术社区  · 6 年前

    TLDR公司 :在Java9/10上,Tomcat中的一个web应用程序无法访问JAXB,即使它的引用实现出现在类路径上。

    编辑 :不,这不是 How to resolve java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException in Java 9 -你可以从 我试过的 我已经试过建议的解决方案了。

    形势

    我们有一个运行在Tomcat上并依赖JAXB的web应用程序。在迁移到Java9的过程中,我们选择添加 the JAXB reference implementation as a regular dependency .

    从IDE启动应用程序时一切正常 with embedded 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

    也许将jar添加到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]
    

    显然是同一个类,所以很明显它是由两个类装入器装入的。我怀疑 the system class loader and the app's class loader ,但为什么要加载 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的应用程序加载器)。

    搜索Bug

    Searching Tomcat's bug database 我发现 #62559 . 可能是同样的错误吗?

    将JAR添加到Tomcat的 lib

    跟随 advice given on the Tomcat user mailing list ,我将JAXB JARs添加到Tomcat的 CATALINA_BASE/lib 目录,但与应用程序的lib文件夹中的错误相同。

    2 回复  |  直到 6 年前
        1
  •  9
  •   Nicolai Parlog    6 年前

    分析

    首先是一些随机事实:

    下面是在Java8上发生的事情:

    • 我们不会将类加载器传递给JAXB(oops),因此它使用线程的上下文类加载器
    • 我们的推测是,Tomcat没有显式地设置上下文类加载器,因此它最终将成为加载Tomcat的同一个:系统类加载器
    • 这很好,因为系统类加载器可以看到整个JDK,以及其中包含的JAXB实现

    爪哇9进入-钢琴停止演奏,每个人放下他们的苏格兰威士忌:

    • 我们增加了 JAXB as a regular dependency 所以它是由web应用的类加载器加载的
    • 就像在Java 8上一样,JAXB搜索系统类加载器,但是那个加载器看不到应用程序的加载器(只有相反的方式)
    • JAXB找不到实现并崩溃了

    解决方案

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

    • 呼叫 Thread.getCurrentThread().setContextClassLoader(this.getClass().getClassLoader()); 但那不是个好主意
    • 创造 a context resolver ,但这需要JAX-WS,感觉就像用另一个邪恶代替了一个邪恶
    • 使用包接受变体 JAXBContext::newInstance ( Javadoc from Java EE 7 )它还接受一个类加载器并传递正确的加载器,尽管这需要一些重构

    我们使用了第三个选项,并对 JAXBContext::newInstance . 卑微的工作,但解决了问题。

    注意

    User curlals 提供了关键信息,但删除了他们的答案。我希望不是因为我要求做一些编辑。所有的功劳/业力都应该属于他们!@卷发:如果你恢复和编辑你的答案,我会接受和投票。

        2
  •  6
  •   Basil Bourque    6 年前

    尝试以下及其依赖项。见a Maven repository for latest version .

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

    它还包含Java服务加载程序描述符。见 Using JAXB in Java 9+