ヘッダーをスキップ
Oracle® Fusion Middleware Oracle WebLogic Server JAX-WS Webサービスの高度な機能のプログラミング
11g リリース1(10.3.6)
B61633-04
  目次へ移動
目次

前
 
次
 

3 Webサービス・クライアントを開発する手順

この章では、Java API for XML Web Services(JAX-WS)用のWebLogic Webサービス・クライアントを開発するためのベスト・プラクティスについて説明します。

この章の内容は以下のとおりです。


注意:

『Oracle WebLogic Server JAX-WS Webサービス・スタート・ガイド』のWebサービスの呼出しに関する項で説明されているWebサービス・クライアントの開発に関する一般的な概念に習熟していることが前提となっています。

信頼性のあるWebサービス・クライアントの開発に関するベスト・プラクティスについては、第5章「信頼性のあるWebサービスとクライアントを開発する手順」を参照してください。

次の項でクライアント・インスタンスは、ポートまたはディスパッチ・インスタンスです。


Webサービス・クライアントを開発する手順

表3-0では、Webサービス・クライアントを開発するためのベスト・プラクティスとその例を示します。非同期Webサービス・クライアントの開発に固有の他のベスト・プラクティスについては、「非同期Webサービス・クライアントを開発する手順」を参照してください。

表3-1 Webサービス・クライアントを開発する手順

ベスト・プラクティス 説明

クライアント・インスタンスの使用と同期させます。

クライアント・インスタンスは必要なときに作成します。長い間保存しないようにしてください。

クライアントIDを含む機能の保管されたリストを使用して、クライアント・インスタンスを作成します。

クライアントIDが含まれるWebサービス・クライアント・インスタンスのすべての機能を定義し、クライアント・インスタンスを作成するたびに異なることがないようにします。例:

_service.getBackendServicePort(_features);

クライアントIDを明示的に定義します。

ClientIdentityFeatureを使用してクライアントIDを明示的に定義します。このクライアントIDは、統計と他のモニタリング情報のグループ化や、実行時の検証のレポートに使用されます。詳細は、『Oracle WebLogic Server JAX-WS Webサービス・スタート・ガイド』のクライアントIDの管理に関する項を参照してください。

注意: クライアントIDは明示的に定義することを強くお薦めします。明示的に定義しないと、サーバーによって自動的にクライアントIDが生成されますが、ユーザーにとってわかりにくい場合があります。

処理が完了したら、クライアント・インスタンスを明示的に閉じます。

例:

((java.io.Closeable)port).close();

明示的に閉じないと、クライアント・インスタンスはユーザーがスコープ外になった時点で自動的に閉じられます。

注意: クライアントIDは、コンテナ(WebアプリケーションまたはEJB)が非アクティブ化されるまで、登録済みで可視の状態を維持します。詳細は、『Oracle WebLogic Server JAX-WS Webサービス・スタート・ガイド』のクライアントIDのライフサイクルに関する項を参照してください。


次の例では、Webサービス・クライアントを開発するためのベスト・プラクティスを示します。

例3-1 Webサービス・クライアントのベスト・プラクティスの例

import java.io.IOException;
import java.util.*;
 
import javax.servlet.*;
import javax.xml.ws.*;
 
import weblogic.jws.jaxws.client.ClientIdentityFeature;
 
/**
 * Example client for invoking a Web service.
 */
