Creación de un nodo de entrada en C

Antes de empezar

Una biblioteca de implementación cargable, o LIL, es el módulo de implementación para un nodo (o analizador) C. Una LIL se implementa como una biblioteca de enlaces dinámicos (DLL). No tiene la extensión de archivo .dll sino .lil.

Las funciones de implementación que el desarrollador debe escribir se listan en el apartado Funciones de implementación de nodo en C. Las funciones de programa de utilidad que WebSphere Message Broker proporciona para ayudar en este proceso se listan en el apartado Funciones de programa de utilidad de nodo en C.

WebSphere Message Broker proporciona el origen de dos nodos de ejemplo definidos por el usuario denominados SwitchNode y TransformNode. Puede utilizar estos nodos en su estado actual o puede modificarlos.

Declaración y definición del nodo

Para declarar y definir un nodo definido por el usuario en el intermediario, debe incluir una función de inicialización bipGetMessageflowNodeFactory, en su LIL. Los pasos siguientes indican cómo llama el intermediario a su función de inicialización y cómo esa función declara y define el nodo definido por el usuario:

  1. El intermediario invoca la función de inicialización, bipGetMessageflowNodeFactory, después de que el sistema operativo haya cargado e inicializado la biblioteca de implementación cargable (LIL). El intermediario invoca esta función para saber qué es capaz de hacer la biblioteca de implementación cargable y cómo la debe invocar. Por ejemplo:
    CciFactory LilFactoryExportPrefix * LilFactoryExportSuffix
    bipGetMessageflowNodeFactory()
  2. Entonces la función bipGetMessageflowNodeFactory debe llamar a la función de utilidad cniCreateNodeFactory. Esta función devuelve un nombre de fábrica (o nombre de grupo) exclusivo para todos los nodos soportados por la LIL. Todos los nombres de fábrica (o nombres de grupo) que se devuelven, deben ser exclusivos en todas las LIL en el intermediario.
  3. Entonces la biblioteca de implementación cargable (LIL) debe llamar a la función de utilidad cniDefineNodeClass para pasar el nombre exclusivo de cada nodo y una tabla de funciones virtual de las direcciones de las funciones de implementación.
    Por ejemplo, el código siguiente declara y define un solo nodo denominado InputxNode:
    {
    CciFactory*      factoryObject;
    int rc = 0;
    CciChar factoryName[] = L"MyNodeFactory";
    CCI_EXCEPTION_ST exception_st;
    
    	/* Crear la fábrica de nodos para este nodo */
    factoryObject = cniCreateNodeFactory(0, factoryName);
    if (factoryObject == CCI_NULL_ADDR) {
    		
    	/* Aquí puede ir cualquier manejo de errores locales */
    	}
    	else {
    	/* Definir los nodos soportados por esta fábrica */
    static CNI_VFT vftable = {CNI_VFT_DEFAULT};
    
    	/* Configurar tabla de funciones con punteros a funciones de implementación de nodo */
    vftable.iFpCreateNodeContext = _createNodeContext;
    vftable.iFpDeleteNodeContext = _deleteNodeContext;
    vftable.iFpGetAttributeName2 = _getAttributeName2;
    vftable.iFpSetAttribute      = _setAttribute;
    vftable.iFpGetAttribute2     = _getAttribute2;
    vftable.iFpRun = _run;
    
    cniDefineNodeClass(0, factoryObject, L"InputxNode", &vftable);
    	}
    
    /* Devolver la dirección de este objeto de fábrica al intermediario */
    return(factoryObject);
    }

    Un nodo definido por el usuario se identifica a sí mismo como un nodo que proporciona las posibilidades de un nodo de entrada implementando la función de implementación cniRun.

    Los nodos definidos por el usuario tienen que implementar una función de implementación cniRun o cniEvaluate o, de lo contrario, el intermediario no cargará el nodo definido por el usuario y la función de utilidad cniDefineNodeClass fallará, devolviendo CCI_MISSING_IMPL_FUNCTION.

    Por ejemplo:
    int cniRun(                       
      CciContext* context,                
      CciMessage* destinationList,        
      CciMessage* exceptionList,          
      CciMessage* message
    ){          
      ...
      /* Obtener datos del origen externo */
      return CCI_SUCCESS_CONTINUE;                                    
    }
    Se deberá utilizar el valor de retorno para devolver el control periódicamente al intermediario.

    Cuando se despliega satisfactoriamente un flujo de mensajes que contienen un nodo de entrada definido por el usuario, se invoca la función cniRun del nodo para cada mensaje propagado al nodo.

    Para el código mínimo requerido para compilar un nodo definido por el usuario en C, consulte Código de esqueleto en C.

