使用 C 语言创建输入节点

使用 C 语言创建用户定义输入节点,以将消息接收到消息流中。

准备工作

关于此任务

可装入实施库或 LIL 是 C 节点的实施模块。 LIL 当作共享或动态链接库(DLL)实现,但其文件扩展名为 .lil 而不是 .dll

您为节点编写的实现函数在 C 节点实现函数中列出。 您可以调用在运行时集成节点中实现的实用程序函数,以帮助执行节点操作; 这些函数列示在 C 节点实用程序函数中。

IBM® App Connect Enterprise 提供了两个名为 SwitchNode 和 TransformNode 的用户自定义节点的源代码。 您可以使用当前状态下的这些节点,也可以修改这些节点。

声明和定义节点

关于此任务

要向集成节点声明和定义用户定义的节点,请在 LIL 中包含初始化函数 bipGetMessageflowNodeFactory。 下列步骤简要说明了集成节点如何调用您的初始化函数,以及该初始化函数如何声明和定义用户定义节点:

过程

  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 integration node */
    	return(factoryObject);
    }

    用户定义节点通过实现 cniRun 实现函数提供输入节点的功能,从而对自己进行标识。

    用户定义节点必须实现 cniRuncniEvaluate 实现函数。 否则,集成节点不会装入用户定义节点,并且 cniDefineNodeClass 实用程序函数失败,返回 CCI_MISSING_IMPL_FUNCTION。

    例如:
    int cniRun(                       
      CciContext* context,                
      CciMessage* localEnvironment,        
      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. 输入节点有许多与它关联的 output 终端,但通常没有任何 input 终端。 在实例化节点时,请使用实用程序函数 cniCreateOutputTerminal 将 output 终端添加到输入节点。 这些函数必须在 cniCreateNodeContext 实现函数中调用。 例如,要定义带有三个 output 终端的输入节点:
       {
          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 框架代码

设置属性

关于此任务

每当启动集成节点或使用新的值重新部署消息流时,将设置属性。

在创建 output 终端后,集成节点调用 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 节点 以获取更多信息。)
messageSetProperty
可选属性,仅当 messageDomainProperty 属性指定了 MRM 解析器时,才会在 Message Set 字段中定义消息集标识或消息集名称。
messageTypeProperty
仅当 MRM 解析器由 messageDomainProperty 属性指定时,在 MessageType 字段中定义消息的标识的可选属性。
messageFormatProperty
可选属性,仅当 messageDomainProperty 属性指定了 MRM 解析器时,才会在 Message Format 字段中定义消息格式。
如果您编写了始终以具有已知结构的数据开始的用户定义输入节点,那么可以确保特定的解析器处理数据的开始。 例如 ,MQInput 节点仅从 IBM MQ 队列读取数据,因此这些数据开头总是带有MQMD, 而MQInput 节点会将 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;
}