IBM Integration Bus, Version 10.0.0.9 Operating Systems: AIX, HP-Itanium, Linux, Solaris, Windows, z/OS


Creating an input node in C

Create a user-defined input node in C to receive messages into a message flow.

Before you begin

About this task

A loadable implementation library, or LIL, is the implementation module for a C node. A LIL is implemented as a shared or dynamic link library (DLL), but has the file extension .lil not .dll.

The implementation functions that you write for the node are listed in C node implementation functions. You can call utility functions, implemented in the runtime integration node, to help with the node operation; these functions are listed in C node utility functions.

IBM® Integration Bus provides the source for two sample user-defined nodes called SwitchNode and TransformNode. You can use these nodes in their current state, or you can modify them.

Declaring and defining the node

About this task

To declare and define a user-defined node to the integration node, include an initialization function, bipGetMessageflowNodeFactory, in your LIL. The following steps outline how the integration node calls your initialization function, and how your initialization function declares and defines the user-defined node:

Procedure

  1. The initialization function, bipGetMessageflowNodeFactory, is called by the integration node after the operating system has loaded and initialized the LIL. The integration node calls this function to understand what your LIL can do and how the integration node can call the LIL. For example:
    CciFactory LilFactoryExportPrefix * LilFactoryExportSuffix
    bipGetMessageflowNodeFactory()
  2. The bipGetMessageflowNodeFactory function must call the utility function cniCreateNodeFactory. This function passes back a unique factory name (or group name) for all the nodes that your LIL supports. Every factory name (or group name) that is passed back must be unique throughout all the LILs in a single runtime integration node.
  3. The LIL must call the utility function cniDefineNodeClass to pass the unique name of each node, and a virtual function table of the addresses of the implementation functions.
    For example, the following code declares and defines a single node called 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);
    }

    A user-defined node identifies itself as providing the features of an input node by implementing the cniRun implementation function.

    User-defined nodes have to implement either a cniRun or a cniEvaluate implementation function. If they do not, the integration node does not load the user-defined node, and the cniDefineNodeClass utility function fails, returning CCI_MISSING_IMPL_FUNCTION.

    For example:
    int cniRun(                       
      CciContext* context,                
      CciMessage* localEnvironment,        
      CciMessage* exceptionList,          
      CciMessage* message
    ){          
      ...
      /* Get data from external source */
      return CCI_SUCCESS_CONTINUE;                                    
    }
    Use the return value periodically to return control to the integration node.

    When a message flow that contains a user-defined input node is deployed successfully, the cniRun function of the node is called repeatedly to enable the node to retrieve messages and propagate them to the rest of the message flow.

    For the minimum code required to compile a C user-defined node, see the C skeleton code.

Creating an instance of the node

About this task

To instantiate your node:

Procedure

  1. When the integration node has received the table of function pointers, it calls the function cniCreateNodeContext for each instantiation of the user-defined node. For example, if three message flows are using your user-defined node, your cniCreateNodeContext function is called for each of them. This function must allocate memory for that instantiation of the user-defined node to hold the values for the configured attributes. For example:
    1. Call the cniCreateNodeContext function:
      CciContext* _createNodeContext(
        CciFactory* factoryObject,
        CciChar*    nodeName,
        CciNode*    nodeObject
      ){
        static char* functionName = (char *)"_createNodeContext()";
        NODE_CONTEXT_ST* p;
        CciChar          buffer[256];
    2. Allocate a pointer to the local context and clear the context area:
        p = (NODE_CONTEXT_ST *)malloc(sizeof(NODE_CONTEXT_ST));
      
        if (p) {
           memset(p, 0, sizeof(NODE_CONTEXT_ST));
    3. Save the node object pointer in the context:
         p->nodeObject = nodeObject;
    4. Save the node name:
       CciCharNCpy((CciChar*)&p->nodeName, nodeName, MAX_NODE_NAME_LEN);
    5. Return the node context:
      return (CciContext*) p;
  2. An input node has a number of output terminals associated with it, but typically does not have any input terminals. Use the utility function cniCreateOutputTerminal to add output terminals to an input node when the node is instantiated. These functions must be invoked within the cniCreateNodeContext implementation function. For example, to define an input node with three output terminals:
       {
          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) ;    }
    For the minimum code required to compile a C user-defined node, see C skeleton code.

Setting attributes

About this task

Attributes are set whenever you start the integration node, or when you redeploy the message flow with new values.

Following the creation of output terminals, the integration node calls the cniSetAttribute function to pass the values for the configured attributes of the user-defined node. For example:
    {
      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) ;
    }
The number of configuration attributes that a node can have is unlimited. However, a user-defined node must not implement an attribute that is already implemented as a base configuration attribute. The base attributes are:
  • label
  • userTraceLevel
  • traceLevel
  • userTraceFilter
  • traceFilter

Implementing the node functionality

About this task

When the integration node knows that it has an input node, it calls the cniRun function of this node at regular intervals. The cniRun function must then decide what course of action it must take. If data is available for processing, the cniRun function can propagate the message. If no data is available for processing, the cniRun function must return with CCI_TIMEOUT so that the integration node can continue configuration changes.

For example, to configure the node to call cniDispatchThread and process the message, or return with 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;

Overriding the default message parser attributes (optional)

About this task

An input node implementation typically determines what message parser initially parses an input message. For example, the MQInput node dictates that an MQMD parser is required to parse the MQMD header. A user-defined input node can select an appropriate header or message parser, and the mode in which the parsing is controlled, by using or overriding the following attributes that are included as default:

rootParserClassName
Defines the name of the root parser that parses message formats that are supported by the user-defined input node. It defaults to GenericRoot, a supplied root parser that causes the integration node to allocate and chain parsers together. It is unlikely that a node would need to modify this attribute value.
firstParserClassName
Defines the name of the first parser, in what might be a chain of parsers that are responsible for parsing the bit stream. It defaults to XML.
messageDomainProperty
An optional attribute that defines the name of the message parser that is required to parse the input message. The supported values are the same as the values that are supported by the MQInput node. (See MQInput node for more information.)
messageSetProperty
An optional attribute that defines the message set identifier, or the message set name, in the Message Set field, only if the MRM parser was specified by the messageDomainProperty attribute.
messageTypeProperty
An optional attribute that defines the identifier of the message in the MessageType field, only if the MRM parser was specified by the messageDomainProperty attribute.
messageFormatProperty
An optional attribute that defines the format of the message in the Message Format field, only if the MRM parser was specified by the messageDomainProperty attribute.
If you have written a user-defined input node that always begins with data of a known structure, you can ensure that a specific parser handles the start of the data. For example, the MQInput node reads data only from WebSphere MQ queues, therefore this data always has an MQMD at the beginning, and the MQInput node sets firstParserClassName to MQHMD. If your user-defined node always handles data that begins with a structure that can be parsed by a specific parser, for example "MYPARSER", set firstParserClassName to MYPARSER in the following way::
  1. Declare the implementation functions:
    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. Set the attribute in the cniCreateNodeContext implementation function:
    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) ;
        }
    In the code example above, the insAttrTblEntry method is called. This method is declared in the SwitchNode and TransformNode sample user-defined nodes.

Deleting an instance of the node

About this task

Nodes are destroyed when a message flow is redeployed, or when the integration server process is stopped (using the mqsistop command). When a node is destroyed, you must call the cniDeleteNodeContext function to free all used memory and release all held resources. For example:

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

  return;
}

as09960_.htm | Last updated 2017-07-17 12:45:53