Creación de una instancia del nodo

El procedimiento siguiente muestra cómo crear instancias del nodo:

  1. Cuando el intermediario ha recibido la tabla de punteros de función, invoca la función cniCreateNodeContext para cada creación de instancia del nodo definido por el usuario. Si tiene tres flujos de mensajes que están utilizando el nodo definido por el usuario, se invoca la función cniCreateNodeContext para cada uno de ellos. Esta función debe asignar memoria para esa creación de instancia del nodo definido por el usuario para que contenga los valores de los atributos configurados. Por ejemplo:
    1. Invoque la función cniCreateNodeContext:
      CciContext* _createNodeContext(
        CciFactory* factoryObject,
        CciChar*    nodeName,
        CciNode*    nodeObject
      ){
        static char* functionName = (char *)"_createNodeContext()";
        NODE_CONTEXT_ST* p;
        CciChar          buffer[256];
    2. Asigne un puntero al contexto local y borre el área de contexto:
        p = (NODE_CONTEXT_ST *)malloc(sizeof(NODE_CONTEXT_ST));
      
        if (p) {
           memset(p, 0, sizeof(NODE_CONTEXT_ST));
    3. Guarde el puntero de objeto de nodo en el contexto:
         p->nodeObject = nodeObject;
    4. Guarde el nombre de nodo:
       CciCharNCpy((CciChar*) &p->nodeName, nodeName, MAX_NODE_NAME_LEN);
    5. Devuelva el contexto de nodo:
      return (CciContext*) p;
  2. Un nodo de entrada tiene asociados varios terminales de salida pero normalmente no tiene ningún terminal de entrada. Utilice la función de utilidad cniCreateOutputTerminal para añadir terminales de salida a un nodo de entrada cuando se cree una instancia del nodo. Estas funciones se deben invocar en la función de implementación cniCreateNodeContext. Por ejemplo, para definir un nodo de entrada con tres terminales de salida:
       {
          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) ;    }
    Para el código mínimo requerido para compilar un nodo definido por el usuario en C, consulte Código de esqueleto en C.

Establecimiento de atributos

Se establecen atributos siempre que se inicia el intermediario o cuando se vuelve a desplegar el flujo de mensajes con valores nuevos.

Después de que se hayan creado los terminales de salida, el intermediario invoca la función cniSetAttribute para pasar los valores de los atributos configurados del nodo definido por el usuario. Por ejemplo:
    {
      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) ;
    }
Inicio del cambioNo hay límite en el número de atributos de configuración que puede tener un nodo. Sin embargo, un nodo de plug-in no debe implementar un atributo que ya esté implementado como atributo de configuración base. Los atributos base son:
  • label
  • userTraceLevel
  • traceLevel
  • userTraceFilter
  • traceFilter
Fin del cambio

Implementación de la funcionalidad de nodo

Cuando el intermediario sabe que tiene un nodo de entrada, invoca la función cniRun de este nodo a intervalos regulares. Entonces la función cniRun debe decidir qué acción debe realizar. Si hay datos disponibles para procesar, la función cniRun debe intentar propagar el mensaje. Si no hay datos disponibles para procesar, la función cniRun debe volver con CCI_TIMEOUT para que el intermediario pueda seguir con los cambios de configuración.

Por ejemplo, si desea configurar el nodo para invocar cniDispatchThread y procesar el mensaje o volver con CCI_TIMEOUT:
If ( algo para hacer )
	CniDispatchThread;

   /* realizar el trabajo */

If ( trabajo realizado satisfactoriamente)
Return CCI_SUCCESS_CONTINUE;
	Else
Return CCI_FAILURE_CONTINUE;
Else
  Return CCI_TIMEOUT;

