プライマリ・コンテンツに移動
Oracle® Fusion Middleware Oracle WebLogic Server 12.1.3 JAX-RPC Webサービスの開発
12c (12.1.3)
E57562-03
  ドキュメント・ライブラリへ移動
ライブラリ
製品リストへ移動
製品
目次へ移動
目次

前
 
次
 

9 会話型Webサービスの作成

この章では、WebLogic Server 12.1.3の会話型のWebLogic Java API for XML-based RPC (JAX-RPC) Webサービスの作成方法について説明します。

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

会話型Webサービスの概要

Webサービスと、そのWebサービスを呼び出すクライアント・アプリケーションは、1つのタスクを完了するために複数回通信する場合があります。また、複数のクライアント・アプリケーションが同時に同じWebサービスと通信する場合もあります。会話を使用すると、直接的な方法で、呼出し間のデータを追跡して、Webサービスが常に正しいクライアントに応答するようにできます。

会話を使用すると、複数の通信にわたってデータを維持することに伴う以下の2つの問題が解決されます。

  • 会話では、1つのクライアント・アプリケーションと1つのWebサービスの間の双方向通信が一意に識別されるので、メッセージが常に正しいクライアントに戻されます。たとえば、ショッピング・カート・アプリケーションでは、どのショッピング・カートがどの顧客に属するかを会話型Webサービスが追跡します。会話型Webサービスでは、クライアント・アプリケーションとの間で新しい会話が始まるたびに、一意の会話IDを作成することによって、これを実現しています。

  • 会話によってWebサービスへの呼出し間の状態を保持します。つまり、サービスに対する呼出し間の特定のクライアント・アプリケーションに関連付けられているデータを追跡します。会話を使用すると、特定のクライアントに関連付けられているデータは、必要がなくなるまで、または処理が完了するまで、確実に保存されます。たとえば、ショッピング・カート・アプリケーションでは、顧客がショッピングを続けている間、ショッピング・カートに入っている品物を会話型Webサービスが記憶します。状態の保持はまた、会話中にWebサービスをホストするコンピュータで発生した障害に対処するためにも必要です。すべての状態関連データがディスクに永続化されるので、コンピュータが再起動すれば、クライアント・アプリケーションとの会話を続行できます。

WebLogic Serverはこの一意のIDと状態を、クライアント・アプリケーションが新しく会話を開始するたびに会話コンテキストを作成することによって管理します。Webサービスがその後、このコンテキストを使用して、サービスとの間の呼出しを相関させ、状態関連データを永続化します。

クライアント・アプリケーションとWebサービスの間の会話には、3つの異なるフェーズがあります。

  • 開始: クライアント・アプリケーションが、会話型Webサービスの開始操作を呼び出すことによって、会話を開始します。次にそのWebサービスが、新しい会話コンテキストおよびそれに伴う一意のIDを作成し、アイドル時間と会話の存続期間を計測する内部タイマーを起動します。

  • 続行: クライアント・アプリケーションは会話を開始した後、1つ以上の続行操作を呼び出して会話を続行します。会話型Webサービスでは、呼出しに関連付けられたIDを使用して、会話しているクライアント・アプリケーション、永続化する状態、およびリセットするアイドル・タイマーを判断します。通常の続行操作は、クライアント・アプリケーションからさらに情報をリクエストするものや、ステータスをリクエストするものなどです。

  • 終了: クライアント・アプリケーションは、会話が終了すると明示的に終了操作を呼び出します。Webサービスがその後、その会話と関連付けられたすべてのデータまたはリソースを、削除済としてマークします。

会話は通常、2つのWebLogic Webサービス間で発生します。そのうち1つは会話型としてマークされ、開始、続行、および終了の操作を定義し、もう1つのWebサービスは@ServiceClientアノテーションを使用して、それが会話型Webサービスのクライアントであることを指定します。また、制限事項はありますが、会話型WebサービスはスタンドアロンJavaクライアントからも呼び出せます。

他のWebLogic Webサービスの機能と同様に、Webサービスを会話型のものとして指定するには、JWSアノテーションを使用します。


注意:

会話型Webサービスを呼び出すクライアントWebサービスは、必ずしも会話型にする必要はありません。ただし、クライアントが会話型でない場合は、このクライアントの複数のインスタンスが同じ会話型Webサービス・スタブにアクセスする危険性があり、保存されている会話状態が破損するおそれがあります。この危険性があると判断した場合は、クライアントWebサービスも会話型になるように指定してください。その場合は、スタンドアロンのJavaクライアントは使用できません。これは、WebLogic APIを使用して会話型であることを指定する方法がないためです。

会話型Webサービス自体は、必ず1回、メッセージの配信が行われることや、メッセージが順序どおりに配信されることを保証するものではありません。そのようなメッセージ配信保証が必要であれば、Webサービスに信頼性があることも指定する必要があります。「Webサービスの信頼性のあるメッセージングの使用:主な手順」および第11章「非同期機能の併用」を参照してください。


会話型Webサービスの作成: 主な手順

次の手順では、会話型Webサービス、ならびにクライアントWebサービスおよびスタンドアロンJavaクライアント・アプリケーション(いずれも会話を開始および実施するもの)を作成する方法について説明します。この手順では、2つのWebサービスを実装するJWSファイルをゼロから作成する方法を示しています。既存のJWSファイルを更新する場合は、この手順をガイドとして利用することもできます。

