32

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 サービスによってエラーが発生しない限り、すべて正常に動作します)。

4

3 に答える 3

4

この問題の核心は、JAXB APIに変更があり、使用しようとしているランタイム実装が JDK にバンドルされている JAXB API のバージョンと一致しないことです。

別のバージョンを使用するには、対応するバージョンのjaxb-api.jarjaxws-api.jarを承認済みの lib (例: %JAVA_HOME%\lib\endorsed) にコピーする必要があります。

オプションの完全なリストは、非公式 JAXB ガイドのセクション 7.1.2 に記載されています。

実装 jar (jaxb-impl.jar など) を承認済みの lib にコピーするのは間違いです。これらは単純にクラスパスにある必要があります。

また、互換性のあるバージョンの jaxws を含めずに新しいバージョンの jaxb を使用しようとすると、問題が発生する可能性があることに注意してください。これは、古い jaxws が古い jaxb を参照しようとするためです。一方を変更する場合は、必ず両方を行ってください。(パッケージ内のスタック トレースはcom.sun.xml.internal.ws、古い jax-ws 実装に関連しています。Java の最新リリースでさえ、古いバージョン 1 jaxb および jaxws API を使用して出荷されています)。

于 2013-04-04T06:16:29.600 に答える