web-dev-qa-db-ja.com

JAX-WS jarからのWSDLのロード

私はSOAPサービスをバグ報告など)に利用するファットクライアントを書いています。

JAX-WSは正常に動作しますが、デフォルトでは(少なくともnetbeansでは)サービスが初期化されるたびにリモートサーバーからWSDLを取得します。これはバージョン管理のサポートなどを提供するのに役立つと思いますが、それは私が望むものではありません。

wsdllocation argをwsimportに追加して、生成されたクラスがローカルリソースを指すようにしました。次のスニペットは、ApplicationService.JavaからのWSDLリソースのURLロードです。

baseUrl = net.example.ApplicationService.class.getResource(".");
url = new URL(baseUrl, "service.wsdl");

Net/example/resourcesパッケージのjar内に保存されたリソースを指す問題はないはずです。jar自体は期待どおりに構築されます。ただし、サービスはロードされません...具体的には、ApplicationService.getPort()を呼び出すとNullPointerExceptionが発生します。

これは可能ですか?それともただのガチョウの追跡?

44
Cogsy

はい、これは、WS-A関連クラスであるjavax.xml.ws.EndpointReferenceを介してクライアントを作成するときに行ったため、最も確実に可能です。 WSDLへのクラスパス参照をWS-A EndPointReferenceに追加し、JAX-WSのMetro実装はそれを正常にロードしました。 WSDLをWS-A EndPointReferenceからロードする場合でも、ファイルまたはhttp URLからロードする場合でも、JAX-WS実装では、URLを解決するのと同じWSDL解析コードを使用する必要があります。

あなたにとって最良のアプローチは、おそらく次のようなことをすることです:

URL wsdlUrl = MyClass.class.getResource(
            "/class/path/to/wsdl/yourWSDL.wsdl");

Service yourService= Service.create(
            wsdlUrl,
            ...);

...は、WSDL内のWSDLサービスのQNameを表します。ここで覚えておくべき重要なことは、WSDLが完全で有効である必要があるということです。これは、WSDLがXSDファイルまたは他のWSDLをインポートする場合、URLが正しい必要があることを意味します。インポートしたWSDLとXSDをWSDLファイルと同じJARに含めた場合、インポートに相対URLを使用し、すべてのインポートを同じJARファイルに保持する必要があります。 JAR URLハンドラーは、相対URLをクラスパスに対してではなく、JARファイル内の相対として扱います。そのため、カスタムURLハンドラーと独自のプレフィックスを実装しない限り、JAR全体で実行されるWSDLのインポートはできませんインポートのクラスパスベースの解決。 WSDLが外部リソースをインポートする場合、それは問題ありませんが、それらのリソースが移動した場合は、メンテナンスの問題にサインアップします。クラスパスからWSDLの静的コピーを使用しても、WSDL、Webサービス、およびJAX-WSの精神に反しますが、必要な場合があります。

最後に、静的なWSDLを埋め込む場合は、少なくともテストと展開の目的でサービスエンドポイントを構成可能にすることをお勧めします。 Webサービスクライアントのエンドポイントを再構成するコードは次のとおりです。

  YourClientInterface client = yourService.getPort(
            new QName("...", "..."),
            YourClientInterface.class);
  BindingProvider bp = (BindingProvider) client;
  bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
                "http://localhost:8080/yourServiceEndpoint");
48
DavidValeri

