Creating a parser in C

Before you start

Ensure that you have read and understood the following topics:

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 have to be written by the developer are listed in C parser implementation functions. The utility functions that are provided by WebSphere Message Broker to aid this process are listed in C parser utility functions.

WebSphere Message Broker provides the source for a sample user-defined parser called BipSampPluginParser.c. This 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. They are described in the following sections:
  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

To declare and define a user-defined parser to the broker you must include an initialization function, bipGetParserFactory, in your LIL. The following steps outline how the broker 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 broker:

  1. The initialization function, bipGetParserFactory, is called by the broker after the LIL has been loaded and initialized by the operating system. The broker calls this function to understand what your LIL is able to do and how the broker should call the LIL. For example:
    CciFactory LilFactoryExportPrefix * LilFactoryExportSuffix
    bipGetParserFactory()
  2. The bipGetParserFactory function must then call 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 broker.
  3. The LIL must then call 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 broker */
    	return(factoryObject);
    }

    The initialization function must then create a parser factory by invoking 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 broker 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 broker:
        return(factoryObject);
      }

Creating an instance of the parser

The following procedure shows you how to instantiate your parser:

When the broker 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 should allocate memory for that instantiation of the user-defined parser to hold the values for the configured attributes. For example:
  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

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

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

  return;
}