jaxb-impl.jar に同梱され、マニフェスト クラスパスに含まれるサード パーティの jar があります。問題は、独自のバージョンの JAXB を提供すると (バージョンに関係なく)、JAX-WS の SoapFaultBuilder が壊れるように見えることです。
Unofficial JAXB Guideによると、スタンドアロン バージョンとの競合を避けるために、Sun が JAXB を JDK に折りたたんだときにパッケージ名を意図的に変更したようです。ただし、JDK に同梱されている SoapFaultBuilder (私が信じている JAX-WS の一部) は、新しい内部パッケージ名に明示的に依存しています。これにより、スタンドアロンの JAXB jar を追加した場合 (JAXB のバージョン番号が同じであっても)、障害メッセージの作成時に失敗します。
ここに私の小さなテスト ケースがあります: 簡単な Web サービスを作成します。
package wstest;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;
//Service Endpoint Interface
@WebService
@SOAPBinding(style = Style.RPC)
public interface HelloWorld{
@WebMethod String getHelloWorldAsString(String name);
}
そして、単純に例外をスローする実装。(問題は SOAPFaultBuilder でのみ発生するため):
package wstest;
import javax.jws.WebService;
//Service Implementation
@WebService(endpointInterface = "wstest.HelloWorld")
public class HelloWorldImpl implements HelloWorld{
@Override
public String getHelloWorldAsString(String name) {
//return "Hello World JAX-WS " + name;
throw new RuntimeException("Exception for: " + name);
}
}
そして、Web サービスを公開するためのクラス:
package wstest;
import javax.xml.ws.Endpoint;
//Endpoint publisher
public class HelloWorldPublisher{
public static void main(String[] args) {
Endpoint.publish("http://localhost:9999/ws/hello", new HelloWorldImpl());
}
}
HelloWorldPublisher を実行し、それに対してこのクライアントを実行します。
package wstest;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
public class HelloWorldClient{
public static void main(String[] args) throws Exception {
URL url = new URL("http://localhost:9999/ws/hello?wsdl");
//1st argument service URI, refer to wsdl document above
//2nd argument is service name, refer to wsdl document above
QName qname = new QName("http://wstest/", "HelloWorldImplService");
Service service = Service.create(url, qname);
HelloWorld hello = service.getPort(HelloWorld.class);
System.out.println(hello.getHelloWorldAsString("Matt"));
}
}
これにより、Web サービスによってスローされた例外が正しく吐き出されます。ただし、jaxb-impl.jar の任意のバージョンを追加すると、クラスパスまたは承認されたライブラリのどちらにあるかに関係なく、次のスタック トレースが表示されます。
Exception in thread "main" java.lang.ExceptionInInitializerError
at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:107)
at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:78)
at com.sun.xml.internal.ws.client.sei.SEIStub.invoke(SEIStub.java:107)
at $Proxy19.getHelloWorldAsString(Unknown Source)
at wstest.HelloWorldClient.main(HelloWorldClient.java:21)
Caused by: java.lang.ClassCastException: com.sun.xml.bind.v2.runtime.JAXBContextImpl cannot be cast to com.sun.xml.internal.bind.api.JAXBRIContext
at com.sun.xml.internal.ws.fault.SOAPFaultBuilder.<clinit>(SOAPFaultBuilder.java:533)
... 5 more
例外が発生するのは、私の jaxb-impl の com.sun.xml.bind.v2.runtime.JAXBContextImpl が com.sun.xml.internal.bind.api.JAXBRIContext (パッケージ階層で欠落している「内部」サブパッケージに注意してください)。
また、非公式の JAXB ガイドによると、JAXB のバージョンを正しくオーバーライドするには、承認された lib を使用する必要があると述べています。ただし、SOAPFaultBuilder は JAXBContext.newInstance() を使用して という名前のファイルのクラスパスを検索し、ファイル/META-INF/services/javax.xml.bind.JAXBContext
で指定されたクラス名に基づいて JAXBContext を手動でロード (および反射的に作成) します。それは問題ではありません-クラスパスまたは承認されたライブラリは同じ動作をします。
回避策の 1 つは、コマンド ラインに を追加-Djavax.xml.bind.JAXBContext=com.sun.xml.internal.bind.v2.ContextFactory
することです。これにより、JAXBContext.newInstance()/META-INF/services/javax.xml.bind.JAXBContext
はクラスパス上のファイルを無視し、ビルトイン バージョンの JAXB を手動で指定します。もう 1 つの回避策は、独自の JAXB を指定せず、JDK に組み込まれているバージョンを使用することですが、非公式の JAXB ガイドによると、Sun は独自の JAXB 実装の提供を処理できるようにこのシステムを設計したようです。JAXB のバージョンを正常に提供でき、障害メッセージを正常にキャプチャできる人はいますか? (Web サービスによってエラーが発生しない限り、すべて正常に動作します)。