Hitachi

Cosminexus V11 アプリケーションサーバ Webサービス開発ガイド


3.6.1 スタブベースの実装例

cjwsimportコマンドで自動生成したサービスクラスやスタブを使用して,Webサービスクライアントを開発できます。スタブベースのWebサービスクライアントの実装で,サービスクラスの生成やポートを取得するには,次の方法があります。

サービスクラスやポートのインジェクションについては,「10.21.1 サービスクラスおよびポートのインジェクション」を,javax.xml.ws.WebServiceRefアノテーションについては,「19.3 アノテーションのサポート範囲」を参照してください。

〈この項の構成〉

(1) 参照されるWSDL

スタブベースのWebサービスクライアントを実行する場合,WSDLのパスまたはURLが必要になります。このとき,次の条件の組み合わせによって,参照されるWSDLが異なります。

条件ごとの参照されるWSDLの対応を次の表に示します。なお,それぞれの条件での例については,「3.6.1(5)(a) サービスクラスをインスタンス化する」を参照してください。

表3‒5 条件の組み合わせと参照されるWSDLの対応

項番

利用するコンストラクタ

-wsdllocation

オプション

参照されるWSDL

1

デフォルトコンストラクタ

×

cjwsimportコマンドの引数に指定したWSDL※1

2

デフォルトコンストラクタ

-wsdllocationオプションに指定したWSDL※2

3

java.net.URL オブジェクトおよびjavax.xml.namespace.QNameオブジェクトをパラメタに持つコンストラクタ

パラメタのURLに指定したWSDL※2

(凡例)

○:指定した場合。

×:指定しなかった場合。

−:指定の有無は参照されるWSDLに影響しません。

注※1

Webサービスクライアントを実行する場合,WSDLはcjwsimportコマンド実行時と同じ場所に存在している必要があります。cjwsimportコマンド実行時に相対パスを指定した場合も,WSDLはcjwsimportコマンド実行時と同じ場所に存在している必要があります。

注※2

絶対URLを指定した場合,Webサービスクライアントの実行時に,WSDLがそのURLが示す場所に存在している必要があります。

相対URLを指定した場合,Webサービスクライアントの実行時に,WSDLがカレントディレクトリを基準として相対URLを解決した場所に存在している必要があります。

(2) 参照されるエンドポイントアドレス

接続するWebサービスのURL(エンドポイントアドレス)は,デフォルトではWSDLのポート(wsdl:port要素)が持つアドレス情報(soap:address子要素のlocation属性)が利用されます。ただし,メッセージコンテキストのjavax.xml.ws.service.endpoint.addressプロパティを利用すると,エンドポイントアドレスを動的に変更できます。エンドポイントアドレスを動的に変更する例については,「3.6.1(5)(c) ポートのメソッドを呼び出す」を参照してください。

(3) サービスクラスの生成およびポートの取得

サービスクラスの生成やポートの取得には処理コストが掛かるので,次のように,ポートをインジェクションまたは再利用することをお推めします。

(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サービスを呼び出します。

Webサービスクライアントの実装でWebサービスのオペレーションを呼び出す手順は,次のとおりです。

  1. サービスクラスをインスタンス化する

  2. ポートを取得する

  3. ポートのメソッドを呼び出す

ここでは,「5.1 開発例の構成(SEI起点)」に構成を示すWebサービス(足し算をするWebサービス)を呼び出すWebサービスクライアントの実装例を示します。

Webサービスクライアントの実装に使用するクラスおよびメソッドを次の表に示します。必要に応じて,「5.5.1 サービスクラスを生成する」に記載されたサービスクラスの生成物の内容を参照してください。

表3‒6 Webサービスクライアントの実装例で使用するクラスおよびメソッド

項番

種別

クラスおよびメソッド

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 サービスクラスを生成する」に記載されたサービスクラスの生成物の内容を参照してください。

表3‒7 Webサービスクライアントの実装例で使用するクラスおよびメソッド(JavaアプリケーションのWebサービスクライアントの場合)

項番

種別

クラスおよびメソッド

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 サービスクラスを生成する」に記載されたサービスクラスの生成物の内容を参照してください。

表3‒8 Webサービスクライアントの実装例で使用するクラスおよびメソッド(サーブレットから呼び出す場合)

項番

種別

クラスおよびメソッド

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文書を指定してください。