C でのメッセージ処理ノードまたは出力ノードの作成

メッセージ処理ノードは何らかの形でメッセージを処理するために使用され、出力ノードはメッセージをビット・ストリームとして出力するために使用されます。

始める前に

メッセージ処理ノードまたは出力ノードをコーディングする場合、ノードは基本的に同じサービスを提供します。出力ノードでメッセージ処理を実行することもできますし、メッセージ処理ノードを使用してメッセージをビット・ストリームとして出力することもできます。 このトピックでは、簡略化のために主にメッセージ処理ノードを指して「ノード」という語を使用していますが、両方のタイプのノードの機能に関する情報も取り上げています。

ロード可能インプリメンテーション・ライブラリー (LIL) は、C ノードのインプリメンテーション・モジュールです。 LIL は共用ライブラリーまたはダイナミック・リンク・ライブラリー (DLL) としてインプリメントされますが、ファイル拡張子は .dll ではなく .lil です。

ノード用に作成する C ノード・インプリメンテーション関数の詳細については、C ノード・インプリメンテーション関数を参照してください。ランタイム・ブローカーにインプリメントされた C ノード・ユーティリティー関数を呼び出して、ノード操作に役立てることもできます。C ノード・ユーティリティー関数を参照してください。

WebSphere® Message Broker では、SwitchNode および TransformNode という名前の 2 つのサンプル・ユーザー定義ノードのソースが準備されています。これらのノードは現行の状態で使用することもできますし、変更を加えてもかまいません。 また、ユーザー定義ノード (C で作成されたメッセージ処理ノードを含む) の使用法を例示する次のサンプルを表示することができます。 サンプルは、Message Brokers Toolkit と統合されているインフォメーション・センターを使用する場合にのみ表示できます。

ノードの宣言および定義

ブローカーに対してユーザー定義ノードを宣言して定義するには、初期化関数 bipGetMessageflowNodeFactory を LIL に含めます。 以下のステップは、構成スレッド上で行われ、ブローカーが初期化関数をどのようにして呼び出し、初期化関数がユーザー定義ノードをどのようにして宣言および定義するかについての概要です。

  1. LIL がオペレーティング・システムによってロード、初期化された後、ブローカーは初期化関数 bipGetMessageflowNodeFactory を呼び出します。ブローカーは、 LIL が実行できる事柄と、ブローカーが LIL を呼び出す方法を理解するために、この関数を呼び出します。以下に例を示します。
    CciFactory LilFactoryExportPrefix * LilFactoryExportSuffix
    bipGetMessageflowNodeFactory()
  2. bipGetMessageflowNodeFactory 関数はユーティリティー関数 cniCreateNodeFactory を呼び出す必要があります。 この関数は、LIL がサポートするすべてのノードのファクトリー名 (またはグループ名) を戻します。 ファクトリー名 (またはグループ名) は、単一のランタイム・ブローカー内のすべての LIL で 固有でなければなりません。
  3. LIL はユーティリティー関数 cniDefineNodeClass を呼び出して、 各ノードの固有の名前と、インプリメンテーション関数のアドレスの仮想関数表を渡す必要があります。
    例えば、 以下のコードは MessageProcessingxNode という単一ノードの宣言と定義を行います。
    {
    	CciFactory* factoryObject;
    	int rc = 0;
    	CciChar factoryName[] = L"MyNodeFactory";
    	CCI_EXCEPTION_ST exception_st;
    
    	/* Create the Node Factory for this node */
    	factoryObject = cniCreateNodeFactory(0, factoryName);
    	if (factoryObject == CCI_NULL_ADDR) {
    		/* Any local error handling can go here */
    	}
    	else {
    		/* Define the nodes supported by this factory */
    	static CNI_VFT vftable = {CNI_VFT_DEFAULT};
    
    	/* Setup function table with pointers to node implementation functions */
    	vftable.iFpCreateNodeContext = _createNodeContext;
    	vftable.iFpDeleteNodeContext = _deleteNodeContext;
    	vftable.iFpGetAttributeName2 = _getAttributeName2;
    	vftable.iFpSetAttribute      = _setAttribute;
    	vftable.iFpGetAttribute2     = _getAttribute2;
    	vftable.iFpEvaluate          = _evaluate;
    
    	cniDefineNodeClass(0, factoryObject, L"MessageProcessingxNode", &vftable);
    
    	}
    
    	/* Return address of this factory object to the broker */
    	return(factoryObject);
    }

    ユーザー定義ノードは、cniEvaluate 関数をインプリメントすることによって、メッセージ処理ノードまたは出力ノードとしての役割を果たします。 ユーザー定義ノードは、cniEvaluate インプリメンテーション関数か cniRun インプリメンテーション関数のいずれかをインプリメントしていなければなりません。そうでないと、ブローカーはそのユーザー定義ノードをロードせず、cniDefineNodeClass ユーティリティー関数が失敗して、 CCI_MISSING_IMPL_FUNCTION が戻されます。

    ユーザー定義のメッセージ処理ノードが含まれたメッセージ・フローが 正常にデプロイされていれば、ノードに伝搬される各メッセージごとに、このノードの cniEvaluate 関数が 呼び出されます。

    メッセージ・フロー・データ (つまり、メッセージ、Environment、LocalEnvironment、および ExceptionList) はノードの入力ターミナルで受信されます。

    以下に例を示します。
    void cniEvaluate(
      CciContext* context,
      CciMessage* localEnvironment,
      CciMessage* exceptionList,
      CciMessage* message
    ){                                    
      ...
    }
    C ユーザー定義ノードのコンパイルに必要な最小コードについては、C のスケルトン・コードを参照してください。

