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

前
 
次
 

5 信頼性のあるWebサービスとクライアントを開発する手順

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

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


注意:

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

「Webサービス永続性を構成する手順」も参照してください。


信頼性のあるWebサービス・クライアントを開発する手順

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

表5-1 信頼性のあるWebサービス・クライアントを開発する手順

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

常に信頼性エラー・リスナーを実装します。

詳細は、「信頼性エラー・リスナーの実装」を参照してください。

メッセージを作業単位にグループ化します。

送信されるすべてのメッセージでRMシーケンスの作成と終了のプロトコル・オーバーヘッドを発生させるのではなく、メッセージをビジネス作業単位にグループ化できます(バッチ化とも呼ばれます)。詳細は、「ビジネス作業単位へのメッセージのグループ化(バッチ化)」を参照してください。

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

確認応答の間隔を特定のシナリオに適した現実的な値に設定します。

お薦めする設定は、リクエスト間の通常の間隔の2倍です。詳細は、「確認応答の間隔の構成」を参照してください。

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

基本の再送信間隔を特定のシナリオに適した現実的な値に設定します。

お薦めする設定は、確認レスポンスの間隔または通常のレスポンス時間のどちらか大きい方の2倍です。詳細は、「基本の再送信間隔の構成」を参照してください。

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

タイムアウト(非アクティブおよび順序の有効期限)を特定のシナリオに適した現実的な値に設定します。

詳細は、「非アクティブ・タイムアウトの構成」および「順序の有効期限の構成」を参照してください。

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


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

例5-1 信頼性のあるWebサービス・クライアントのベスト・プラクティスの例

import java.io.*;
import java.util.*;
 
import javax.servlet.*;
import javax.xml.bind.JAXBContext;
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 weblogic.wsee.reliability2.api.ReliabilityErrorContext;
import weblogic.wsee.reliability2.api.ReliabilityErrorListener;
import weblogic.wsee.reliability2.api.WsrmClientInitFeature;
 
import com.sun.xml.ws.developer.JAXWSProperties;
 
/**
 * Example client for invoking a reliable Web service asynchronously.
 */