public class BestPracticeClient
  extends GenericServlet {
 
  private BackendServiceService _service;
  private WebServiceFeature[] _features;
  private ClientIdentityFeature _clientIdFeature;
 
  @Override
  public void init()
    throws ServletException {
 
    // Create a single instance of a Web service as it is expensive to create repeatedly.
    if (_service == null) {
      _service = new BackendServiceService();
    }
 
    // Best Practice: Use a stored list of features, per client ID, to create client instances.
    // Define all features for the Web service client instance, per client ID, so that they are 
    // consistent each time the client instance is created. For example: 
    // _service.getBackendServicePort(_features);
 
    List<WebServiceFeature> features = new ArrayList<WebServiceFeature>();
 
    // Best Practice: Explicitly define the client ID.
    // TODO: Maybe allow ClientIdentityFeature to store other features, and
    //       then create new client instances simply by passing the
    //       ClientIdentityFeature (and the registered features are used).
    _clientIdFeature = new ClientIdentityFeature("MyBackendServiceClient");
    features.add(_clientIdFeature);
 
    // Set the features used when creating clients with
    // the client ID "MyBackendServiceClient". The features are stored in an array to 
    // reinforce that the list should be treated as immutable.
    _features = features.toArray(new WebServiceFeature[features.size()]);
  }
 
  @Override
  public void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException {
 
    // ... Read the servlet request ...
 
    // Best Practice: Synchronize use of client instances.
    // Create a Web service client instance to talk to the backend service.
    // Note, at this point the client ID is 'registered' and becomes
    // visible to monitoring tools such as the Administration Console and WLST.
    // The client ID *remains* registered and visible until the container
    // (the Web application hosting our servlet) is deactivated (undeployed).
    //
    // A client ID can be used when creating multiple client instances (port or Dispatch client).
    // The client instance should be created with the same set of features each time, and should
    // use the same service class and refer to the same port type. 
    // A given a client ID should be used for a given port type, but not across port types.
    // It can be used for both port and Dispatch clients.
    BackendService port =
      _service.getBackendServicePort(_features);
 
    // Set the endpoint address for BackendService.
    ((BindingProvider)port).getRequestContext().
      put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
          "http://localhost:7001/BestPracticeService/BackendService");
 
    // Print out the explicit client ID, and compare it to the client ID 
    // that would have been generated automatically for the client instance.
    showClientIdentity();
 
    // Make the invocation on our real port
    String request = "Make a cake";
    System.out.println("Invoking DoSomething with request: " + request);
    String response = port.doSomething(request);
    System.out.println("Got response: " + response);
    res.getWriter().write(response);
 
    // Best Practice: Explicitly close client instances when processing is complete.
    // If not closed, the client instance will be closed automatically when it goes out of 
    // scope. Note, this client ID will remain registered and visible until our
    // container (Web application) is undeployed.
    ((java.io.Closeable)port).close();
  }
 
  /**
    // Print out the client's full ID, which is a combination of
    // the client ID provided above and qualifiers from the application and
    // Web application that contain the client. Then compare this with the client ID that
    // would have been generated for the client instance if not explicitly set.
    //
  private void showClientIdentity()
    throws IOException {
 
    System.out.println("Client Identity is: " + _clientIdFeature.getClientId());
 
    // Create a client instance without explicitly defining the client ID to view the 
    // client ID that is generated automatically.
    ClientIdentityFeature dummyClientIdFeature =
      new ClientIdentityFeature(null);
    BackendService dummyPort =
      _service.getBackendServicePort(dummyClientIdFeature);
    System.out.println("Generated Client Identity is: " +
                       dummyClientIdFeature.getClientId());
    // Best Practice: Explicitly close client instances when processing is complete.
    // If not closed, the client instance will be closed automatically when it goes out of 
    // scope. Note, this client ID will remain registered and visible until our
    // container (Web application) is undeployed.
    ((java.io.Closeable)dummyPort).close();
  }
 
  @Override
  public void destroy() {
  }
}

非同期Webサービス・クライアントを開発する手順

表3-1では、非同期Webサービス・クライアントを開発するためのベスト・プラクティスとその例を示します。これらのガイドラインは、「Webサービス・クライアントを開発する手順」で示されている一般的なガイドラインと併せて使用する必要があります。

表3-2 非同期Webサービス・クライアントを開発する手順

ベスト・プラクティス 説明

非同期およびディスパッチ・コールバック処理のためにポートベースの非同期コールバック・ハンドラAsyncClientHandlerFeatureを定義します。

拡張性があり、JVMを再起動しても維持されるため、非同期呼び出しを使用するときのベスト・プラクティスとして、AsyncClientHandlerFeatureを使用することをお薦めします。このハンドラは(持続可能かどうかに関係なく)任意のクライアントで使用できます。詳細は、「非同期ハンドラ・インタフェースの開発」を参照してください。

シングルトン・ポート・インスタンスを定義し、(デプロイメント時に)クライアント・コンテナを初期化するときに初期化します。

シングルトン・ポートの作成:

  • デプロイメント時に公開する非同期レスポンス・エンドポイントをトリガーします。

  • VMの再起動後にシングルトン・ポート・インスタンスを再初期化することで、障害リカバリをサポートします。

クラスタ内では、シングルトン・ポートを初期化すると、クラスタ内のすべてのメンバー・サーバーが非同期レスポンス・エンドポイントをパブリッシュすることが保証されます。これによって、非同期レスポンス・メッセージが任意のメンバー・サーバーに配信され、必要に応じてインプレース・クラスタ・ルーティングを介して正しいサーバーに転送されることが保証されます。詳細は、「非同期Webサービス・メッセージングのクラスタリングの考慮事項」を参照してください。

ファイアウォールの内側にあるクライアントにMake Connectionを使用する場合は、Make Connectionポーリング間隔をシナリオに対して現実的な値に設定します。

Make Connectionのポーリング間隔は、不必要なポーリング・オーバーヘッドを避けることができる大きな値で、なおかつ遅れずにレスポンスを取得できる十分に小さい値に設定する必要があります。Make Connectionポーリング間隔の推奨される値は、呼び出されるWebサービスの予想される平均レスポンス時間の半分です。Make Connectionポーリング間隔の設定の詳細は、「ポーリング間隔の構成」を参照してください。

注意: このベスト・プラクティスは、例3-2では示されていません。

JAX-WS参照実装(RI)を使用している場合は、AsyncHandler<T>インタフェースを実装します。

AsyncHandler<T>インタフェースを使用する方が、Response<T>インタフェースを使用するより効率的です。詳細および例は、「JAX-WS参照実装の使用」を参照してください。

注意: このベスト・プラクティスは、例3-2では示されていません。

ワーク・マネージャを定義し、スレッド・プールの最小サイズ制限(min-threads-constraint)を、最低でもサービスに対して予想される同時リクエスト数または同時レスポンス数と同じ値に設定します。

たとえば、Webサービス・クライアントが20個のリクエストを立て続けに発行する場合、そのクライアントをホストするアプリケーションで推奨されるスレッド・プールの最小サイズ制約の値は20です。構成した制約値が小さすぎると、受信処理で処理スレッドの空き待ちが発生するためパフォーマンスが大幅に低下するおそれがあります。

スレッド・プールの最小サイズ制約の詳細は、『Oracle WebLogic Serverサーバー環境の構成』の制約に関する項を参照してください。


次の例では、非同期Webサービス・クライアントを開発するためのベスト・プラクティスを示します。

例3-2 非同期Webサービス・クライアントのベスト・プラクティスの例

import java.io.*;
import java.util.*;
 
import javax.servlet.*
import javax.xml.ws.*
 
import weblogic.jws.jaxws.client.ClientIdentityFeature;
import weblogic.jws.jaxws.client.async.AsyncClientHandlerFeature;
import weblogic.jws.jaxws.client.async.AsyncClientTransportFeature;
 
import com.sun.xml.ws.developer.JAXWSProperties;
 
/**
 * Example client for invoking a Web service asynchronously.
 */
