Cosminexus アプリケーションサーバ V8 Webサービス開発の手引

[目次][用語][索引][前へ][次へ]

3.6.1 スタブベースの実装例

スタブベースのWebサービスクライアントの実装例について説明します。

<この項の構成>
(1) 参照されるWSDL
(2) 参照されるエンドポイントアドレス
(3) サービスクラスおよびポートの再利用
(4) サービスの選択
(5) 基本的な実装例
(6) JavaアプリケーションのWebサービスクライアントの実装例
(7) サーブレットのWebサービスクライアントの実装例

(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) サービスクラスおよびポートの再利用

サービスクラスの生成には処理コストが掛かるので,一度生成したサービスクラスは再利用することをお勧めします。ポートを取得するためにサービスクラスを複数回生成する必要はありません。同様に,ポートの取得にも処理コストが掛かるので,一度取得したポートは再利用することをお勧めします。ポートのWebメソッドを複数回呼び出すためにポートを複数回取得する必要はありません。ただし,複数スレッドでポートを共有する場合,共有するポートの要求コンテキストのプロパティに対する変更は,複数スレッドが動作する前に実行してください。複数スレッドの動作中に変更すると,通信が失敗したり,不正なSOAPメッセージが送信されたりすることがあります。

WebサービスクライアントをサーブレットやEJBなどで実装する場合は,それぞれの初期化メソッドでサービスクラスの生成,またはポートの取得を実施し,再利用してください。ポートの要求コンテキストのプロパティに対する変更も,それぞれの初期化メソッドで実行してください。

(4) サービスの選択

呼び出すWebサービスのWSDLに複数のサービス(wsdl:service要素)が含まれる場合,サービスクラスを生成するときに,java.net.URLオブジェクトとjavax.xml.namespace.QNameオブジェクトをパラメタに持つコンストラクタを使用し,javax.xml.namespace.QNameオブジェクトでそのサービス(wsdl:service要素)を呼び出すのか明示的に指定してください。

(5) 基本的な実装例

スタブベースの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サービスを呼び出す実装では,サービスクラスおよびポートをサーブレットのメンバ変数として保持し,初期化します。

サービスクラスのインスタンス化およびポートの初期化の例を次に示します。

...
public class TestClient extends HttpServlet {
 
    AddNumbersImplService service;
    AddNumbersImpl port;
 
    @Override
    public void init() {
        // サービスクラスをインスタンス化する
        service = new AddNumbersImplService();
        // ポートを取得する
        port = service.getAddNumbersImplPort();
    }
    ...
}
(b) ポートのメソッド呼び出しによるWebサービスのオペレーション実行

サーブレットのメンバ変数として生成したポートを使って,メソッドを呼び出します。

メソッド呼び出しの例を次に示します。

...
public class TestClient extends HttpServlet {
 
    AddNumbersImplService service;
    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 {
 
    AddNumbersImplService service;
    AddNumbersImpl port;
 
    @Override
    public void init() {
        // サービスクラスを初期化する
        service = new AddNumbersImplService();
        // ポートを取得する
        port = service.getAddNumbersImplPort();
    }
 
    @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

注意事項
Webサービスクライアント(サーブレット)とWebサービスが,同じJ2EEサーバ上にデプロイされている環境で,Webサービスクライアントが含まれるWebアプリケーションのDD(web.xml)に<load-on-startup>タグを指定し,J2EEサーバ起動時にサーブレットを初期化する設定にすると,Webサービスクライアント(サーブレット)のinit()実行時に例外が発生します。そのため,DD(web.xml)には<load-on-startup>タグを指定しないでください。性能などの理由で<load-on-startup>タグを使用したい場合は,WebサービスクライアントとWebサービスを別々のJ2EEサーバ上にデプロイして実行してください。