public class BestPracticeAsyncRmClient
  extends GenericServlet {
 
  private BackendReliableServiceService _service;
  private BackendReliableService _singletonPort;
  private WebServiceFeature[] _features;
 
  private static int _requestCount;
  private static String _lastResponse;
  private static final String MY_PROPERTY = "MyProperty";
 
  @Override
  public void init()
    throws ServletException {
 
    _requestCount = 0;
    _lastResponse = null;
 
    // Only create the Web service object once as it is expensive to create repeatedly.
    if (_service == null) {
      _service = new BackendReliableServiceService();
    }
 
    // Best Practice: Use a stored list of features, per client ID, to create client instances.
    // Define all features for the Web service port, per client ID, so that they are 
    // consistent each time the port is called. For example: 
    // _service.getBackendServicePort(_features);
 
    List<WebServiceFeature> features = new ArrayList<WebServiceFeature>();
 
    // Best Practice: Explicitly define the client ID.
    ClientIdentityFeature clientIdFeature =
      new ClientIdentityFeature("MyBackendServiceAsyncRmClient");
    features.add(clientIdFeature);
 
    // Best Practice: Always implement a reliability error listener. 
    // Include this feature in your reusable feature list. This enables you to determine
    // a reason for failure, for example, RM cannot deliver a request or the RM sequence fails in
    // some way (for example, client credentials refused at service).
    WsrmClientInitFeature rmFeature = new WsrmClientInitFeature();
    features.add(rmFeature);
    rmFeature.setErrorListener(new ReliabilityErrorListener() {
      public void onReliabilityError(ReliabilityErrorContext context) {
 
        // At a *minimum* do this
        System.out.println("RM sequence failure: " +
                           context.getFaultSummaryMessage());
        _lastResponse = context.getFaultSummaryMessage();
 
        // And optionally do this...
 
        // The context parameter conveys whether a request or the entire
        // sequence has failed. If a sequence fails, you will get a notification
        // for each undelivered request (if any) on the sequence.
        if (context.isRequestSpecific()) {
          // Single request failure (possibly as part of a larger sequence failure).
          // Retrieve the original request.
          String operationName = context.getOperationName();
          System.out.println("Failed to deliver request for operation '" +
                             operationName + "'. Fault summary: " +
                             context.getFaultSummaryMessage());
          if ("DoSomething".equals(operationName)) {
            try {
              String request = context.getRequest(JAXBContext.newInstance(),
                                                  String.class);
              System.out.println("Failed to deliver request for operation '" +
                                 operationName + "' with content: " +
                                 request);
              Map<String, Serializable> requestProps =
                context.getUserRequestContextProperties();
              if (requestProps != null) {
                // Retrieve the request property. Use MyProperty
                // to describe the request that failed and print this value
                // during the simple 'error recovery' below.
                String myProperty = (String)requestProps.get(MY_PROPERTY);
                System.out.println("Got MyProperty value propagated from request: "+
                                   myProperty);
                System.out.println(myProperty + " failed!");
              }
            } catch (Exception e) {
              e.printStackTrace();
            }
          }
        } else {
          // The entire sequence has encountered an error.
          System.out.println("Entire sequence failed: " +
                             context.getFaultSummaryMessage());
 
        }
      }
    });
 
    // 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.
    BackendReliableServiceAsyncHandler handler =
      new BackendReliableServiceAsyncHandler() {
        public void onDoSomethingResponse(Response<DoSomethingResponse> res) {
          // ... Handle Response ...
          try {
            // Show getting the MyProperty value back.
            DoSomethingResponse response = res.get();
            _lastResponse = response.getReturn();
            System.out.println("Got (reliable) 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 "MyBackendServiceAsyncRmClient."
 
    _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, we will get a call to destroy() before this.
    _singletonPort = _service.getBackendReliableServicePort(_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 in order to make a new invocation against
    // BackendService to generate a new response
 
    // Best Practice: Synchronize use of client instances.
    // Create another port 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.
    // NOTE: This is *DEFINITELY* not best practice or ideal because our application is
    //       incurring the cost of an RM handshake and sequence termination
    //       for *every* reliable request sent. It would be better to send
    //       multiple requests on each sequence. If there is not a natural grouping 
    //       for messages (a business 'unit of work'), then you could batch
    //       requests onto a sequence for efficiency. For more information, see
    //        Grouping Messages into Business Units of Work (Batching).
    BackendReliableService anotherPort =
      _service.getBackendReliableServicePort(_features);
 
    // Set the endpoint address for BackendService.
    ((BindingProvider)anotherPort).getRequestContext().
      put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
          "http://localhost:7001/BestPracticeReliableService/BackendReliableService");
 
    // Make the invocation. Our asynchronous handler implementation (set 
    // into the AsyncClientHandlerFeature above) receives the response.
    String request = "Protect and serve";
    System.out.println("Invoking DoSomething reliably/async with request: " +
                       request);
    // Add a persistent context property that will be returned on the response.
    // This property can be used to 'remember' the context of this
    // request and subsequently process the response. This property will *not*
    // get passed over 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 (reliably) with MyProperty value: " +
                       myProperty);
    anotherPort.doSomethingAsync(request);
 
    // Return a canned string indicating the response was not received
    // synchronously. Client needs 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, the port will be closed automatically when it goes out of scope.
    // This will force the termination of the RM sequence we created when sending the first
    // doSomething request. For a better way to handle this, see 
    //  Grouping Messages into Business Units of Work (Batching).
    // NOTE: Even though the port is closed explicitly (or even if it goes out of scope)
    //       the reliable request sent above will still be delivered
    //       under the scope of the client ID used. So, even if the service endpoint
    //       is down, RM retries the request and delivers it when the service endpoint 
    //       available. The asynchronous resopnse will be delivered as if the port instance was 
    //       still available.
    ((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 our 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();
    }
  }
}

信頼性のあるWebサービスを開発する手順

表5-2では、信頼性のあるWebサービスを開発するためのベスト・プラクティスを示します。ファイアウォールの内側から信頼性のあるWebサービスにアクセスするときのベスト・プラクティスについては、「ファイアウォールの内側から信頼性のあるWebサービスにアクセスする手順(接続作成)」を参照してください。

表5-2 信頼性のあるWebサービスを開発する手順

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

基本の再送信間隔を特定のシナリオに適した現実的な値に設定します。

詳細は、「基本の再送信間隔の構成」を参照してください。

確認応答の間隔を特定のシナリオに適した現実的な値に設定します。

お薦めする設定は、リクエスト間の通常の間隔の2倍です。詳細は、「確認応答の間隔の構成」を参照してください。

タイムアウト(非アクティブおよび順序の有効期限)を特定のシナリオに適した現実的な値に設定します。

以下のことを考慮してください。

  • 非常に短期間の交換の場合、デフォルトのタイムアウトでは長すぎて、シーケンスの状態が必要以上に長く維持される場合があります。

  • 特定のビジネス作業単位の予想される継続時間の2倍にタイムアウトを設定します。これにより、シーケンスは十分に長く存在できます。

詳細は、「非アクティブ・タイムアウトの構成」および「順序の有効期限の構成」を参照してください。

必要な最低の配信保証(またはサービス品質)を反映した信頼性のあるメッセージング・ポリシーを使用します。

デフォルトでは、配信保証はExactly Once, In Orderに設定されています。順序が必要ない場合、配信保証を単にExactly Onceに設定することで、パフォーマンスを向上させることができます。同様に、重複するリクエストをサービスが許容できる場合は、配信保証をAt Least Onceに設定できます。

信頼性のあるメッセージングの配信保証の詳細は、表6-1「信頼性のあるメッセージングの配信保証」および「Webサービスの信頼性のあるメッセージングのWS-Policyファイルの作成」を参照してください。


ファイアウォールの内側から信頼性のあるWebサービスにアクセスする手順(接続作成)

表5-3では、接続作成を使用してファイアウォールの内側から信頼性のあるWebサービスにアクセスするためのベスト・プラクティスを示します。これらのガイドラインは、「信頼性のあるWebサービスを開発する手順」および「非同期Webサービス・クライアントを開発する手順」で示されている一般的なガイドラインと組み合わせて使用する必要があります。

表5-3 ファイアウォールの内側から信頼性のあるWebサービスにアクセスする手順(接続作成)

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

信頼性のあるメッセージングの基本の再送信間隔で接続作成のポーリング間隔を調整します。

接続作成トランスポートに対して設定するポーリング間隔は、信頼性のあるメッセージング・プロトコルのメッセージがクライアントとサービスの間を往復するのに要する時間の下限を設定します。信頼性のあるメッセージングの基本の再送信間隔を接続作成ポーリング間隔に近い値に設定した場合、信頼性のあるメッセージングのリクエストがWebサービスによって受信され、信頼性のあるメッセージングのランタイムがリクエストの再送信を試みる前に関連するRM確認応答がリクエストに対して送信される(最善で1 接続作成ポーリング間隔後に)可能性が低くなります。信頼性のあるメッセージングの基本の再送信間隔を低すぎる値に設定すると、リクエストに対して不必要な再送信が発生し、重複する着信リクエストの処理が試みられて、接続作成がリクエストからレスポンスを取得するためにメッセージをポーリングするため、サービス側で負荷が連鎖的に発生する可能性があります。

基本の再送信間隔は接続作成ポーリング間隔の2倍以上の値に設定することをお薦めします。

注意: Webサービスの信頼性のあるメッセージングと接続作成を併用すると、接続作成のポーリング間隔が必要に応じて実行時に調整され、信頼性のあるメッセージングの基本の再送信間隔より少なくとも3秒小さい値に設定されます。基本の再送信間隔が3秒以下の場合は、接続作成のポーリング間隔は基本の再送信間隔の値に設定されます。

接続作成のポーリング間隔と信頼性のあるメッセージングの基本の再送信間隔の設定の詳細は、それぞれ、「ポーリング間隔の構成」および「基本の再送信間隔の構成」を参照してください。


信頼性のあるWebサービスを保護する手順

表5-4では、WS-SecureConversationを使用して信頼性のあるWebサービスを保護するためのベスト・プラクティスを示します。これらのガイドラインは、「信頼性のあるWebサービスを開発する手順」で示されているガイドラインと併せて使用する必要があります。

表5-4 信頼性のあるWebサービスを保護する手順

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

信頼性のあるメッセージングの基本の再送信間隔および確認応答の間隔でWS-SecureConversationの存続期間を調整します。

信頼性のあるメッセージングの基本の再送信間隔および確認応答の間隔に近い値、またはそれより小さい値に、WS-SecureConversationの存続期間を設定すると、信頼性のあるメッセージングのハンドシェイク・メッセージをWebサービスに送信できる前に、WS-SecureConversationトークンの有効期限が切れる可能性があります。このため、WS-SecureConversationの存続期間は、基本の再送信間隔の2倍以上に設定することをお薦めします。

基本の再送信間隔の設定の詳細は、「基本の再送信間隔の構成」を参照してください。