C での入力ノードの作成

始める前に

以下のトピックを読み、理解していることを確認してください。

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

開発者が作成する必要のあるインプリメンテーション関数は、C ノード・インプリメンテーション関数でリストしています。 このプロセスに役立てるために WebSphere Message Broker によって提供されるユーティリティー関数は、C ノード・ユーティリティー関数でリストしています。

WebSphere Message Broker では、SwitchNode および TransformNode という名前の 2 つのサンプル・ユーザー定義ノードのソースが準備されています。 これらのノードは現行の状態で使用することもできますし、変更を加えてもかまいません。

このトピックでは、 C を使用して入力ノードを作成するために実行する必要のあるステップについて説明します。 以下のステップについて要約しています。
  1. ノードの宣言および定義
  2. ノードのインスタンスの作成
  3. 属性の設定
  4. ノード機能のインプリメント
  5. デフォルトのメッセージ・パーサー属性のオーバーライド (オプション)
  6. ノードのインスタンスの削除

ノードの宣言および定義

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

  1. LIL がロードされ、オペレーティング・システムによって初期化されると、 ブローカーによって初期化関数 bipGetMessageflowNodeFactory が呼び出されます。 ブローカーは、 LIL が実行できる事柄と、ブローカーが LIL を呼び出す方法を理解するために、この関数を呼び出します。 例えば、以下のようになります。
    CciFactory LilFactoryExportPrefix * LilFactoryExportSuffix
    bipGetMessageflowNodeFactory()
  2. bipGetMessageflowNodeFactory 関数は次にユーティリティー関数 cniCreateNodeFactory を呼び出します。 この関数は、LIL がサポートするすべてのノードに固有のファクトリー名 (またはグループ名) を戻します。 戻されるファクトリー名 (またはグループ名) はいずれも、そのブローカー内のすべての LIL で固有でなければなりません。
  3. LIL は次にユーティリティー関数 cniDefineNodeClass を呼び出して、 各ノードの固有の名前と、インプリメンテーション関数のアドレスの仮想関数表を渡します。
    例えば、 以下のコードは InputxNode という単一ノードの宣言と定義を行います。
    {
    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.iFpRun = _run;
    
    cniDefineNodeClass(0, factoryObject, L"InputxNode", &vftable);
    	}
    
    /* Return address of this factory object to the broker */
    return(factoryObject);
    }

    ユーザー定義ノードは、cniRun インプリメンテーション関数をインプリメントすることによって、 入力ノードの機能を提供するという役割を果たします。

    ユーザー定義ノードは、cniRun インプリメンテーション関数か cniEvaluate インプリメンテーション関数のいずれかをインプリメントしていなければなりません。そうでないと、ブローカーはそのユーザー定義ノードをロードせず、cniDefineNodeClass ユーティリティー関数が失敗して、 CCI_MISSING_IMPL_FUNCTION が戻されます。

    例えば、以下のようになります。
    int cniRun(
      CciContext* context,
      CciMessage* destinationList,
      CciMessage* exceptionList,
      CciMessage* message
    ){          
      ...
      /* Get data from external source */
      return CCI_SUCCESS_CONTINUE;
    }
    この戻り値は、定期的にブローカーに制御を戻すために使用されます。

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

    C ユーザー定義ノードのコンパイルに必要な最小コードについては、C のスケルトン・コードを参照してください。

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

以下の手順は、ノードをインスタンス化する方法を表しています。

  1. ブローカーは、関数ポインターの表を受信すると、 ユーザー定義ノードの各インスタンス化ごとに関数 cniCreateNodeContext を呼び出します。 ユーザー定義ノードを使用する 3 つのメッセージ・フローがある場合は、 3 つのそれぞれに対して cniCreateNodeContext 関数が呼び出されます。 この関数は、そのユーザー定義ノードのインスタンス化用に、 構成された属性の値を保持するためのメモリーを割り振ります。 例えば、以下のようになります。
    1. cniCreateNodeContext 関数を呼び出します。
      CciContext* _createNodeContext(
        CciFactory* factoryObject,
        CciChar* nodeName,
        CciNode* nodeObject
      ){
        static char* functionName = (char *)"_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. 入力ノードには、いくつかの出力ターミナルが関連付けられますが、 通常は入力ターミナルはありません。 ノードをインスタンス化するときに、 ユーティリティー関数 cniCreateOutputTerminal を使用して、 入力ノードに出力ターミナルを追加します。 これらの関数は、cniCreateNodeContext インプリメンテーション関数内で呼び出されなければなりません。 例えば、 3 つの出力ターミナルを持つ入力ノードを定義するには、 次のようにします。
       {
          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) ;
        }    
        {
          const CciChar* ucsCatch = CciString("catch", BIP_DEF_COMP_CCSID) ;
          insOutputTerminalListEntry(p, (CciChar*)ucsCatch);
          free((void *)ucsCatch) ; }
    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
変更の終わり

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

入力ノードがあることをブローカーが認識している場合、 ブローカーは定期的にこのノードの cniRun 関数を呼び出します。 cniRun 関数は、 実行すべきアクションを判断しなければなりません。 処理に使用できるデータがあれば、cniRun 関数はメッセージの伝搬を試みます。 処理に使用できるデータがなければ、cniRun 関数は CCI_TIMEOUT とともに制御を戻して、ブローカーが構成変更を続行できるようにします。