Antベースの開発環境を設定済であり、かつjwsc Antタスクを実行して生成された会話型Webサービスをデプロイするためのターゲットを追加できる、作業用のbuild.xmlファイルがあることが前提となっています。さらに、会話を開始するクライアントWebサービスをホストする、WebLogic Serverインスタンスを同様に設定してあることも前提となっています。詳細は、次の項を参照してください。

表9-1 会話型Webサービスの作成手順

#
手順 説明

1

会話型Webサービスを実装する、新しいJWSファイルを作成するか、既存のJWSファイルを更新します。

使い慣れたIDEまたはテキスト・エディタを使用します。「会話型JWSファイルに関するプログラミングのガイドライン」を参照してください。

2

build.xmlファイルを更新して、会話型JWSファイルをWebサービスにコンパイルするjwsc Antタスクの呼出しを含めます。

「jwsc WebLogic WebサービスAntタスクの実行」を参照してください。

3

Antターゲットを実行して、会話型Webサービスをビルドします。

例:

prompt> ant build-mainService

4

ターゲットWebサービスを通常どおりデプロイします。

「WebLogic Webサービスのデプロイとアンデプロイ」を参照してください。

5

クライアントWebサービスを実装する、新しいJWSファイルを作成するか、既存のJWSファイルを更新します。

クライアント・アプリケーションがスタンドアロンJavaクライアントである場合は、「会話型Webサービスを呼び出すためのスタンドアロンJavaクライアントの更新」を参照してください。手順6 - 9をスキップします。

クライアント・アプリケーション自体がWebサービスである場合は、ステップ6 - 9に従います。

6

会話型Webサービスで会話を開始および実施する新しいJWSファイルを作成するか、既存のJWSファイルを更新します。

使い慣れたIDEまたはテキスト・エディタを使用します。クライアントWebサービスが、会話型Webサービスをホストするものとは別のWebLogic Serverインスタンスにデプロイされていることが前提となります。「会話型Webサービスを呼び出すJWSファイルに関するプログラミングのガイドライン」を参照してください。

7

クライアントWebサービスをビルドするbuild.xmlファイルを更新します。

「会話型Webサービスのクライアント用build.xmlファイルの更新」を参照してください。

8

Antターゲットを実行して、クライアントWebサービスをビルドします。

例:

prompt> ant build-clientService

9

クライアントWebサービスを通常どおりデプロイします。

「WebLogic Webサービスのデプロイとアンデプロイ」を参照してください。


会話型JWSファイルに関するプログラミングのガイドライン

次のサンプルでは、会話型Webサービスを実装する簡単なJWSファイルを示します。太字で示されたJavaコードに対応するコーディングのガイドラインについては、サンプルの後の説明を参照してください。

package examples.webservices.conversation;

import java.io.Serializable; 

import weblogic.jws.WLHttpTransport;
import weblogic.jws.Conversation; 
import weblogic.jws.Conversational; 
import weblogic.jws.Context; 
import weblogic.wsee.jws.JwsContext; 
import weblogic.wsee.jws.ServiceHandle; 

import javax.jws.WebService;
import javax.jws.WebMethod;
@Conversational(maxIdleTime="10 minutes", 
                maxAge="1 day", 
                runAsStartUser=false, 
                singlePrincipal=false ) 
@WebService(name="ConversationalPortType",
            serviceName="ConversationalService",
            targetNamespace="http://examples.org/")

@WLHttpTransport(contextPath="conv",
                 serviceUri="ConversationalService",
                 portName="ConversationalServicePort")

/**
 * Conversational web service.
 */

public class ConversationalServiceImpl implements Serializable { 

  @Context 
  private JwsContext ctx; 
  public String status = "undefined"; 

  @WebMethod
  @Conversation (Conversation.Phase.START) 
  public String start() {

    ServiceHandle handle = ctx.getService(); 
    String convID = handle.getConversationID(); 

    status = "start";
    return "Starting conversation, with ID " + convID + " and status equal to " + status;

  }

  @WebMethod
  @Conversation (Conversation.Phase.CONTINUE) 
  public String middle(String message) {

    status = "middle";
    return "Middle of conversation; the message is: " + message + " and status is " + status;

  }

  @WebMethod
  @Conversation (Conversation.Phase.FINISH) 
  public String finish(String message ) {

    status = "finish";
    return "End of conversation; the message is: " + message + " and status is " + status;

  }

}