ノードのインスタンスの作成

ノードをインスタンス化するには、次のようにします。

  1. ブローカーは、関数ポインターの表を受信すると、 ユーザー定義ノードの各インスタンス化ごとに関数 cniCreateNodeContext を呼び出します。 例えば、3 つのメッセージ・フローがユーザー定義ノードを使用する場合、3 つのそれぞれに対して cniCreateNodeContext 関数が呼び出されます。 この関数は、そのユーザー定義ノードのインスタンス化用に、 構成された属性の値を保持するためのメモリーを割り振ります。 以下に例を示します。
    1. ユーザー関数 cniCreateNodeContext が呼び出されます。
      CciContext* _Switch_createNodeContext(
        CciFactory* factoryObject,
        CciChar*    nodeName,
        CciNode*    nodeObject
      ){
        static char* functionName = (char *)"_Switch_createNodeContext()";
        NODE_CONTEXT_ST* p;
        CciChar          buffer[256];
      
    2. ローカル・コンテキストにポインターを割り振り、コンテキスト・エリアを消去します。
        p = (NODE_CONTEXT_ST *)malloc(sizeof(NODE_CONTEXT_ST));
      
        if (p) {
           memset(p, 0, sizeof(NODE_CONTEXT_ST));
    3. コンテキストの中のノード・オブジェクト・ポインターを保存します。
         p->nodeObject = nodeObject;
    4. ノード名を保存します。
       CciCharNCpy((CciChar*)&p->nodeName, nodeName, MAX_NODE_NAME_LEN);
    5. ノード・コンテキストを戻します。
      return (CciContext*) p;
  2. ブローカーが適切なユーティリティー関数を呼び出して、 ノードの入力ターミナルと出力ターミナルについての情報を検出します。 ノードには、 いくつかの入力ターミナルと出力ターミナルが関連付けられています。 ユーザー関数 cniCreateNodeContext 内で、cniCreateInputTerminal および cniCreateOutputTerminal への呼び出しがなされ、ユーザー・ノードのターミナルが定義される必要があります。 これらの関数は、cniCreateNodeContext インプリメンテーション関数内で開始してください。 例えば、1 つの入力ターミナルと 2 つの出力ターミナルを持つノードを定義するには、 次のようにします。
        {
          const CciChar* ucsIn = CciString("in", BIP_DEF_COMP_CCSID) ;
          insInputTerminalListEntry(p, (CciChar*)ucsIn);
          free((void *)ucsIn) ;
        }
        {
          const CciChar* ucsOut = CciString("out", BIP_DEF_COMP_CCSID) ;
          insOutputTerminalListEntry(p, (CciChar*)ucsOut);
          free((void *)ucsOut) ;
        }
        {
          const CciChar* ucsFailure = CciString("failure", BIP_DEF_COMP_CCSID) ;
          insOutputTerminalListEntry(p, (CciChar*)ucsFailure);
          free((void *)ucsFailure) ;
        }

    前述のコードは insInputTerminalListEntry および insOutputTerminalListEntry 関数を開始します。これらの関数は Common.c サンプル・コードにあります。サンプル・ノード・ファイル を参照してください。 これらの関数は、ターミナルをブローカーに定義し、ターミナルへのハンドルを保管します。ハンドルは、CciContext* 内で戻される値によって参照される構造内に保管されます。CciContext は他のインプリメンテーション関数に渡されるため、それらのインプリメンテーション関数 (例えば CciEvaluate) 内から、ノードはターミナル・ハンドルにアクセスできます。

    以下のコードは、insInputTerminalListEntry の定義を示しています。

    TERMINAL_LIST_ENTRY *insInputTerminalListEntry( 
      NODE_CONTEXT_ST* context, 
      CciChar*         terminalName 
    ){ 
      static char* functionName = (char *)"insInputTerminalListEntry()"; 
      TERMINAL_LIST_ENTRY* entry; 
      int             rc;
     
      entry = (TERMINAL_LIST_ENTRY *)malloc(sizeof(TERMINAL_LIST_ENTRY)); 
      if (entry) { 
     
        /* This entry is the current end of the list */ 
        entry->next = 0; 
     
        /* Store the terminal name */ 
        CciCharCpy(entry->name, terminalName); 
     
        /* Create terminal and save its handle */ 
        entry->handle = cniCreateInputTerminal(&rc, context->nodeObject, (CciChar*)terminalName); 
     
        /* Link an existing previous element to this one */ 
        if (context->inputTerminalListPrevious) context->inputTerminalListPrevious->next = entry; 
        else if ((context->inputTerminalListHead) == 0) context->inputTerminalListHead = entry; 
     
        /* Save the pointer to the previous element */ 
        context->inputTerminalListPrevious = entry; 
      } 
      else {
        /* Error: Unable to allocate memory */
      } 
     
      return(entry); 
    } 

    以下の例は、insOutputTerminalListEntry のコードを示しています。

    TERMINAL_LIST_ENTRY *insOutputTerminalListEntry( 
      NODE_CONTEXT_ST* context, 
      CciChar*         terminalName 
    ){ 
      static char* functionName = (char *)"insOutputTerminalListEntry()"; 
      TERMINAL_LIST_ENTRY* entry; 
      int             rc;
     
      entry = (TERMINAL_LIST_ENTRY *)malloc(sizeof(TERMINAL_LIST_ENTRY)); 
      if (entry) { 
     
        /* This entry is the current end of the list */ 
        entry->next = 0; 
     
        /* Store the terminal name */ 
        CciCharCpy(entry->name, terminalName); 
     
        /* Create terminal and save its handle */ 
        entry->handle = cniCreateOutputTerminal(&rc, context->nodeObject, (CciChar*)terminalName); 
     
        /* Link an existing previous element to this one */ 
        if (context->outputTerminalListPrevious) context->outputTerminalListPrevious->next = entry; 
        else if ((context->outputTerminalListHead) == 0) context->outputTerminalListHead = entry; 
     
        /* Save the pointer to the previous element */ 
        context->outputTerminalListPrevious = entry; 
      } 
      else {
        /* Error: Unable to allocate memory */
      } 
     
      return(entry); 
    } 
    C ユーザー定義ノードのコンパイルに必要な最小コードについては、C のスケルトン・コードを参照してください。

属性の設定

属性は、ブローカーを開始するとき、 あるいは新しい値を持つメッセージ・フローを再デプロイするときに設定します。 構成スレッド上のユーザー・コードを呼び出すブローカーによって、属性が設定されます。 コードでは、後にメッセージを処理するときに使用するために、 そのノードのコンテキスト・エリアにこれらの属性を保管する必要があります。

入力ターミナルと出力ターミナルの作成後、 ブローカーは cniSetAttribute 関数を呼び出して、 このユーザー定義ノードのインスタンス化用に構成されている属性の値を渡します。 以下に例を示します。
    {
      const CciChar* ucsAttr = CciString("nodeTraceSetting", BIP_DEF_COMP_CCSID) ;
      insAttrTblEntry(p, (CciChar*)ucsAttr, CNI_TYPE_INTEGER);
      _setAttribute(p, (CciChar*)ucsAttr, (CciChar*)constZero);
      free((void *)ucsAttr) ;
    }
    {
      const CciChar* ucsAttr = CciString("nodeTraceOutfile", BIP_DEF_COMP_CCSID) ;
      insAttrTblEntry(p, (CciChar*)ucsAttr, CNI_TYPE_STRING);
      _setAttribute(p, (CciChar*)ucsAttr, (CciChar*)constSwitchTraceLocation);
      free((void *)ucsAttr) ;
    }
ノードが持つことのできる構成属性の数は、無制限です。ただし、ノードは、基本構成属性としてすでにインプリメントされている属性をインプリメントしてはなりません。 以下のリストは、基本属性を示しています。
  • label
  • userTraceLevel
  • traceLevel
  • userTraceFilter
  • traceFilter

ノード機能のインプリメント

ブローカーがキューからメッセージを取り出し、 そのメッセージがユーザー定義のメッセージ処理ノードまたは出力ノードの入力ターミナルに到着すると、 ブローカーはインプリメンテーション関数 cniEvaluate を呼び出します。 この関数はメッセージ処理スレッド上に呼び出され、メッセージを使用して何を行うかを決定します。 特に追加のインスタンスが使用される場合に、この関数はマルチスレッド上に呼び出される場合もあります。

ノードのインスタンスの削除

ノードが削除された場合には、ブローカーは cniDeleteNodeContext 関数を呼び出します。 この関数は、cniCreateNodeContext と同じスレッドで開始されます。 この関数を使用して、ユーザー定義ノードが使用していたすべてのリソースを解放します。 以下に例を示します。

void _deleteNodeContext(
  CciContext* context
){
  static char* functionName = (char *)"_deleteNodeContext()";
  free ((void*) context);
  return;
}
特記事項 | 商標 | ダウンロード | ライブラリー | サポート | フィードバック

Copyright IBM Corporation 1999, 2009Copyright IBM Corporation 1999, 2009.
最終更新 : 2009-02-20 12:44:39

as09980_