public class BestPracticeAsyncClient
  extends GenericServlet {
 
  private static final String MY_PROPERTY = "MyProperty";
 
  private BackendServiceService _service;
  private WebServiceFeature[] _features;
  private BackendService _singletonPort;
 
  private static String _lastResponse;
  private static int _requestCount;
 
  @Override
  public void init()
    throws ServletException {
 
    // Only create the Web service object once as it is expensive to create repeatedly.
    if (_service == null) {
      _service = new BackendServiceService();
    }
 
    // Best Practice: Use a stored list of features, including client ID, to create client 
    // instances.
    // Define all features for the Web service client instance, including client ID, so that they
    // are consistent each time the client instance is created. For example: 
    // _service.getBackendServicePort(_features);
 
    List<WebServiceFeature> features = new ArrayList<WebServiceFeature>();
 
    // Best Practice: Explicitly define the client ID.
    ClientIdentityFeature clientIdFeature =
      new ClientIdentityFeature("MyBackendServiceAsyncClient");
    features.add(clientIdFeature);
 
    // Asynchronous endpoint
    AsyncClientTransportFeature asyncFeature =
      new AsyncClientTransportFeature(getServletContext());
    features.add(asyncFeature);
 
    // Best Practice: Define a port-based asynchronous callback handler,
    // AsyncClientHandlerFeature, for asynchronous and dispatch callback handling.
    BackendServiceAsyncHandler handler =
      new BackendServiceAsyncHandler() {
        // This class is stateless and should not depend on
        // having member variables to work with across restarts.
        public void onDoSomethingResponse(Response<DoSomethingResponse> res) {
          // ... Handle Response ...
          try {
            DoSomethingResponse response = res.get();
            res.getContext();
            _lastResponse = response.getReturn();
            System.out.println("Got async response: " + _lastResponse);
            // Retrieve the request property. This property can be used to 
            // 'remember' the context of the request and subsequently process
            // the response.
            Map<String, Serializable> requestProps =
              (Map<String, Serializable>)
                res.getContext().get(JAXWSProperties.PERSISTENT_CONTEXT);
            String myProperty = (String)requestProps.get(MY_PROPERTY);
            System.out.println("Got MyProperty value propagated from request: "+
                               myProperty);
          } catch (Exception e) {
            _lastResponse = e.toString();
            e.printStackTrace();
          }
        }
      };
    AsyncClientHandlerFeature handlerFeature =
      new AsyncClientHandlerFeature(handler);
    features.add(handlerFeature);
 
    // Set the features used when creating clients with
    // the client ID "MyBackendServiceAsyncClient".
 
    _features = features.toArray(new WebServiceFeature[features.size()]);

    // Best Practice: Define a singleton port instance and initialize it when 
    // the client container initializes (upon deployment).
    // The singleton port will be available for the life of the servlet.
    // Creation of the singleton port triggers the asynchronous response endpoint to be published
    // and it will remain published until our container (Web application) is undeployed.
    // Note, the destroy() method will be called before this.
    // The singleton port ensures proper/robust operation in both
    // recovery and clustered scenarios.
    _singletonPort = _service.getBackendServicePort(_features);
  }
 
  @Override
  public void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException {
 
    // TODO: ... Read the servlet request ...
 
    // For this simple example, echo the _lastResponse captured from 
    // an asynchronous DoSomethingResponse response message.
 
    if (_lastResponse != null) {
      res.getWriter().write(_lastResponse);
      _lastResponse = null; // Clear the response so we can get another
      return;
    }
 
    // Set _lastResponse to NULL to to support the invocation against
    // BackendService to generate a new response.
 
    // Best Practice: Synchronize use of client instances.
    // Create another client instance using the *exact* same features used when creating _
    // singletonPort. Note, this port uses the same client ID as the singleton port 
    // and it is effectively the same as the singleton
    // from the perspective of the Web services runtime. 
    // This port will use the asynchronous response endpoint for the client ID, 
    // as it is defined in the _features list.
    BackendService anotherPort =
      _service.getBackendServicePort(_features);
 
    // Set the endpoint address for BackendService.
    ((BindingProvider)anotherPort).getRequestContext().
      put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
          "http://localhost:7001/BestPracticeService/BackendService");
 
    // Add a persistent context property that will be retrieved on the
    // response. This property can be used as a reminder of the context of this
    // request and subsequently process the response. This property will *not*
    // be passed over the wire, so the properties can change independent of the
    // application message.
    Map<String, Serializable> persistentContext =
      (Map<String, Serializable>)((BindingProvider)anotherPort).
        getRequestContext().get(JAXWSProperties.PERSISTENT_CONTEXT);
    String myProperty = "Request " + (++_requestCount);
    persistentContext.put(MY_PROPERTY, myProperty);
    System.out.println("Request being made with MyProperty value: " +
                       myProperty);
 
    // Make the asychronous invocation. The asynchronous handler implementation (set 
    // into the AsyncClientHandlerFeature above) receives the response.
    String request = "Dance and sing";
    System.out.println("Invoking DoSomething asynchronously with request: " +
                       request);
    anotherPort.doSomethingAsync(request);
 
    // Return a canned string indicating the response was not received
    // synchronously. Client will need to invoke the servlet again to get
    // the response.
    res.getWriter().write("Waiting for response...");
 
    // Best Practice: Explicitly close client instances when processing is complete.
    // If not closed explicitly, the port will be closed automatically when it goes out of scope.
    ((java.io.Closeable)anotherPort).close();
  }
 
  @Override
  public void destroy() {
 
    try {
      // Best Practice: Explicitly close client instances when processing is complete.
      // Close the singleton port created during initialization. Note, the asynchronous
      // response endpoint generated by creating _singletonPort *remains*
      // published until our container (Web application) is undeployed.
      ((java.io.Closeable)_singletonPort).close();

      // Upon return, the Web application is undeployed, and the asynchronous
      // response endpoint is stopped (unpublished). At this point,
      // the client ID used for _singletonPort will be unregistered and will no longer be
      // visible from the Administration Console and WLST.
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}