Creación de un nodo de proceso de mensajes o de salida en C

Antes de empezar

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. Además, puede ver el ejemplo de extensión definida por el usuario que muestra el uso de nodos definidos por el usuario, incluido un nodo de proceso de mensajes escrito en C.

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). Tiene la extensión de archivo .lil, no .dll.

Las funciones de implementación que el desarrollador tiene que 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. También puede utilizar el ejemplo Ejemplo de extensión definida por el usuario.

Conceptualmente, un nodo de proceso de mensajes se utiliza para procesar un mensaje de algún modo y un nodo de salida se utiliza para producir un mensaje como una corriente de bits. Sin embargo, cuando se codifica un nodo de proceso de mensajes o un nodo de salida, éstos son esencialmente lo mismo. Puede realizar el proceso de mensajes en un nodo de salida y, del mismo modo, puede producir un mensaje en una corriente de bits utilizando un nodo de proceso de mensajes. Aunque, para una mayor sencillez, este tema hace referencia principalmente al nodo como nodo de proceso de mensajes, describe la funcionalidad de ambos tipos de nodo.

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 tienen lugar en la hebra de configuración e 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) para todos los nodos soportados por la LIL. El nombre de fábrica (o nombre de grupo) debe ser exclusivos en todas las LIL en el intermediario.
  3. Entonces la biblioteca de implementación cargable (LIL) debe invocar 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 MessageProcessingxNode:
    {
    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.iFpEvaluate = _evaluate;
    
    cniDefineNodeClass(0, factoryObject, L"MessageProcessingxNode", &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 proceso de mensajes o de salida implementando la función cniEvaluate. Los nodos definidos por el usuario tienen que implementar una función de implementación cniEvaluate o cniRun 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.

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

    Los datos de flujo de mensajes se reciben en el terminal de entrada del nodo, es decir el mensaje, el entorno global, el entorno local y la lista de excepciones.

    Por ejemplo:
    void cniEvaluate(                
      CciContext* context,                
      CciMessage* destinationList,        
      CciMessage* exceptionList,          
      CciMessage* message                 
    ){                                    
      ...
    }
    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. Se invoca la función de usuario cniCreateNodeContext:
      CciContext* _Switch_createNodeContext(
        CciFactory* factoryObject,
        CciChar*    nodeName,
        CciNode*    nodeObject
      ){
        static char* functionName = (char *)"_Switch_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. El intermediario invoca las funciones de programa de utilidad apropiadas para obtener información sobre los terminales de entrada y los terminales de salida del nodo. Un nodo tiene asociados varios terminales de entrada y terminales de salida. En la función de usuario cniCreateNodeContext, se deben efectuar llamadas a cniCreateInputTerminal y cniCreateOutputTerminal para definir los terminales del nodo de usuario. Estas funciones se deben invocar en la función de implementación cniCreateNodeContext. Por ejemplo, para definir un nodo con un terminal de entrada y dos terminales de salida:
        {
          const CciChar* ucsIn = CciString("in", BIP_DEF_COMP_CCSID) ;
          insInputTerminalListEntry(p, (CciChar*)ucsIn);
          free((void *)ucsIn) ;
        }
        {
          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) ;
        }
    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 un flujo de mensajes con valores nuevos. El intermediario establece atributos invocando el código de usuario en la hebra de configuración. El código de usuario necesita almacenar estos atributos en el área de contexto de nodo, para utilizarlos posteriormente al procesar mensajes.

Después de que se hayan creado los terminales de entrada y salida, el intermediario invoca la función cniSetAttribute para pasar los valores de los atributos configurados de esta creación de instancia 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 recupera un mensaje de la cola y dicho mensaje llega al terminal de entrada del nodo de salida o de proceso de mensajes definido por el usuario, el intermediario invoca la función de implementación cniEvaluate. Esta función se invoca en la hebra de proceso de mensajes y debe decidir qué se debe hacer con el mensaje. Es posible que esta función se invoque en varias hebras, específicamente si se utilizan instancias adicionales.

Supresión de una instancia del nodo

En el caso de que se suprima un nodo, el intermediario llama a la función cniDeleteNodeContext. Esta función debe liberar todos los recursos utilizados por el nodo definido por el usuario. Por ejemplo:

void _deleteNodeContext(
  CciContext* context
){
  static char* functionName = (char *)"_deleteNodeContext()";
  free ((void*) context);  return;
}
Avisos | Marcas registradas | Descargas | Biblioteca | Soporte | Su opinión
Copyright IBM Corporation 1999, 2006 Última actualización: 22/08/2006
as09980_