C Sharpens you up

http://qiita.com/yuba に移しつつあります

JAX-WSでも少しはまともなWebサービスクライアントを作る(実行時にWSDLを要求させない)

注意 追記 2014/03/17
この記事で紹介した問題回避方法は処理系依存かもしれません。Windows版JDK7(Oracle)のクライアントモードJVMでは意図通りに動作したもののLinux版JDK7(Oracle)のサーバモードJVMで依然として問題動作(WSDLを実行時に参照してしまう)が見られました。詳しい検証ができていませんが、この技法を利用する場合開発環境・動作環境両方でのチェックをお願いします。
JavaWebサービスのクライアント作ろうと思うと、フレームワークとしてはAxis2JAX-WSを選択することになると思います。といってもJDKに標準でJAX-WSがついてる以上わざわざ外部ライブラリ増やしてまでAxis2というのも今さら考えにくいです(Axis2は自動生成されるコードがコンパイル時警告の出まくるものなのもイヤな点)。

さてそのJAX-WS控えめに言ってもとても残念な子です

個人的には、プロキシコードを静的に生成する点やプロキシコードが中途半端にWSDLを参照している点が、そもそも仕様としてイケテナイと思っています。(プロキシコードを生成するくらいなら、WSDLを参照しないで欲しいですね。)

JAX-WS仕様は、イケテナイ | OPENSQUARE.jp - BLOG

WSDLとはWebサービスのインターフェース仕様を記述したXML文書です。クライアント側のプロキシコードを自動生成するのにWSDLが必要なのは当たり前です。しかしJAX-WSの生成したプロキシコードは、実行時にもう一度WSDLを読みに行くんです。

なぜ実行時にWSDLなんか必要なのか。WSDLにはインターフェース仕様のほかに、アクセス先URLも書いてあるからです。それだけ。本当にそれだけ。だったらアクセス先URLを直接指定させてくれればいいことじゃない!?

繰り返します。JAX-WSは控えめに言っても本当に本当に残念な子なんです

アクセス先URLだけのためにWSDLをいちいち読みに行かれるんじゃオーバーヘッドだってソケット数リソース消費だって重いし、そもそもWSDLが必ず読めるとは限りませんよ? IEEE1888みたいに規格としてWebサービスインターフェースが決まっているサービスの場合、アクセスURLだけわかっててWSDLのURLはわからないなんてことざらです*1

目的のアクセス先URLを埋め込んだテンポラリWSDLを生成して file:///... 形式のURLで読ませちゃいなよって? 断る。なんでこんなことのためにI/O負荷増やさなきゃならんの…

はい、解決法。

まず、プロキシファクトリを作るときに、WSDLのURLとして無慈悲にnullを食わせます。

EchoService echoService = new EchoService(null); // WSDL locationは… null…っ!
EchoServicePortType proxyObj = echoService.getEchoServiceSOAP11PortHttp();

このままではプロキシオブジェクトは(WSDLが読めなかったせいで)アクセス先URLを知りません。
強引に教え込みましょう。

((BindingProvider) proxyObj).put(
    BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
    "http://アクセス先URL");

これでWSDLを読み込むことなく望み通りのURLにWebサービスアクセスしてくれます。

*1:IEEE1888の場合は特にそうで、レジストリがアクセスURLしか案内してくれません。