会話型Webサービスを実装するJWSファイルをプログラミングする際には、次のガイドラインに従います。ガイドラインのコード・スニペットは、前述のサンプルでは太字で示されています。

  • 会話型Webサービスは、java.io.Serializableを実装している必要があるので、まずJWSファイルにクラスをインポートします。

    import java.io.Serializable;
    
  • 会話型JWSアノテーションをインポートします。

    import weblogic.jws.Conversation;
    import weblogic.jws.Conversational;
    
  • 会話型Webサービスの実行時情報にアクセスする場合は、@ContextアノテーションおよびコンテキストAPIをインポートします。

    import weblogic.jws.Context;
    
    import weblogic.wsee.jws.JwsContext;
    import weblogic.wsee.jws.ServiceHandle;
    
  • クラス・レベルの@Conversationalアノテーションを使用して、Webサービスが会話型であると指定します。このアノテーションはオプションですが(メソッド・レベルの@Conversationアノテーションを指定していると仮定した場合)、常にこれをJWSファイル内で使用して、Webサービスが会話型であると明確に指定することをお薦めします。

    指定するオプション属性は、WebLogic Serverが会話を終了するまでの、Webサービスがアイドル状態でいられる最大時間maxIdleTime、会話の最大存続期間maxAge、既存の会話の続行フェーズと終了フェーズを、会話を開始したユーザーとして実行するかどうかを示すrunAsStartUser、会話を開始したユーザー以外のユーザーが、会話の続行フェーズと終了フェーズの実行を許可されるかどうかを示すsinglePrincipalのうちのいずれかです。

    @Conversational(maxIdleTime="10 minutes",
                    maxAge="1 day",
                    runAsStartUser=false,
                    singlePrincipal=false )
    

    JWSファイルに@Conversationalアノテーションが含まれている場合、Webサービスのすべての操作が会話型です。操作のデフォルトのフェーズは、明示的に@Conversationアノテーションが指定されていなければ、続行フェーズです。ただし、会話型Webサービスには、1つ以上の開始操作と、1つの終了操作が含まれていることが必要なので、メソッド・レベルの@Conversationアノテーションを使用して、これらの操作をどのメソッドで実装するかを指定する必要があります

    属性のデフォルト値の詳細は、『Oracle WebLogic Server WebLogic Webサービス・リファレンス』のweblogic.jws.Conversationalに関する項を参照してください。

  • JWSファイルには、java.io.Serializableが実装されている必要があります。

    public class ConversationalServiceImpl implements Serializable {
    
  • Webサービスの実行時情報にアクセスするには、データ型weblogic.wsee.jws.JwsContextのプライベート・クラス変数に、フィールド・レベルの@Context JWSアノテーションを付けます。

     @Context
     private JwsContext ctx;
    
  • @Conversationアノテーションを使用して、会話の開始、続行、および終了フェーズを実装するメソッドを指定します。会話には、1つ以上の開始操作と、1つの終了操作が必要です。続行操作は任意指定です。フェーズを指定するアノテーションには、Conversation.Phase.STARTConversation.Phase.CONTINUE、またはConversation.Phase.FINISHの各パラメータを使用します。次の例では、開始操作を指定しています。

      @WebMethod
      @Conversation (Conversation.Phase.START)
      public String start() {...
    

    @ConversationアノテーションでマークするJWSファイルのメソッドが1つのみの場合、Webサービス全体が会話型となり、各操作は、会話の一部とみなされます。これは、JWSファイルで任意指定のクラス・レベル@Conversationalアノテーションを使用していなかった場合でも当てはまります。@Conversationで明示的にアノテーション付きにされていないメソッドはすべて、デフォルトで続行操作となります。つまり、たとえばクライアント・アプリケーションが、それ以前に開始操作を呼び出していない状態でこれらの続行メソッドの1つを呼び出すと、Webサービスが実行時エラーを返すということです。

    なお、スタンドアロンJavaクライアントから会話型Webサービスを呼び出す場合、開始操作はリクエスト-レスポンスであることが必要です。つまり、この操作に、@Oneway JWSアノテーションを付けることはできません。操作は、voidを返すことがあります。Webサービスを、WebLogic Serverで実行されているクライアント・アプリケーションからのみ呼び出す場合、この要件は適用されません。

    詳細は、『Oracle WebLogic Server WebLogic Webサービス・リファレンス』のweblogic.jws.Conversationに関する項を参照してください。

  • Webサービスの実行時情報を取得するには、JwsContextインスタンスを使用します。

    たとえば、開始操作の次のコードでは、WebLogic Serverが新しい会話に割り当てるIDを取得します。

    ServiceHandle handle = ctx.getService();
    String convID = handle.getConversationID();
    

会話型Webサービスを呼び出すJWSファイルに関するプログラミングのガイドライン

次の例では、「会話型JWSファイルに関するプログラミングのガイドライン」で説明した会話型Webサービスを呼び出すWebサービス用の簡単なJWSファイルを示します。太字で示されたJavaコードに対応するコーディングのガイドラインについては、サンプルの後の説明を参照してください。

package examples.webservices.conversation;

import weblogic.jws.WLHttpTransport;
import weblogic.jws.ServiceClient; 

import weblogic.wsee.conversation.ConversationUtils; 

import javax.jws.WebService;
import javax.jws.WebMethod;

import javax.xml.rpc.Stub;

import examples.webservices.conversation.ConversationalPortType; 

import java.rmi.RemoteException;

@WebService(name="ConversationalClientPortType",
            serviceName="ConversationalClientService",
            targetNamespace="http://examples.org/")

@WLHttpTransport(contextPath="convClient",
                 serviceUri="ConversationalClient",
                 portName="ConversationalClientPort")

/**
 *  client that has a conversation with the ConversationalService.
 */

public class ConversationalClientImpl {

  @ServiceClient( 
     wsdlLocation="http://localhost:7001/conv/ConversationalService?WSDL", 
     serviceName="ConversationalService", 
     portName="ConversationalServicePort") 
 
  private ConversationalPortType port; 

  @WebMethod
  public void runConversation(String message) {

    try {

      // Invoke start operation
      String result = port.start(); 
      System.out.println("start method executed.");
      System.out.println("The message is: " + result);

      // Invoke continue operation
      result = port.middle(message ); 
      System.out.println("middle method executed.");
      System.out.println("The message is: " + result);

      // Invoke finish operation
      result = port.finish(message ); 
      System.out.println("finish method executed.");
      System.out.println("The message is: " + result);
      ConversationUtils.renewStub((Stub)port); 

    }
    catch (RemoteException e) {
      e.printStackTrace();
    }

  }

}

会話型Webサービスを呼び出すJWSファイルをプログラミングする際には、次のガイドラインに従います。ガイドラインのコード・スニペットは、前述のサンプルでは太字で示されています。

  • @ServiceClient JWSアノテーションをインポートします。

    import weblogic.jws.ServiceClient;
    
  • 必要に応じて、会話をさらに構成するためのWebLogicユーティリティ・クラスをインポートします。

    import weblogic.wsee.conversation.ConversationUtils;
    
  • 呼び出す会話型Webサービスのポート・タイプのJAX-RPCスタブをインポートします。実際のスタブそのものは、後でjwsc Antタスクによって作成されます。スタブ・パッケージは、<jws>の子要素<clientgen>packageName属性によって指定され、スタブの名前は呼び出されたWebサービスのWSDLによって決まります。

    import examples.webservices.conversation.ConversationalPortType;
    
  • JWSファイルの本文で、@ServiceClient JWSアノテーションを使用して、呼び出す会話型WebサービスのWSDL、名前、およびポートを指定します。このアノテーションは、プライベート変数のフィールド・レベルで指定します。この変数のデータ型は、呼び出しているWebサービスのJAX-RPCポート・タイプとなります。

    @ServiceClient(
         wsdlLocation="http://localhost:7001/conv/ConversationalService?WSDL",
         serviceName="ConversationalService",
         portName="ConversationalServicePort")
    
      private ConversationalPortType port;
    
  • @ServiceClientアノテーションを付けたスタブを使用して、会話型Webサービスの開始操作を呼び出し、会話を開始します。開始メソッドは、JWSファイルの任意の場所(コンストラクタ、メソッドなど)から呼び出せます。

    String result = port.start();
    
  • 必要に応じて続行メソッドを呼び出し、会話を続行します。開始したものと同じ会話が続行されるよう、必ず同じスタブ・インスタンスを使用します。

    result = port.middle(message );
    
  • 会話が完了したら、会話型Webサービスが現在の会話に使用していたリソースを解放できるよう、終了操作を呼び出します。

    result = port.finish(message );
    
  • Webサービスの会話スタブを再利用して新しい会話を開始するには、weblogic.wsee.conversation.ConversationUtilsユーティリティ・クラスのrenewStub()メソッドを使用して、そのスタブを明示的に更新する必要があります。

    ConversationUtils.renewStub((Stub)port);
    

    注意:

    会話型Webサービスを呼び出すクライアントWebサービスは、必ずしも会話型にする必要はありません。ただし、クライアントが会話型でない場合は、このクライアントの複数のインスタンスが同じ会話型Webサービス・スタブにアクセスする危険性があり、保存されている会話状態が破損するおそれがあります。この危険性があると判断した場合は、クライアントWebサービスも会話型になるように指定してください。

ConversationUtilsユーティリティ・クラス

WebLogic Serverには、会話機能で使用するユーティリティ・クラスが用意されています。このクラスを使用すると、会話IDの取得や設定、構成オプションの設定といった一般的なタスクを実行できます。これらのタスクには、会話型Webサービス内で実行するものと、会話型Webサービスを呼び出すクライアント内で実行するものがあります。このクラスの使用例については、「会話型Webサービスを呼び出すJWSファイルに関するプログラミングのガイドライン」を参照してください。

詳細は、Oracle WebLogic Server Java APIリファレンスweblogic.wsee.conversation.ConversationUtilsに関する項を参照してください。

会話型Webサービスのクライアント用build.xmlファイルの更新

build.xmlファイルを更新して、会話型Webサービスを呼び出すJWSファイルを生成するには、次のサンプルのようなtaskdefsおよびbuild-clientServiceターゲットを追加します。詳細は、サンプルの後の説明を参照してください。

<taskdef name="jwsc"
    classname="weblogic.wsee.tools.anttasks.JwscTask" />

  <target name="build-clientService">

    <jwsc
        enableAsyncService="true"
        srcdir="src"
        destdir="${clientService-ear-dir}" >

        <jws file="examples/webservices/conversation/ConversationalClientImpl.java" >
          <clientgen 
            wsdl="http://${wls.hostname}:${wls.port}/conv/ConversationalService?WSDL" 
            packageName="examples.webservices.conversation"/> 

        </jws>

    </jwsc>

  </target>

jwsc Antタスクの完全なクラス名を定義するには、taskdef Antタスクを使用します。

クライアントWebサービスをコンパイルするjwsc Antタスクを更新して、<jws>要素の<clientgen>子要素を含めます。これにより、デプロイされたConversationalService WebサービスのJAX-RPCスタブが生成およびコンパイルされるようになります。jwsc Antタスクでは、これらのスタブが生成されたWARファイルに自動的にパッケージ化されるため、即座にクライアントWebサービスからアクセスできるようになります。このようにするのは、生成されたクラスの1つをConversationalClientImpl JWSファイルでインポートして使用するためです。

会話型Webサービスを呼び出すためのスタンドアロンJavaクライアントの更新

次の例では、「会話型JWSファイルに関するプログラミングのガイドライン」で説明した会話型Webサービスを呼び出す、簡単なスタンドアロンのJavaクライアントを示します。太字で示されたJavaコードに対応するコーディングのガイドラインについては、サンプルの後の説明を参照してください。

package examples.webservices.conv_standalone.client;

import java.rmi.RemoteException;

import javax.xml.rpc.ServiceException;
import javax.xml.rpc.Stub;
import weblogic.wsee.jaxrpc.WLStub; 

/**
 * stand-alone client that invokes and converses with ConversationlService.
 */

public class Main {

  public static void main(String[] args)
      throws ServiceException, RemoteException{

      ConversationalService service = new ConversationalService_Impl(args[0] + "?WSDL");
      ConversationalPortType port = service.getConversationalServicePort();

      // Set property on stub to specify that client is invoking a web service
      // that uses advanced features; this property is automatically set if
      // the client runs in a WebLogic Server instance.

      Stub stub = (Stub)port; 
      stub._setProperty(WLStub.COMPLEX,  "true"); 

      // Invoke start operation to begin the conversation
      String result = port.start(); 
      System.out.println("start method executed.");
      System.out.println("The message is: " + result);

      // Invoke continue operation
      result = port.middle("middle" ); 
      System.out.println("middle method executed.");
      System.out.println("The message is: " + result);

      // Invoke finish operation
      result = port.finish("finish" ); 
      System.out.println("finish method executed.");
      System.out.println("The message is: " + result);

  }

}

会話型Webサービスを呼び出すスタンドアロンJavaクライアントをプログラミングする際には、次のガイドラインに従います。ガイドラインのコード・スニペットは、前述のサンプルでは太字で示されています。

  • weblogic.wsee.jaxrpc.WLStubクラスをインポートします。

    import weblogic.wsee.jaxrpc.WLStub;
    
  • _setPropertyメソッドを使用して、WLStub.ComplexプロパティをConversationalServiceのJAX-RPCスタブに対して設定します。

    Stub stub = (Stub)port;
    stub._setProperty(WLStub.COMPLEX,  "true");
    

    このプロパティは、クライアントが高度なWebサービス(この場合は会話型Webサービス)を呼び出すことをWebサービスのランタイムに対して指定します。このプロパティは、別のWebLogic Webサービスから会話型Webサービスを呼び出している場合には、自動的に設定されます。

  • 会話型Webサービスの開始操作を呼び出して、会話を開始します。

          String result = port.start();
    
  • 必要に応じて続行メソッドを呼び出し、会話を続行します。

    result = port.middle(message );
    
  • 会話が完了したら、会話型Webサービスが現在の会話に使用していたリソースを解放できるよう、終了操作を呼び出します。

    result = port.finish(message );
    

会話型Webサービスの.NETクライアントの例

この項では、会話型WebLogic Webサービスの.NET WSE3.0クライアントを作成する方法について説明します。この例には次のファイルが含まれています。

  • ConversationService.java -- @Conversationおよび@Callbackアノテーションを使用して会話型Webサービスを実装するJWSファイル。ConversationService.javaは、コールバックを介してクライアントに結果を通知することもできます(オプション)。

  • Service.cs -- ConversationService Webサービスのクライアントとして機能するConversationClient .NET WebサービスのC#ソース・ファイル。

    このサンプル.NETクライアントは、ConversationServiceとの会話に参加できます。また、コールバックを介して結果を受け取ることも可能です。

  • build.xml -- 会話型Webサービスをビルドおよびデプロイするためのターゲットを含むAntビルド・ファイル。

これらのファイルについては、以降の項で詳しく説明します。

ConversationService.javaファイル

サンプルのConversationService.javaファイルを例9-1に示します。このサンプルには、機能を説明するコメントが大量に含まれています。

例9-1 ConversationService.javaファイル

package conv;
 
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.Oneway;
 
import weblogic.jws.Conversation;
import weblogic.jws.Callback;
import weblogic.jws.CallbackService;
 
import java.io.Serializable;
 
/**
 * Demonstrates use of the @Conversation annotation to manage the lifetime of the service
 * and provide data persistence and message correlation.
 * 
 * Remember that multiple clients might invoke a web service simultaneously.  When the
 * web service stores data relevant to the client or calls additional services
 * in order to process a client's request, the service must be able to process returned
 * data from the external services in the context of the specific client it relates
 * to.  This is all automatic when conversations are used.
 * 
 * Remember that not all clients are capable of accepting callbacks.  Specifically,
 * clients operating from behind firewalls may not be able to receive asynchronous
 * callbacks.  You may wish to provide a synchronous interface, like this one,
 * for such clients.  If a client can accept callbacks, it must send a callback endpoint refrence
 * as part of any "start conversation" method invocation.
 * 
 * To see the behavior in the Test View, invoke startRequest and then getRequestStatus
 * several times quickly.
 */
@WebService(serviceName = "ConversationService", portName = "conversation", targetNamespace = "http://www.openuri.org/")
public class ConversationService implements Serializable {
 
  @Callback
  public CallbackInterface callback;
  private boolean useCallbacks;
  private int num;
 
  /**
   * Starts the conversation
   */
  @Conversation(Conversation.Phase.START)
  @WebMethod
  public void startRequest(boolean useCallbacks) {
    this.useCallbacks = useCallbacks;
  }
 
  @WebMethod
  @Conversation(Conversation.Phase.CONTINUE)
  public String getRequestStatus() {
 
    num++;
    if (num == 1)
      return "This is the first time you call getRequestStatus method.";
    if (num == 2 && useCallbacks) {
      callback.onResultReady("finished");
      return "This is the second time you call  getRequestStatus method, the conversation has been terminated automtically when the onResultReady callback method is invoked.";
    } else
      return "You have called getRequestStatus method " + num + " times";
 
  }
 
  /**
   * Used to tell Conversation.jws that the current conversation is
   * no longer needed.
   */
  @WebMethod
  @Conversation(Conversation.Phase.FINISH)
  public void terminateRequest() {
 
  }
 
  @CallbackService(serviceName = "ConversationCallbackService")
  public interface CallbackInterface {
 
    /**
     * Callback to invoke on the client when the external service
     * returns its result. Will only be called it the client can
     * accept callbacks and told us where to send them.
     * <p/>
     * If this callback is used, it implicitly terminates the
     * conversation with no action required on the part of the
     * client.
     */
    @WebMethod
    @Oneway
    @Conversation(Conversation.Phase.FINISH)
    public void onResultReady(String result);
  }
 
}

Service.csファイル

サンプルのService.csファイルを例9-2に示します。

この会話プロキシ・ファイルは、Microsoft WSDL to Proxy ClassツールWseWsdl3.exe(http://msdn.microsoft.com/en-us/library/aa529578.aspxを参照)とConversationService WebサービスのWSDLファイルを使用して作成したものです。

このサンプルには、機能を説明するコメントが大量に含まれています。

例9-2 Service.csファイル

using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Diagnostics;
using System.IO;
using System.Xml;
using Microsoft.Web.Services3.Addressing;
using Microsoft.Web.Services3;
using System.Collections.Generic;
using Microsoft.Web.Services3.Design;
 
 
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Service : System.Web.Services.WebService
{
    
    public Service () {
 
        //Uncomment the following line if using designed components 
        //InitializeComponent(); 
    }
 
    /*
    * start invokes the Conversation web service's startRequest
    * operation.
    * 
    * Since the Conversation web service is conversational,
    * we must also persist the ReplyTo endpoint reference SOAP header
    * for subsequent calls.
    * 
    * Since the Conversation web service can optionally communicate
    * the result of it's work via a callback, we must prepare a
    * second SOAP header CallbackTo SOAP header, which is the endpoint reference 
    * of the recipient to which callbacks should be sent.
    */
    [WebMethod(EnableSession = true)]
    public void start(Boolean useCallbacks, Boolean useIPAddress)
    {
        /*
         * The Conversation proxy was created using .NET WSE3.0's WseWsdl3.exe
         * application and the Conversation.jws's WSDL file.  The WSDL
         * file for any WLS web service may be obtained
         * by hitting the web service's URL with "?WSDL" appended to
         * the end.  For example:
         * 
         * http://somehost:7001/samples/async/Conversation.jws?WSDL
         * 
         * WseWsdl3.exe produces a C# proxy class.  Place the resulting
         * ConversationService.cs file in your .NET project, then use Visual
         * Studio's Project->Add Existing Item menu action to "import"
         * the class into the project.
         */
        ConversationServiceSoapBinding conv;
        String callbackLocation;
        int asmxIndex;
 
        /*
         * Construct the callback URL from various pieces of
         * server and HttpRequest info.
         */
        Uri requestUrl = Context.Request.Url;
 
        if (useIPAddress)
        {
            /*
             * if useIPAddress is true, construct the callback address
             * with the IP address of this host.
             */
            callbackLocation = requestUrl.Scheme + "://" + System.Net.Dns.GetHostByName(requestUrl.Host).AddressList[0] +
                ":" + requestUrl.Port + requestUrl.AbsolutePath;
        }
        else
        {
            /*
             * if useIPAddress is false, construct the callback address
             * with the hostname of this host.
             */
            callbackLocation = requestUrl.Scheme + "://" + requestUrl.Host +
                ":" + requestUrl.Port + requestUrl.AbsolutePath;
        }
 
        // Remove everything after ".asmx"
        asmxIndex = callbackLocation.IndexOf(".asmx") + 5;
        callbackLocation = callbackLocation.Remove(asmxIndex,
                                 callbackLocation.Length - asmxIndex);
 
        /*
         * Create an instance of the proxy for the Conversation
         * web service.
         * 
         */
        conv = new ConversationServiceSoapBinding();
 
        
        /*
         * When callback is enabled, a custom callback header should be added into 
         * the outbound soap message. 
         * 
         */
        if (useCallbacks)
            enableSoapFilterToAddCallbackHeader(conv, callbackLocation);
        
    
        /*
         * Invoke the startRequest method of the web service. The
         * single boolean parameter determines whether the Conversation
         * web service will use callbacks to communicate the result
         * back to this client.
         * 
         * If the argument is true, an onResultReady callback will
         * be sent when the result is ready.  This client must implement
         * a method with that name that expects the message shape defined
         * by the target web service (returns void and accepts a single
         * string argument).  See the onResultReady method below.
         * 
         * If the argument to startRequest is false, callbacks will not
         * be used and this client must use the getRequestStatus method
         * to poll the Conversation web service for the result.
         */
        conv.startRequest(useCallbacks);
        /*
         * Persist the ReplyTo header  in session state so that it can
         * be used in other methods that take part in the conversation.
         * 
         * This is not safe since one session could start multiple
         * conversations, but there is no other apparent way to persist
         * this information.  Member variables of WebService classes
         * are not persisted across method invocations.
         */
        Session["ConversationReplyTo"] = conv.ResponseSoapContext.Addressing.ReplyTo;
 
 
    }
 
    /* the CallbackTo header defintion isn't exposed by WLS9x/WLS10x callback service. 
     * So we need to use SOAPFilter to add the CallbackTo header. 
     * 
     */
    private static void enableSoapFilterToAddCallbackHeader(ConversationServiceSoapBinding conv, String callbackLocation)
    {
        //Create a custom policy.
        Policy myPolicy = new Policy();
        // Create a new policy assertion
        MyPolicyAssertion myAssertion = new MyPolicyAssertion(callbackLocation);
        // Add the assertion to the policy
        myPolicy.Assertions.Add(myAssertion);
        //Set the custom policy you have created on the client proxy 
        conv.SetPolicy(myPolicy);
    }
 
     /*
     * getStatus invokes Conversation's getRequestStatus method.
     * getRequestStatus is a polling method that is an alternative
     * for web services that cannot recieve callbacks.
     * 
     * Note that a conversation must be started with startRequest before
     * this method may be invoked.  If not, or if this method is invoked
     * outside of a conversation for any reason, it will get back a SOAP
     * fault indicating that the conversation does not exist.
     */
    [WebMethod(EnableSession = true)]
    public String getStatus()
    {
        String result;
 
        /*
         * Create an instance of the proxy for the Conversation
         * web service.  We could probably persist the proxy instance
         * in session state, but chose not to.
         */
        ConversationServiceSoapBinding conv = new ConversationServiceSoapBinding();
 
        /*
         * change the destination to the ReplyTo endpoint reference we cached on session state in
         * the start method.
         */
        conv.RequestSoapContext.Addressing.Destination = (EndpointReference)Session["ConversationReplyTo"];
        /* 
         * Invoke the getRequestStatus method of the web service.
         */
        result = conv.getRequestStatus();
        return result;
    }
 
    /*
     * finish invokes Conversation's terminateRequest method, which
     * terminates the current conversation.
     * 
     * Note that a conversation must be started with startRequest before
     * this method may be invoked.  If not, or if this method is invoked
     * outside of a conversation for any reason, it will get back a SOAP
     * fault indicating that the conversation does not exist.
     */
    [WebMethod(EnableSession = true)]
    public void finish()
    {
        /*
         * Create an instance of the proxy for the Conversation
         * web service.  We could probably persist the proxy instance
         * in session state, but chose not to.
         */
        ConversationServiceSoapBinding conv = new ConversationServiceSoapBinding();
 
        /*
         * change the destination to the ReplyTo endpoint reference we cached on session state in
         * the start method. Both "continue" and "finish" methods use the same destination.
         */
        conv.RequestSoapContext.Addressing.Destination = (EndpointReference)Session["ConversationReplyTo"];
        /* 
         * Invoke the terminateRequest method of the web service.
         */
        conv.terminateRequest();
    }
 
    /*
     * onResultReady is a callback handler for the onResultReady
     * callback that Conversation.jws can optionally use to return
     * its results.
     * 
     * .NET WSE3.0 does not support callbacks directly, but a callback is just
     * a method invocation message.  So if you construct a WebMethod with
     * the same signature as the callback and set the XML namespace
     * properly, it serves as a callback handler.
     * 
     */
    [WebMethod]
    [SoapDocumentMethod(OneWay = true, 
        Action = "http://www.openuri.org/ConversationService_CallbackInterface/onResultReady", 
        RequestElementName = "http://www.openuri.org/", 
        ResponseNamespace = "http://www.openuri.org/"
        )]
    public void onResultReady(String result)
    {
        /*
         * When the callback is invoked, log a message to the
         * hardcoded file c:\temp\ConversationClient.log.
         * 
         * Note: if c:\temp does not exist on this server, an
         * Exception will be raised.  Since it is not handled here,
         * it will be returned as a SOAP fault to the Conversation
         * web service.
         */
        TextWriter output;
        output = File.AppendText("c:\\temp\\ConversationClient.log");
        String msg = "[" + DateTime.Now.ToString() + "] callback received";
        output.WriteLine(msg);
        output.Flush();
        output.Close();
    }
 
    
}
 
public class MyFilter : Microsoft.Web.Services3.SoapFilter
{
    private String callbackLocation;
 
    public MyFilter(String callbackLocation)
    {
        this.callbackLocation = callbackLocation;
    }
    
    public override SoapFilterResult ProcessMessage(SoapEnvelope envelope)
    {
 
       //create the CallbackTo soap element.
       XmlDocument xmldoc = new XmlDocument();
       XmlElement xmlEle = xmldoc.CreateElement("callback", "CallbackTo", "http://www.openuri.org/2006/03/callback");
       
       //create the CallbackTo endpoint reference.
       Address callbacto = new Address(new Uri(callbackLocation));      
       XmlElement xmlEle2 =new EndpointReference(callbacto).GetXml(xmldoc);
       //add the CallbackTo endpoint reference into CallbackTo SOAP element.
       xmlEle.AppendChild(xmlEle2.FirstChild);
       //add the whole CallbackTo SOAP element into SOAP header. 
       XmlNode callbackheader = envelope.ImportNode(xmlEle, true);
       envelope.DocumentElement.FirstChild.AppendChild(callbackheader);
       return SoapFilterResult.Continue;
 
    }
}
 
public class MyPolicyAssertion : Microsoft.Web.Services3.Design.PolicyAssertion
{
    private String callbackLocation;
 
    public MyPolicyAssertion(String callbackLocation)
    {
        this.callbackLocation = callbackLocation;
    }
    
    public override  SoapFilter CreateClientInputFilter(FilterCreationContext context)
    {
        return null;
    }
 
 
    public override  SoapFilter CreateClientOutputFilter(FilterCreationContext context)
    {
        //use MyFilter to add the CallbackTo header in the outbound soap message. 
        return new MyFilter(callbackLocation);
    }
    
    public override  SoapFilter CreateServiceInputFilter(FilterCreationContext context)
    {
        return null;
    }
    
    public override  SoapFilter CreateServiceOutputFilter(FilterCreationContext context)
    {
        return null;
    }
 
}

build.xmlファイル

サンプルのbuild.xmlファイルを例9-3に示します。

build.xmlは、サンプル・ソース・ファイルを新しいEXAMPLES_HOME\wl_server\examples\src\examples\webservices\convディレクトリにコピーすることを前提としています。EXAMPLES_HOMEは、WebLogic Serverのサンプル・コードが構成されているディレクトリを示します。WebLogic Serverのサンプル・コードの詳細は、『Oracle WebLogic Serverの理解』のサンプル・アプリケーションとサンプル・コードに関する項を参照してください。

build.xmlを使用するには、まずORACLE_HOME\user_projects\domains\wl_server>setExamplesEnv.cmd(shを実行してサンプルの環境を正しく設定し、サンプルのサーバーを開始しておくことも必要です。

このサンプルには、ビルド・ファイルの機能を説明するコメントとターゲットが含まれています。

例9-3 build.xmlファイル

<?xml version="1.0" encoding="ISO-8859-1"?>
<project name="webservices.conversation" default="all" basedir=".">
 
  <!-- set global properties for this build -->
  <property file="../../../examples.properties"/>
 
  <property name="client.dir" value="${client.classes.dir}/webservices_conversation" />
  <property name="package.dir" value="examples/webservices/conv"/>
  <property name="package" value="examples.webservices.conv"/>
  <property name="ear.dir" value="${examples.build.dir}/webservicesConversationEar" />
 
  <path id="client.class.path">
    <pathelement path="${java.class.path}"/>
  </path>
 
  <!-- Web service WLS Ant task definitions -->
  <taskdef name="jwsc"
    classname="weblogic.wsee.tools.anttasks.JwscTask" />
  <taskdef name="clientgen"
      classname="weblogic.wsee.tools.anttasks.ClientGenTask" />
 
  <target name="all" depends="build, deploy"/>
 
 
  <target name="clean">
    <delete dir="${ear.dir}"/>
  </target>
 
  <!-- Target that builds the conversational  web service -->
  <target name="build" description="Target that builds the MTOM web service">
    <jwsc
      srcdir="${examples.src.dir}/${package.dir}"
      sourcepath="${examples.src.dir}"
      destdir="${ear.dir}"
      classpath="${java.class.path}"
      keepGenerated="true"
      deprecation="${deprecation}"
      debug="${debug}">
        <jws file="ConversationService.java">
                <WLHttpTransport contextPath="/samples/async" serviceURI="conversation.jws"/>
                </jws>
    </jwsc>
  </target>
 
  
 
  <!-- Target that deploys the conversational web service -->
  <target name="deploy" description="Target that deploys the conversational web service">
    <wldeploy
      action="deploy"
      source="${ear.dir}"
      user="${wls.username}"
      password="${wls.password}"
      verbose="true"
      adminurl="t3://${wls.hostname}:${wls.port}"
      targets="${wls.server.name}"
      failonerror="${failondeploy}"/>
  </target>
 
  <!-- Target that undeploys the conversational web service -->
  <target name="undeploy" description="Target that deploys the conversational web service">
    <wldeploy
      action="undeploy"
      name="webservicesConversationEar"
      user="${wls.username}"
      password="${wls.password}"
      verbose="true"
      adminurl="t3://${wls.hostname}:${wls.port}"
      targets="${wls.server.name}"
      failonerror="${failondeploy}"/>
  </target>
 
</project>

会話型Webサービスを再デプロイする際にクライアント側で考慮すべき事項

WebLogic Serverでは、本番再デプロイメントがサポートされています。つまり、会話型WebLogic Webサービスの更新後の新しいバージョンを、同じWebサービスの古いバージョンと並行してデプロイできます。

WebLogic Serverでは、新しいクライアントのリクエストのみが新しいバージョンに転送されるように、クライアント接続が自動的に管理されます。再デプロイメント時にすでにWebサービスに接続していたクライアントは、作業が完了するまで古いバージョンのサービスを使用し続け、それらの作業が完了した時点で、WebLogic Serverが古いWebサービスを自動的にリタイアします。クライアントが会話型Webサービスに接続されている場合は、既存の会話がクライアントによって明示的に終了されるか、またはタイムアウトになったときに、そのクライアントの作業が完了したとみなされます。

本番再デプロイメントおよびWebサービス・クライアントに関する追加情報は、Webサービスを再デプロイする際にクライアント側で考慮すべき事項に関する項を参照してください。