Developing user-defined parsers

Create a user-defined parser to interpret messages with a different format and structure.

Before you begin

About this task

A loadable implementation library, or a LIL, is the implementation module for a C parser (or node). A LIL is a Linux® or UNIX shared object or Windows dynamic link library (DLL), that does not have the file extension .dll but .lil.

The implementation functions that you must write are listed in C parser implementation functions. The utility functions that are provided by IBM® Integration Bus to help you are listed in C parser utility functions.

IBM Integration Bus provides the source for a sample user-defined parser called BipSampPluginParser.c. This example is a simple pseudo-XML parser that you can use in its current state, or you can modify.

The task of writing a parser varies considerably according to the complexity of the bit stream to be parsed. Only the basic steps are described here:
  1. Declaring and defining the parser
  2. Creating an instance of the parser
  3. Deleting an instance of the parser

Declaring and defining the parser

About this task

To declare and define a user-defined parser to the integration node, you must include an initialization function, bipGetParserFactory, 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 parser:

The following procedure shows you how to declare and define your parser to the integration node:

Procedure

  1. The initialization function, bipGetParserFactory, is called by the integration node after the LIL has been loaded and initialized by the operating system. The integration node calls this function to understand what your LIL is able to do, and how it must be called. For example:
    CciFactory LilFactoryExportPrefix * LilFactoryExportSuffix
    bipGetParserFactory()
    
  2. The bipGetParserFactory function calls the utility function cpiCreateParserFactory. This function passes back a unique factory name (or group name) for all the parsers that your LIL supports. Every factory name (or group name) passed back must be unique throughout all the LILs in the integration node.
  3. The LIL calls the utility function cpiDefineParserClass to pass the unique name of each parser, and a virtual function table of the addresses of the implementation functions.
    For example, the following code declares and defines a single parser called InputxParser:
    {
    	CciFactory* factoryObject;
    	int rc = 0;
    	CciChar factoryName[] = L"MyParserFactory";
    	CCI_EXCEPTION_ST exception_st;
    
    	/* Create the Parser Factory for this parser */
    	factoryObject = cpiCreateParserFactory(0, factoryName);
    	if (factoryObject == CCI_NULL_ADDR) {
    		
    		/* Any local error handling can go here */
    	}
    	else {
    		/* Define the parsers supported by this factory */
    		static CNI_VFT vftable = {CNI_VFT_DEFAULT};
    
    	/* Setup function table with pointers to parser implementation functions */
    	vftable.iFpCreateContext            = cpiCreateContext;
    	vftable.iFpParseBufferEncoded       = cpiParseBufferEncoded;
    	vftable.iFpParseFirstChild          = cpiParseFirstChild;
    	vftable.iFpParseLastChild           = cpiParseLastChild;
    	vftable.iFpParsePreviousSibling     = cpiParsePreviousSibling;
    	vftable.iFpParseNextSibling         = cpiParseNextSibling;
    	vftable.iFpWriteBufferEncoded       = cpiWriteBufferEncoded;
    	vftable.iFpDeleteContext            = cpiDeleteContext;
    	vftable.iFpSetElementValue          = cpiSetElementValue;
    	vftable.iFpElementValue             = cpiElementValue;
    	vftable.iFpNextParserClassName      = cpiNextParserClassName;
    	vftable.iFpSetNextParserClassName   = cpiSetNextParserClassName;
    	vftable.iFpNextParserEncoding       = cpiNextParserEncoding;
    	vftable.iFpNextParserCodedCharSetId = cpiNextParserCodedCharSetId;
    
    	cpiDefineParserClass(0, factoryObject, L"InputxParser", &vftable);
    	}
    
    	/* Return address of this factory object to the integration node */
    	return(factoryObject);
    }

    The initialization function must create a parser factory by starting cpiCreateParserFactory. The parser classes supported by the factory are defined by calling cpiDefineParserClass. The address of the factory object (returned by cpiCreateParserFactory) must be returned to the integration node as the return value from the initialization function.

    For example:
    1. Create the parser factory using the cpiCreateParserFactory function:
        factoryObject = cpiCreateParserFactory(&rc, constParserFactory);
        
    2. Define the classes of message supported by the factory using the cpiDefineParserClass function:
      if (factoryObject) {
         cpiDefineParserClass(&rc, factoryObject, constPXML, &vftable);
        }
      else {
          /* Error: Unable to create parser factory */
        }
      
    3. Return the address of this factory object to the integration node:
        return(factoryObject);
      }

Creating an instance of the parser

About this task

When the integration node has received the table of function pointers, it calls the function cpiCreateContext for each instantiation of the user-defined parser. If you have three message flows that use your user-defined parser, your cpiCreateContext function is called for each of them. This function allocates memory for that instantiation of the user-defined parser to hold the values for the configured attributes. For example:

Procedure

  1. Call the cpiCreateContext function:
    CciContext* _createContext(
      CciFactory* factoryObject,
      CciChar*    parserName,
      CciNode*    parserObject
    ){
      static char* functionName = (char *)"_createContext()";
      PARSER_CONTEXT_ST* p;
      CciChar          buffer[256];
    
    
  2. Allocate a pointer to the local context and clear the context area:
      p = (PARSER_CONTEXT_ST *)malloc(sizeof(PARSER_CONTEXT_ST));
    
      if (p) {
         memset(p, 0, sizeof(PARSER_CONTEXT_ST));
    
  3. Save the parser object pointer in the context:
       p->parserObject = parserObject;
    
  4. Save the parser name:
     CciCharNCpy((CciChar*)&p->parserName, parserName, MAX_NODE_NAME_LEN);
    
  5. Return the parser context:
    return (CciContext*) p;
    

Deleting an instance of the parser

About this task

Parsers are destroyed when a message flow is deleted or redeployed, or when the integration server process is stopped (using the mqsistop command). When a parser is destroyed, it must free any used memory and release any held resources using the cpiDeleteContext function. For example:

void cpiDeleteContext(
  CciParser*  parser,
  CciContext* context
){
  PARSER_CONTEXT_ST* pc = (PARSER_CONTEXT_ST *)context ;
  int                rc = 0;

  return;
}