使用 C 创建输入节点

开始之前

可装入实现库或 LIL 是 C 节点(或解析器)的实现模块。LIL 实现为动态链接库(DLL)。它没有文件扩展名 .dll 但有文件扩展名 .lil。

必须由开发者写的实施函数在C 节点实施函数中列出。WebSphere Message Broker 提供的用于帮助此过程的实用程序函数在C 节点实用程序函数中列出。

WebSphere Message Broker 为称为 SwitchNode 和 TransformNode 的两个用户定义的样本节点提供了数据源。您可以使用当前状态下的这些节点,也可以修改这些节点。

本主题描述了使用 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);
    }

    用户定义的节点通过实现 cniEvaluate 实施函数提供输入节点来标识它自己。

    用户定义的节点必须实现 cniRuncniEvaluate 实施函数,否则代理不会装入用户定义的节点,且 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。如果您有三个使用您的用户定义的节点的消息流,将为每个消息流调用您的 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 实施函数中调用。例如,要定义带有三个输出终端的输入节点:
       {
                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 属性指定时)。
messageTypeProperty
MessageType 字段中定义消息的标识的可选属性(仅当 MRM 解析器由 messageDomainProperty 属性指定时)。
messageFormatProperty
消息格式字段中定义消息的格式的可选属性(仅当 MRM 解析器由 messageDomainProperty 属性指定时)。
如果您写了总是以已知结构的数据开始的用户定义的输入节点,您可以确保特定的解析器处理数据的开始。例如,MQInputNode 只读取来自 WebSphere MQ 队列的数据,因此该数据总是在开始处有 MQMD,且 MQInputNode 将 firstParserClassName 设置为 MQHMD。如果您的用户定义的节点总是处理以特定的解析器可以解析的结构开始的数据,如“MYPARSER”,您的到 MYPARSER 的 firstParserClassName to 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 方法被调用。此方法在用户定义的样本节点中声明。

删除节点实例

当消息流重新部署时或执行组进程停止时(使用 mqsistop 命令),节点被破坏。当节点被破坏时,它应该释放任何已使用的内存并释放任何占用的资源。您使用 cniDeleteNodeContext 函数这样做。例如:

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

    return;
}
声明 | 商标 | 下载 | 书库 | 支持 | 反馈
Copyright IBM Corporation 1999, 2006 最后一次更新时间:2006/08/14
as09960_