Alteración temporal de los atributos de analizador de mensajes por omisión (opcional)

Normalmente una implementación de nodo de entrada determina qué analizador de mensajes analiza inicialmente un mensaje de entrada. Por ejemplo, el nodo MQInput primitivo indica que se necesita un analizador MQMD para analizar la cabecera MQMD. Un nodo de entrada definido por el usuario puede seleccionar un analizador de mensajes o cabeceras apropiado y la modalidad en la que se controla el análisis, utilizando los atributos siguiente que se incluyen como valores por omisión y que se pueden alterar temporalmente:

rootParserClassName
Define el nombre del analizador raíz que analiza los formatos de mensaje soportados por el nodo de entrada definido por el usuario. Toma por omisión GenericRoot, un analizador raíz proporcionado que hace que el intermediario asigne y encadene analizadores. Es improbable que un nodo necesite modificar este valor de atributo.
firstParserClassName
Define el nombre del primer analizador, de lo que puede ser una cadena de analizadores responsables de analizar la corriente de bits. Toma por omisión XML.
messageDomainProperty
Atributo opcional que define el nombre del analizador de mensajes necesario para analizar el mensaje de entrada. Los valores soportados son los mismos que los soportados por el nodo MQInput. (Consulte el apartado Nodo MQInput si desea ver más información sobre el nodo MQInput.)
messageSetProperty
Atributo opcional que define el identificador de conjunto de mensajes o el nombre de conjunto de mensajes, en el campo Conjunto de mensajes, sólo si el atributo messageDomainProperty especificaba el analizador MRM.
messageTypeProperty
Atributo opcional que define el identificador del mensaje en el campo Tipo de mensaje, sólo si el atributo messageDomainProperty especificaba el analizador MRM.
messageFormatProperty
Atributo opcional que define el formato del mensaje en el campo Formato del mensaje, sólo si el atributo messageDomainProperty especificaba el analizador MRM.
Si ha escrito un nodo de entrada definido por el usuario que empieza siempre con datos de una estructura conocida, puede asegurarse de que un determinado analizador se encargue del inicio de los datos. Por ejemplo, MQInputNode sólo lee datos de las colas de WebSphere MQ, de modo que estos datos siempre tienen MQMD al principio y MQInputNode establece firstParserClassName en MQHMD. Si el nodo definido por el usuario siempre se encarga de los datos que empiezan con una estructura que puede ser analizada por un determinado analizador, por ejemplo "MYPARSER", establezca firstParserClassName en MYPARSER como se indica a continuación:
  1. Declare las funciones de implementación:
    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. Establezca el atributo en la función de implementación cniCreateNodeContext:
    CciContext* _createNodeContext(
      CciFactory* factoryObject,
      CciChar*    nodeName,
      CciNode*    nodeObject
    ){
      NODE_CONTEXT_ST* p;
      ...
    
        /* Asignar un puntero al contexto local */
        p = (NODE_CONTEXT_ST *)malloc(sizeof(NODE_CONTEXT_ST));
        /* Crear atributos y establecer valores por omisión */
        {
          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);
          /*ver BipSampPluginNode.c de ejemplo para la implementación de insAttrTblEntry*/
    
          _setAttribute(p, (CciChar*)ucsAttrName, (CciChar*)ucsAttrValue);
          free((void *)ucsAttrName) ;
          free((void *)ucsAttrValue) ;
        }
    En el código de ejemplo anterior, se invoca al método insAttrTblEntry. Este método se declara en los nodos de ejemplo definidos por el usuario SwitchNode y TransformNode.

Supresión de una instancia del nodo

Los nodos se destruyen cuando se vuelve a desplegar un flujo de mensajes o cuando se detiene el proceso de grupo de ejecución (utilizando el mandato mqsistop). Cuando un nodo se destruye, debe liberar la memoria utilizada así como los recursos retenidos. Para ello, utilice la función cniDeleteNodeContext. Por ejemplo:

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

  return;
}
Avisos | Marcas registradas | Descargas | Biblioteca | Soporte | Su opinión
Copyright IBM Corporation 1999, 2006 Última actualización: 22/08/2006
as09960_