web-dev-qa-db-ja.com

Java 1.6でJAX-WSに異なるバージョンのJAXBを提供する

Jaxb-impl.jarとともに出荷され、マニフェストクラスパスに含まれるサードパーティのjarファイルがあります。問題は、JAXBの独自のバージョン(どのバージョンであるかに関係なく)を提供すると、JAX-WSのSoapFaultBuilderが破損するように見えることです。

非公式JAXBガイド によると、スタンドアロンバージョンとの競合を避けるために、SunがJAXBをJDKにフォールドする際に意図的にパッケージ名を変更したようです。ただし、JDKに同梱されているSoapFaultBuilder(JAX-WSの一部)は、新しい内部パッケージ名に明示的に依存しています。これにより、JAXBの同じバージョンnumberであっても、スタンドアロンJAXB jarを追加した場合、障害メッセージの作成時に失敗します)。

ここに私の小さなテストケースがあります。私は簡単な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の組み込みバージョンを手動で指定します。他の回避策は、単純に独自のJAXBを指定せず、JDKに組み込まれているバージョンを使用することですが、Sunが独自のJAXB実装を提供できるようにこのシステムを設計したのは、非公式JAXBガイドにあるようです。誰かがJAXBのバージョンを正常に提供でき、それでも障害メッセージを正常にキャプチャできましたか? (Webサービスによって障害が生成されない限り、すべてが正常に動作します)。

31
matt forsythe

私はこの問題で立ち往生しましたが、これにリストされている「回避策」を使用することができました フォーラム 次のようなシステムプロパティを設定することによるQ&A:

System.setProperty("javax.xml.bind.JAXBContext", 
                   "com.Sun.xml.internal.bind.v2.ContextFactory"); 
40
Frank

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

別のバージョンを使用するには、対応するバージョンの jaxb-api.jar および jaxws-api.jar を承認済みのライブラリ(例:%Java_HOME%\lib\endorsed)。

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

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

また、jaxwsの互換バージョンを含めずに新しいバージョンのjaxbを使用しようとすると、トラブルが発生する可能性があることに注意してください。これは、古いjaxwsが古いjaxbを参照しようとするためです。そのため、1つを変更する場合は、必ず両方を実行してください。 (パッケージcom.Sun.xml.internal.wsのスタックトレースは、古いjax-ws実装に関係しています。Javaの最新リリースでも、古いバージョン1のjaxbおよびjaxws apiに同梱されています)。

4
gb96

システムプロパティを変更しない別の可能な解決策は、API docuで説明されています

https://docs.Oracle.com/cd/E17802_01/webservices/webservices/docs/1.6/api/javax/xml/bind/JAXBContext.html

モデルクラスのパッケージ内にjaxb.propertiesファイルを配置できます。

javax.xml.bind.context.factory=com.Sun.xml.internal.bind.v2.ContextFactory
3
Thomas Bergmann