この記事に書いてあること
JAXBを利用するSOAPアプリが、テストでは動くのにTomcatへ置いたWARで動かなかった記録です。JDK 9ではJAXB・JAX-WS関連モジュールの既定の解決範囲が変わり、JDK 11ではこれらのモジュールが削除されました。
発生した問題
当時はTomcat 9で javax.xml.bind 系のアプリを動かしていました。テストの依存関係にJAXBがあっても、デプロイ先に必要なAPIや実装があるとは限りません。WAR内の WEB-INF/lib とTomcatの共有 lib を確認します。
解決した方法
当時は関連JARをWARから外し、共有lib側へ揃えることで動きました。共有libの変更は同じTomcat上の他アプリにも関わります。アプリが依存を持つ構成では、互換性のあるAPIと実装をWAR内に揃え、同じ依存を共有libへ重複配置しない構成をまず検討します。
次の診断例は、クラスの読み込み元とJAXB実装が選ばれるところまでを確認します。JAXBが動くことと、SOAPクライアントやWAR全体が動くことは別に確認します。
public class JaxbProbe {
public static void main(String[] args) {
inspect("javax.xml.bind.JAXBContext");
inspect("jakarta.xml.bind.JAXBContext");
}
private static void inspect(String name) {
try {
Class<?> type = Class.forName(name, false, JaxbProbe.class.getClassLoader());
System.out.println(name + " -> " + type.getProtectionDomain()
.getCodeSource().getLocation());
Object context = type.getMethod("newInstance", Class[].class)
.invoke(null, (Object) new Class<?>[]{String.class});
System.out.println("provider -> " + context.getClass().getName());
} catch (ClassNotFoundException e) {
System.out.println(name + " -> missing");
} catch (ReflectiveOperationException e) {
throw new IllegalStateException("API found, but context could not be created", e);
}
}
}<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>example</groupId>
<artifactId>jaxb-classloading-probe</artifactId>
<version>1.0</version>
<properties>
<maven.compiler.release>11</maven.compiler.release>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.14.1</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.6.3</version>
<configuration>
<mainClass>JaxbProbe</mainClass>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>javax</id>
<dependencies>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.6</version>
</dependency>
</dependencies>
</profile>
<profile>
<id>jakarta</id>
<dependencies>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>4.0.4</version>
</dependency>
</dependencies>
</profile>
</profiles>
</project>mvn compile exec:java
mvn -Pjavax compile exec:java
mvn -Pjakarta compile exec:javaJDK 17で依存を追加しない場合、どちらのJAXBContextも見つかりませんでした。javaxプロファイルでは com.sun.xml.bind.v2.runtime.JAXBContextImpl、jakartaプロファイルでは org.glassfish.jaxb.runtime.v2.runtime.JAXBContextImpl が選ばれました。1回の起動でプロファイルは片方だけ選びます。
追加情報
providedなし、war内にライブラリを内包した場合のエラー
WARに依存JARを入れても起動しない場合は、例外チェーンとWARの内容を確認します。javax.xml.bind 用のアプリへ jakarta.xml.bind 名前空間のJARを入れても、要求されたクラスは提供されません。Tomcat 10以降へ移行する場合は、Servlet側の名前空間の変更も別途確認します。
providedなし、war内とTomcatのlibフォルダ両方に配置
当時は、例外にWAR内とTomcat共有libの両方のJAXBContext.classが現れ、キャストに失敗していました。読み込んだJARとクラスローダーを確認し、APIと実装を同じ配置方針に揃えます。