例えば、cniDispatchThread を呼び出してメッセージを処理するか、そうでなければ CCI_TIMEOUT と共に制御を戻すようにノードを構成するには、次のようにします。
If ( anything to do )
	CniDispatchThread;

   /* do the work */

If ( work done O.K.)
Return CCI_SUCCESS_CONTINUE;
Else
Return CCI_FAILURE_CONTINUE;
Else
  Return CCI_TIMEOUT;

デフォルトのメッセージ・パーサー属性のオーバーライド (オプション)

入力ノードは通常、 最初に入力メッセージを構文解析するメッセージ・パーサーを判別します。 例えば、プリミティブ MQInput ノードは、 MQMD ヘッダーを構文解析するために MQMD パーサーが必要であることを指示します。 ユーザー定義入力ノードは、 デフォルトとして組み込まれる、オーバーライドも可能な以下の属性を使用することによって、 適切なヘッダーかメッセージ・パーサー、および構文解析の制御のモードを選択することができます。

rootParserClassName
ユーザー定義入力ノードによってサポートされているメッセージ形式を構文解析するルート・パーサーの名前を定義します。 デフォルトでは GenericRoot に設定されます。 これは、ブローカーにパーサーを割り振らせたりチェーニングするために提供されているルート・パーサーです。 おそらく、ノードでこの属性値を変更する必要はありません。
firstParserClassName
ビット・ストリームを構文解析する役割を持つパーサーのチェーンの中で、 最初のパーサーの名前を定義します。 デフォルトでは XML に設定されます。
messageDomainProperty
入力メッセージを構文解析するのに必要なメッセージ・パーサーの名前を定義するオプションの属性。サポートされる値は、MQInput ノードによってサポートされるものと同じです。 (MQInput ノードの詳細については、MQInput ノードを参照してください。)
messageSetProperty
MRM パーサーが messageDomainProperty 属性によって指定された場合にのみ、 Message Set フィールドのメッセージ・セットの ID (またはメッセージ・セット名) を定義するオプションの属性。
messageTypeProperty
MRM パーサーが messageDomainProperty 属性によって指定された場合にのみ、 MessageType フィールドのメッセージの ID を定義するオプションの属性。
messageFormatProperty
MRM パーサーが messageDomainProperty 属性によって指定された場合にのみ、 Message Format フィールドのメッセージの形式を定義するオプションの属性。
常に既知の構造のデータで始まるユーザー定義の入力ノードを作成している場合は、 特定のパーサーがデータの開始を処理するようにすることができます。 例えば、MQInputNode は WebSphere MQ キューからのみデータを読み取るので、 このデータには常に先頭に MQMD が付いており、MQInputNode は firstParserClassName を MQHMD に設定しています。 ユーザー定義ノードが常に、特定のパーサー、つまり "MYPARSER"、で構文解析できる構造で始まるデータを処理する場合は、 次のようにして firstParserClassName を MYPARSER に設定します。
  1. インプリメンテーション関数を宣言します。
    CciFactory LilFactoryExportPrefix * LilFactoryExportSuffix bipGetMessageflowNodeFactory()
    {
      ....
      CciFactory* factoryObject;
      ....
      factoryObject = cniCreateNodeFactory(0, (unsigned short *)constPluginNodeFactory);
      ...
      vftable.iFpCreateNodeContext = _createNodeContext;
      vftable.iFpSetAttribute = _setAttribute;
      vftable.iFpGetAttribute = _getAttribute;
      ...  
      cniDefineNodeClass(&rc, factoryObject, (CciChar*)constSwitchNode, &vftable);
      ...
      return(factoryObject);
    }
  2. cniCreateNodeContext インプリメンテーション関数で属性を設定します。
    CciContext* _createNodeContext(
      CciFactory* factoryObject,
      CciChar* nodeName,
      CciNode* nodeObject
    ){
      NODE_CONTEXT_ST* p;
      ...
    
        /* Allocate a pointer to the local context */
        p = (NODE_CONTEXT_ST *)malloc(sizeof(NODE_CONTEXT_ST));
        /* Create attributes and set default values */
        {
          const CciChar* ucsAttrName = CciString("firstParserClassName", BIP_DEF_COMP_CCSID);
          const CciChar* ucsAttrValue = CciString("MYPARSER", BIP_DEF_COMP_CCSID) ;
          insAttrTblEntry(p, (CciChar*)ucsAttrName, CNI_TYPE_INTEGER);
          /*see sample BipSampPluginNode.c for implementation of insAttrTblEntry*/
    
          _setAttribute(p, (CciChar*)ucsAttrName, (CciChar*)ucsAttrValue);
          free((void *)ucsAttrName) ;
          free((void *)ucsAttrValue) ;
        }
    上記のコード例では、insAttrTblEntry メソッドが 呼び出されます。このメソッドは、サンプル・ユーザー定義ノード SwitchNode および TransformNode で宣言されます。

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

メッセージ・フローが再デプロイされたり、 実行グループの処理が (mqsistop コマンドを使用して) 停止されたりすると、 ノードは破棄されます。 ノードが破棄されるとき、 使用されていたメモリーおよび保持されていたリソースがあれば、 それらは開放される必要があります。 これは、cniDeleteNodeContext 関数を使用して行います。 例えば、以下のようになります。

void _deleteNodeContext(
  CciContext* context
){
  static char* functionName = (char *)"_deleteNodeContext()";

  return;
}
特記事項 | 商標 | ダウンロード | ライブラリー | サポート | フィードバック
Copyright IBM Corporation 1999, 2006 最終更新: 08/21/2006
as09960_