少なくとも最近のJAX-WSでは、スキーマカタログやプログラムによるwsdlの場所設定を行う必要はありません[〜#〜] if [〜#〜] WSDLをJARに入れてから設定wsimport wsdlLocationをJAR内のWSDLの相対リソースパスに。つまり、JAX-WSはJavaの組み込みClass.getResource WSDLをロードします。

Mavenを使用している場合:

  <plugin>
    <groupId>org.jvnet.jax-ws-commons</groupId>
    <artifactId>jaxws-maven-plugin</artifactId>
    <version>2.3</version>
    <executions>
      <execution>
        <goals>
          <goal>wsimport</goal>
        </goals>
        <!-- Following configuration will invoke wsimport once for each wsdl. -->
        <configuration>
            <!--- VERY IMPORTANT THAT THE PATH START WITH '/' -->
    <wsdlLocation>/com/adamgent/ws/blah.wsdl</wsdlLocation>
    <wsdlDirectory>${basedir}/src/main/resources/com/adamgent/ws</wsdlDirectory>
    <wsdlFiles><wsdlFile>blah.wsdl</wsdlFile></wsdlFiles>
       </configuration>
      </execution>
    </executions>
  </plugin>

上の例では、Mavenプロジェクトレイアウトを使用してWSDLをここに配置しますsrc/main/resources/com/adamgent/ws

WSDLが次のようにMavenのJARに含まれていることを確認します。

<build>
      <resources>
        <resource>
          <directory>src/main/resources</directory>
        </resource>
      </resources> ....

これで、wsimportが生成したコードとWSDLは自己完結型のJARになりました。サービスを使用するには、WSDLの場所を設定する必要はなく、次のように簡単です。

BlahService myService = new BlayService_Service().getBlahServicePort();

これをANTのwsimportにマッピングするのは簡単です。

14
Adam Gent

少し遅かったかもしれませんが、この問題を解決するための非常に簡単な解決策を見つけましたが、これにはServiceクラスの生成コードの変更が含まれていました。

Serviceクラスの次の行

baseUrl = net.example.ApplicationService.class.getResource(".");

に変更されます

baseUrl = net.example.ApplicationService.class.getResource("");

jARにパックされたWSDLでも正常に機能します。いずれの場合でもgetResource()の正確な想定動作についてはわかりませんが、複数のOSおよびJavaバージョン。

6
weiresr

説明するのはJAX-WSのバグです。 JAX_WS-888-カスタムwsdlLocationのURLを解決するための間違ったコード

これはV2.2で修正されたため、作成中にwsdlLocationを設定するだけで動作するはずです。

5
sleske

他の答えは

new Service(wsdllocation, servicename );

サービスオブジェクトを取得します。

これが私が問題を解決した方法です。

3
thorty

クラスパスに「。」がある場合その中で、Class.getResource( "。")は、Javaコマンドを実行したディレクトリのURLを返します。それ以外の場合は、nullを返します。それに応じてwsdllocationを調整します。

3
nagarjun

ここでクライアントjarを作成する前に、WSDLの場所を置き換えました。

  1. WSDLをクラスディレクトリにコピーします。
  2. クラスパスを使用してWSDLを参照するサービスクラスを置き換えます。
  3. クライアントスタブを構築します。
  4. スタブをジャーします。
<copy todir="@{dest-dir}/@{dir-package}" verbose="@{verbose}">
  <fileset dir="@{dir-wsdl}" includes="*.wsdl,*.xsd" />
</copy>
<echo message="Replacing Service to point to correct WSDL path..." />
<replaceregexp match="new URL(.*)" replace='Class.class.getResource("@{name-wsdl}");' flags="gm">
  <fileset dir="@{source-dest-dir}">
    <include name="@{dir-package}/*Service.Java" />
  </fileset>
</replaceregexp>
<replaceregexp match="catch (.*)" replace='catch (Exception ex) {' flags="gm">
  <fileset dir="@{source-dest-dir}">
    <include name="@{dir-package}/*Service.Java" />
  </fileset>
</replaceregexp>
2
kiri

私は同じ問題につまずいた。 JAXWS生成クライアントコードはMyService.class.getResource(".")トリックを使用してwsdlファイルをロードしますが、テスト後、クラスファイルがfilesytemのディレクトリにある場合にのみ機能するようです。クラスファイルがJARにある場合、この呼び出しはURLに対してnullを返します。

次のようにURLを作成すると、JDKのバグのように聞こえます。

final URL url = new URL( MyService.class.getResource( MyService.class.getSimpleName() + ".class"), "myservice.wsdl");

クラスとwsdlがjarにバンドルされている場合にも機能します。

私はほとんどの人が実際に瓶にバンドルすると思います!

2
David Nouls

これが私のハックな回避策です。

JarからWSDLを解凍し、jarの近くのファイルに書き込みます。

File wsdl = new File("../lib/service.wsdl");
InputStream source = getClass().getResource("resources/service.wsdl").openStream();
FileOutputStream out = new FileOutputStream(wsdl);

byte[] buffer = new byte[512];
int read;
while((read = source.read(buffer)) >= 0) {
    out.write(buffer, 0, read);
}

次に、サービスクラスがfile:../lib/service.wsdlを指すようにします。

これは機能しますが、誰かがもっとエレガントなソリューションを見せてくれれば幸いです。

1
Cogsy

ここに私のために働くものがあります(特に、httpおよびhttpsを介して)。 Apache CXF 3.1.1で作成されたクラスで動作するOracle JDK 1.8.0_51のJAX-WSの場合。

リモートWSDLは、どの場合でも最初の呼び出しでのみ取得されることに注意してください。使用パターン(長時間実行されるプログラム)によっては、これは完全に受け入れられる場合があります。

基礎:

  • リモートホストからWSDLをダウンロードし、ファイルとして保存します:wget --output-document=wsdl_raw.xml $WSDL_URL
  • 素敵な書式設定のためにxmllint --format wsdl_raw.xml > wsdl.xmlが必要な場合があります
  • コマンドラインツール./cxf/bin/wsdl2Java -d output/ -client -validate wsdl.xmlを使用してクライアントクラスを生成し、プロジェクトにインポートします

httphttpsの両方のサービス定義がWSDLファイルに存在することを確認します。私の場合、プロバイダーにはhttpsに対するものはありませんでしたが(httpsトラフィックを受け入れました)、手動で追加する必要がありました。これらの行に沿った何かがWSDLにあるはずです:

  <wsdl:service name="fooservice">
    <wsdl:port binding="tns:fooserviceSoapBinding" name="FooBarWebServicePort">
      <soap:address location="http://ws.example.com/a/b/FooBarWebService"/>
    </wsdl:port>
  </wsdl:service>
  <wsdl:service name="fooservice-secured">
    <wsdl:port binding="tns:fooserviceSoapBinding" name="FooBarWebServicePort">
      <soap:address location="https://ws.example.com/a/b/FooBarWebService"/>
    </wsdl:port>
  </wsdl:service>

CXFは、適切なコンストラクターを使用して、たとえばFooserviceと呼ばれるjavax.xml.ws.Serviceを実装するクラスを生成する必要があります。

public class Fooservice extends Service {

  public Fooservice(URL wsdlLocation) {
      super(wsdlLocation, SERVICE);
  }

  public Fooservice(URL wsdlLocation, QName serviceName) {
      super(wsdlLocation, serviceName);
  }

  public Fooservice() {
      super(WSDL_LOCATION, SERVICE);
  }

  ...etc...

コードのどこか(ここでは読みやすいようにいくつかのGroovy)で、上記のServiceインスタンスを初期化し、ポートを呼び出します。ここでは、secureというフラグに応じて、httpsまたはhttpを使用します。

static final String NAMESPACE = 'com.example.ws.a.b'
static final QName SERVICE_NAME_HTTP = new QName(NAMESPACE, 'fooservice')
static final QName SERVICE_NAME_HTTPS = new QName(NAMESPACE, 'fooservice-secured')

Fooservice wsService
File wsdlfile = new File('/somewhere/on/disk/wsdl.xml')

// If the file is missing there will be an exception at connect
// time from Sun.net.www.protocol.file.FileURLConnection.connect
// It should be possible to denote a resource on the classpath 
// instead of a file-on-disk. Not sure how, maybe by adding a handler
// for a 'resource:' URL scheme?

URI wsdlLocationUri = Java.nio.file.Paths(wsdlfile.getCanonicalPath()).toUri()

if (secure) {
  wsService = new Fooservice(wsdlLocationUri.toURL(), SERVICE_NAME_HTTPS)
}
else {
  wsService = new Fooservice(wsdlLocationUri.toURL(), SERVICE_NAME_HTTP)
}

SomeServicePort port = wsService.getSomeServicePort()

port.doStuff()

サービスの呼び出しに使用される接続とは別の接続でWSDLをダウンロードする別の方法(トラフィックを監視するにはtcpdump -n -nn -s0 -A -i eth0 'tcp port 80'を使用)は、次のようにします。

URI wsdlLocationUri

if (secure) {
   wsdlLocationUri = new URI('https://ws.example.com/a/b/FooBarWebService?wsdl')
}
else {
   wsdlLocationUri = new URI('http://ws.example.com/a/b/FooBarWebService?wsdl')
}

Fooservice wsService = new Fooservice(wsdlLocationUri.toURL(), SERVICE_NAME_HTTP)

SomeServicePort port = wsService.getSomeServicePort()

port.doStuff()

wsdlLocationUriSERVICE_NAME_HTTPで初期化されているという事実にもかかわらず、wsServicehttpsを指定する場合、これは実際にhttpsを適切に使用することに注意してください。 (理由はわかりません-サービスは、WSDLリソースの取得に使用されるスキームを使用しますか?)

そしてそれはそれについてです。

接続をデバッグするには、次を渡します。

-Dcom.Sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump=true
-Dcom.Sun.xml.internal.ws.transport.http.HttpAdapter.dump=true

コマンドラインでJVMに。これにより、http接続コードからstdoutに情報が書き込まれます(残念ながら、Java.util.loggingではありません。Oracle、お願いします!)。

1
David Tonhofer

私の解決策は、生成されたサービスを変更することでした。ヘッダーアノテーションのwsdlLocationを変更する必要があり、インスタンス化ブロックは次のようになります。

    static {
    URL url = null;
    url = com.ups.wsdl.xoltws.ship.v1.ShipService.class.getResource("Ship.wsdl");
    SHIPSERVICE_WSDL_LOCATION = url;
    }

ShipServiceクラスの隣のbinディレクトリにwsdlファイルを配置します

0
IcedDante

複雑にする必要はありません。jarクラスローダーを使用するだけです

ClassLoader cl = SomeServiceImplService.class.getClassLoader();
SERVICE_WSDL_LOCATION = cl.getResource("META-INF/wsdls/service.wsdl");

それを試してみてください!

0
lunicon

いくつかの操作で動作するようにすることはできますが、notを実行し、現在の方法を維持することをお勧めします。

Webサービスエンドポイントプロバイダーは、契約の一部としてWSDLを提供する必要があります。生成するコードは、サーバー自体からWSDLからプルする必要があります。

WebSphereでの展開時に、展開UIからエンドポイントを他のエンドポイントに変更できます。他のアプリケーションサーバーでは、ベンダー固有のバインディングXMLを見つけてそれを実行する必要があります。

初期化時にのみ発生するため、アプリケーション全体への影響はごくわずかです。

0