3.6.1 スタブベースの実装例
cjwsimportコマンドで自動生成したサービスクラスやスタブを使用して,Webサービスクライアントを開発できます。スタブベースのWebサービスクライアントの実装で,サービスクラスの生成やポートを取得するには,次の方法があります。
-
サービスクラスのコンストラクタの利用
サービスクラスのコンストラクタを利用して,サービスクラスのインスタンスを生成します。生成したサービスクラスのインスタンスから,ポートを取得します。
-
javax.xml.ws.WebServiceRefアノテーションの利用
javax.xml.ws.WebServiceRefアノテーションを利用して,サービスクラスやポートをインジェクトします。
サービスクラスやポートのインジェクションについては,「10.21.1 サービスクラスおよびポートのインジェクション」を,javax.xml.ws.WebServiceRefアノテーションについては,「19.3 アノテーションのサポート範囲」を参照してください。
- 〈この項の構成〉
(1) 参照されるWSDL
スタブベースのWebサービスクライアントを実行する場合,WSDLのパスまたはURLが必要になります。このとき,次の条件の組み合わせによって,参照されるWSDLが異なります。
-
javax.xml.ws.WebServiceRefアノテーションを利用してインジェクションする場合
インジェクションする場合に参照されるWSDLについては,「19.3.1(2) wsdlLocation要素(javax.xml.ws.WebServiceRef)」を参照してください。
-
サービスクラスのコンストラクタを利用してインスタンスを生成する場合
参照されるWSDLは次に示す条件の組み合わせによって異なります。
-
利用するコンストラクタの種類
-
cjwsimportコマンドに-wsdllocationオプションを指定して実行したかどうか
-
条件ごとの参照されるWSDLの対応を次の表に示します。なお,それぞれの条件での例については,「3.6.1(5)(a) サービスクラスをインスタンス化する」を参照してください。
項番 |
利用するコンストラクタ |
-wsdllocation オプション |
参照されるWSDL |
---|---|---|---|
1 |
デフォルトコンストラクタ |
× |
cjwsimportコマンドの引数に指定したWSDL※1 |
2 |
デフォルトコンストラクタ |
○ |
-wsdllocationオプションに指定したWSDL※2 |
3 |
java.net.URL オブジェクトおよびjavax.xml.namespace.QNameオブジェクトをパラメタに持つコンストラクタ |
− |
パラメタのURLに指定したWSDL※2 |
(2) 参照されるエンドポイントアドレス
接続するWebサービスのURL(エンドポイントアドレス)は,デフォルトではWSDLのポート(wsdl:port要素)が持つアドレス情報(soap:address子要素のlocation属性)が利用されます。ただし,メッセージコンテキストのjavax.xml.ws.service.endpoint.addressプロパティを利用すると,エンドポイントアドレスを動的に変更できます。エンドポイントアドレスを動的に変更する例については,「3.6.1(5)(c) ポートのメソッドを呼び出す」を参照してください。
(3) サービスクラスの生成およびポートの取得
サービスクラスの生成やポートの取得には処理コストが掛かるので,次のように,ポートをインジェクションまたは再利用することをお推めします。
-
WebサービスクライアントをサーブレットやEJBとして実装する場合
ポートをインジェクションします。詳細については,「10.21 インジェクション」を参照してください。
-
ほかのアプリケーション(コマンドラインアプリケーションなど)でWebサービスクライアントを実装する場合
初期化処理でサービスクラスの生成やポートの取得を実施し再利用します。ポートを取得するたびにサービスクラスを生成したり,ポートのメソッドを呼び出すたびにポートを取得したりする必要はありません。
(4) サービスの選択
呼び出すWebサービスのWSDLに複数のサービス(wsdl:service要素)が含まれる場合,サービスクラスを生成するときに,java.net.URLオブジェクトとjavax.xml.namespace.QNameオブジェクトをパラメタに持つコンストラクタを使用し,javax.xml.namespace.QNameオブジェクトでそのサービス(wsdl:service要素)を呼び出すのか明示的に指定してください。
(5) 基本的な実装例
Webサービスクライアントの実装には,コンストラクタを利用してサービスクラスを生成する方法と,インジェクションを利用する方法があります。ここでは,コンストラクタを利用してサービスクラスを生成する実装の手順を説明します。インジェクションについては,「10.21.1 サービスクラスおよびポートのインジェクション」を参照してください。
スタブベースのWebサービスクライアントを開発する場合,cjwsimportコマンドを使用してWebサービスを呼び出すために必要なJavaソースを生成します。cjwsimportコマンドは,-generateServiceオプションを指定しないで実行してください。
スタブベースのWebサービスクライアントは,次のJavaソースを使用してWebサービスを呼び出します。
-
サービスクラス
サービスクラスは,JAX-WS 2.2仕様で規定されたWebサービスを呼び出すためのWSDLのサービス(wsdl:service要素)に対応するクラスです。wsdl:service要素がwsdl:port要素をまとめるように,複数のポートをまとめています。
Webサービスクライアントを実装する場合は,はじめにサービスクラスのインスタンスを生成します。
-
ポート
WSDLのポート(wsdl:port要素)に対応するインスタンスで,インタフェースはサービスエンドポイントインタフェース(SEI)です。リモートにある接続先Webサービスのプロキシのように動作するため,Webサービスクライアントがこのポートのメソッドを呼び出すことで,Webサービスのオペレーションを透過的に呼び出せます。
Webサービスクライアントの実装でWebサービスのオペレーションを呼び出す手順は,次のとおりです。
-
サービスクラスをインスタンス化する
-
ポートを取得する
-
ポートのメソッドを呼び出す
ここでは,「5.1 開発例の構成(SEI起点)」に構成を示すWebサービス(足し算をするWebサービス)を呼び出すWebサービスクライアントの実装例を示します。
Webサービスクライアントの実装に使用するクラスおよびメソッドを次の表に示します。必要に応じて,「5.5.1 サービスクラスを生成する」に記載されたサービスクラスの生成物の内容を参照してください。
項番 |
種別 |
クラスおよびメソッド |
---|---|---|
1 |
サービスクラス |
AddNumbersImplService |
2 |
SEI |
AddNumbersImpl |
3 |
SEIのメソッド |
int add(int, int) |
4 |
クライアントのメインクラス |
TestClient |
(a) サービスクラスをインスタンス化する
デフォルトのコンストラクタを使用してサービスクラスのオブジェクトを生成する例を次に示します。
// サービスクラスをインスタンス化する AddNumbersImplService service = new AddNumbersImplService();
この場合,cjwsimportコマンドの引数または-wsdllocationオプションに指定したWSDLが参照されます。
cjwsimportコマンドの実行時に-wsdllocationオプションを指定しなかった場合の三つの例を次に示します。
- 実行例1
> "%COSMINEXUS_HOME%\jaxws\bin\cjwsimport.bat" D:\dev\development.wsdl
D:\dev\development.wsdlは,Webサービスクライアント実行時にも存在し,参照できる状態である必要があります。
- 実行例2
> D: > cd D:\dev\ > "%COSMINEXUS_HOME%\jaxws\bin\cjwsimport.bat" relative\development.wsdl
D:\dev\relative\development.wsdlは,Webサービスクライアント実行時にも存在し,参照できる状態である必要があります。
- 実行例3
> "%COSMINEXUS_HOME%\jaxws\bin\cjwsimport.bat" http://sample.com/fromjava/AddNumbersImplService?wsdl
http://sample.com/fromjava/AddNumbersImplService?wsdlは,Webサービスクライアント実行時にも存在し,参照できる状態である必要があります。
cjwsimportコマンドの実行時に-wsdllocationオプションを指定した場合の二つの例を次に示します。
- 実行例1
> "%COSMINEXUS_HOME%\jaxws\bin\cjwsimport.bat" -wsdllocation file:/home/wsdl4runtime/master.wsdl D:\dev\development.wsdl
file:/home/wsdl4runtime/master.wsdlは,Webサービスクライアント実行時にも存在し,参照できる状態である必要があります。D:\dev\development.wsdlは,Webサービスクライアント開発時に実装に必要なJavaコードの生成に使用されるだけです。そのため,D:\dev\development.wsdlは実行時には存在しない,または参照できない状態でも問題ありません。
- 実行例2
> "%COSMINEXUS_HOME%\jaxws\bin\cjwsimport.bat" -wsdllocation ./wsdl4runtime/master.wsdl D:\dev\development.wsdl
Webサービスクライアント実行時のカレントディレクトリを<実行時カレントディレクトリ>とした場合,<実行時カレントディレクトリ>/wsdl4runtime/master.wsdlが存在し,参照できる状態である必要があります。
なお,デフォルトのコンストラクタではなく,java.net.URLオブジェクトおよびjavax.xml.namespace.QNameオブジェクトをパラメタに持つコンストラクタを使用する場合は,次の例のようになります。
// サービスクラスをインスタンス化する java.net.URL wsdlLocation = new java.io.File( "./wsdl4runtime/master.wsdl" ).toURL(); javax.xml.namespace.QName serviceName = new javax.xml.namespace.QName( "http:/sample.com/", "AddNumbersImplService"); AddNumbersImplService service = new AddNumbersImplService( wsdlLocation, serviceName );
java.net.URLオブジェクトで指定したURLのWSDLが参照されるため,cjwsimportコマンドの引数や-wsdllocationオプションで指定したWSDLは,Webサービスクライアント実行時には存在しない,または参照できない状態でも問題ありません。ただし,Webサービスクライアント実行時のカレントディレクトリを<実行時カレントディレクトリ>とした場合は,<実行時カレントディレクトリ>/wsdl4runtime/master.wsdlが存在し,参照できる状態である必要があります。
(b) ポートを取得する
インスタンス化したサービスクラスからポートを取得する例を次に示します。
// ポートを取得する AddNumbersImpl port = service.getAddNumbersImplPort();
(c) ポートのメソッドを呼び出す
インスタンス化したサービスクラスから取得したポートのメソッドを呼び出す例を次に示します。
// ポートのメソッドを呼び出す int returnValue = port.add(205, 103)
この例では,ポートのメソッドの引数に二つの値を渡すと,Webサービスで足し算の処理が行われます。戻り値として足し算の演算結果が返されます。
接続するWebサービスのURL(エンドポイントアドレス)は,デフォルトでは,WSDLのポート(wsdl:port要素)が持つアドレス情報(soap:address子要素のlocation属性)が利用されます。ただし,メッセージコンテキストのjavax.xml.ws.service.endpoint.addressプロパティを利用すると,エンドポイントアドレスを動的に変更できます。
エンドポイントアドレスを動的に変更しない場合,参照されるWSDLのsoap:address子要素のlocation属性に,WebサービスクライアントからアクセスできるURLが記述されているかどうか確認してください。
エンドポイントアドレスを動的に変更する場合,ポートのメソッドを呼び出す前に要求コンテキストを取得し,javax.xml.ws.service.endpoint.addressプロパティの値を変更します。例を次に示します。
// 要求コンテキストを取得する java.util.Map<String, Object> context = ( ( javax.xml.ws.BindingProvider )port ).getRequestContext(); // エンドポイントアドレスを変更する // (javax.xml.ws.BindingProvider.ENDPOINT_ADDRESS_PROPERTYは // "javax.xml.ws.service.endpoint.address"を定義した定数) context.put( javax.xml.ws.BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "http://other.remote.org/fromjava/AddNumbersImplService" ); // ポートのメソッドを呼び出す int returnValue = port.add(205, 103)
- 注意事項
-
サービスクラスの生成やポートの取得には処理コストが掛かるので,一度生成,取得したサービスクラスとポートは再利用することをお勧めします。ポートのWebメソッドを複数回呼び出す場合に,サービスクラスを複数回生成したり,ポートを複数回取得したりする必要はありません。ただし,複数スレッドでポートを共有する場合,共有するポートの要求コンテキストのプロパティに対する変更は,複数スレッドが動作する前に実行してください。複数スレッドの動作中に変更すると,通信が失敗したり,不正なSOAPメッセージが送信されたりすることがあります。
ポートのメソッドを複数回呼び出す例を次に示します。
// サービスクラスをインスタンス化する AddNumbersImplService service = new AddNumbersImplService(); // ポートを取得する AddNumbersImpl port = service.getAddNumbersImplPort(); // ポートのメソッドを複数回呼び出す for (int i = 0; i < 10; i++) { int returnValue = port.add(i, i); }
(6) JavaアプリケーションのWebサービスクライアントの実装例
JavaアプリケーションのWebサービスクライアントから,サービスクラスを使用してWebサービスを呼び出す処理を実装します。
ここでは,「5.1 開発例の構成(SEI起点)」に構成を示すWebサービス(足し算をするWebサービス)を呼び出すWebサービスクライアントの実装例を示します。Webサービスクライアントの実装に使用するクラスおよびメソッドを次の表に示します。必要に応じて,「5.5.1 サービスクラスを生成する」に記載されたサービスクラスの生成物の内容を参照してください。
項番 |
種別 |
クラスおよびメソッド |
---|---|---|
1 |
サービスクラス |
AddNumbersImplService |
2 |
ポート |
AddNumbersImpl |
3 |
ポートのメソッド |
int add(int, int) |
4 |
Webサービスクライアントのクライアントのメインクラス |
TestClient |
Javaアプリケーションの実装例を次に示します。
package com.example.sample.client; import com.example.sample.AddNumbersImplTestJaxWs; import com.example.sample.AddNumbersImplTestJaxWsService; import com.sample.AddNumbersFault_Exception; // Sample implementation of web service's client public class TestClient { public static void main( String[] args ) { try { // サービスクラスをインスタンス化する AddNumbersImplTestJaxWsService service = new AddNumbersImplTestJaxWsService(); // ポートを取得する AddNumbersImplTestJaxWs port = service.getAddNumbersImplPortTestJaxWs(); // ポートのメソッドを呼び出す port.jaxWsTest1( ... ); int number1 = 205; int number2 = 103; int returnValue = port.add(number1, number2); // 結果を表示する System.out.println( "[RESULT] " + number1 + " + " + number2 + " = " + returnValue ); } catch( Exception e ){ // 例外処理(ここでは単にスタックトレースを出力) e.printStackTrace(); } } }
プログラムの実行結果を次に示します。
[RESULT] 205 + 103 = 308
(7) サーブレットのWebサービスクライアントの実装例
サーブレット形態のWebサービスクライアントから,サービスクラスを使用してWebサービスを呼び出す処理を実装します。
ここでは,「5.1 開発例の構成(SEI起点)」に構成を示すWebサービス(足し算をするWebサービス)を呼び出すWebサービスクライアントの実装例を示します。Webサービスクライアントの実装に使用するクラスおよびメソッドを次の表に示します。必要に応じて,「5.5.1 サービスクラスを生成する」に記載されたサービスクラスの生成物の内容を参照してください。
項番 |
種別 |
クラスおよびメソッド |
---|---|---|
1 |
サービスクラス |
AddNumbersImplService |
2 |
ポート |
AddNumbersImpl |
3 |
ポートのメソッド |
int add(int, int) |
4 |
Webサービスクライアントとなるサーブレット実装クラス |
TestClient |
(a) ポートのインジェクション
サーブレットからWebサービスを呼び出す実装では,ポート型のフィールドにjavax.xml.ws.WebServiceRefアノテーションを指定して,サーブレットインスタンスの生成時にポートのインジェクションを実行します。
インジェクションの例を次に示します。
... public class TestClient extends HttpServlet { // ポート(AddNumbersImpl)型のフィールドに,ポートをインジェクトする @WebServiceRef(AddNumbersImplService.class) AddNumbersImpl port; @Override public void init() { } ... }
(b) ポートのメソッド呼び出しによるWebサービスのオペレーション実行
サーブレットのフィールドとして生成したポートを使って,メソッドを呼び出します。
メソッド呼び出しの例を次に示します。
... public class TestClient extends HttpServlet { // ポート(AddNumbersImpl)型のフィールドに,ポートをインジェクトする @WebServiceRef(AddNumbersImplService.class) AddNumbersImpl port; ... @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ... int number1 = ...; // requestオブジェクトから取得した値を代入 int number2 = ...; // requestオブジェクトから取得した値を代入 // ポートのメソッドを呼び出す int returnValue = port.add(number1, number2); ... } }
サーブレットからWebサービスを呼び出す場合の実装例の全体を次に示します。
package com.sample.client; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.sample.AddNumbersFault_Exception; import com.sample.AddNumbersImpl; import com.sample.AddNumbersImplService; public class TestClient extends HttpServlet { // ポート(AddNumbersImpl)型のフィールドに,ポートをインジェクトする @WebServiceRef(AddNumbersImplService.class) AddNumbersImpl port; @Override public void init() { } @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); try { // Invoke a method of the target web service. int number1 = ...; // requestオブジェクトから取得した値を代入 int number2 = ...; // requestオブジェクトから取得した値を代入 // ポートのメソッドを呼び出す int returnValue = port.add(number1, number2); // 結果を表示する out.println("<html><body>"); out.println("<h1>RESULT</h1>"); out.println(number1 + " + " + number2 + " = " + returnValue); out.println("</body></html>"); } catch(AddNumbersFault_Exception e) { // 例外処理(ここでは単に例外の詳細メッセージを出力) out.println("<html><body>"); out.println("<h1>" + e.getMessage() + "</h1>"); out.println("</body></html>"); } } }
プログラムの実行結果を次に示します。ブラウザなどからサーブレットに接続すると実行結果が表示されます。
RESULT 205 + 103 = 308
(c) 注意事項
Webサービスクライアント(サーブレット)と接続先のWebサービスが,同じJ2EEサーバ上にデプロイされている環境では,次の場合,J2EEサーバの起動時に例外が発生します。
-
initメソッドまたはjavax.annotation.PostConstructアノテーションを指定したメソッドの中で,WebサービスからWSDLを取得してサービスクラスを生成するときに,Webサービスクライアントが含まれるWARファイルのweb.xmlにload-on-startup要素を指定した場合
-
javax.xml.ws.WebServiceRefアノテーションを利用して,WebサービスからWSDLを取得してサービスクラスやポートをインジェクトする場合
例外が発生する場合の対策方法を次に示します。
-
J2EEサーバを停止する前に,Webサービスクライアントが含まれるWebアプリケーションを停止してください。J2EEサーバを再起動したあとに,Webサービスクライアントが含まれるWebアプリケーションを開始してください。
-
WebサービスとWebサービスクライアントを別のJ2EEサーバにデプロイして,WebサービスをデプロイしたJ2EEサーバを起動したあとにWebサービスクライアントをデプロイしたJ2EEサーバを起動してください。
-
カタログ機能を使用して,サービスクラスの生成時にローカルに格納したWSDL文書を参照するように設定してください。
-
initメソッドまたはjavax.annotation.PostConstructアノテーションを指定したメソッドで,サービスクラスを生成する場合は,URLをパラメタに取るコンストラクタにローカルのWSDL文書を指定してサービスクラスを生成してください。または,web.xmlにload-on-startup要素を指定しないでください。
-
javax.xml.ws.WebServiceRefアノテーションを利用する場合は,wsdlLocation要素に相対パスまたは絶対パスで,ローカルに格納したWSDL文書を指定してください。