Antes de empezar
Una biblioteca de implementación cargable, o una LIL, es el módulo de implementación para un nodo C (o analizador). Una LIL se implementa como una biblioteca de enlace dinámico (DLL). No tiene la extensión de archivo .dll, sino la extensión .lil.
Las funciones de implementación que debe escribir el desarrollador están enumeradas en el apartado Funciones de implementación de nodo de lenguaje C. Las funciones de programa de utilidad que proporciona WebSphere Business Integration Message Broker para facilitar este proceso están enumeradas en el apartado Funciones de programa de utilidad de nodo de lenguaje C.
WebSphere Business Integration Message Broker proporciona el origen para dos nodos de ejemplo definidos por el usuario, llamados SwitchNode y TransformNode. Puede utilizar estos nodos en su estado actual, o puede modificarlos.
Un nodo de entrada puede recibir datos procedentes de cualquier tipo de origen externo, como un sistema de archivos o conexiones FTP, siempre que la salida del nodo tenga el formato correcto. Para las conexiones a colas o bases de datos, debe utilizar los nodos primitivos de IBM y las llamadas API suministradas, principalmente porque los nodos primitivos ya están configurados para el manejo de errores. No utilice los mandatos mqget o mqput para acceder directamente a las tablas de base de datos.
{ static char* functionName = (char *)"_Input_run()"; void* buffer; CciTerminal* terminalObject; int buflen = 4096; int rc = CCI_SUCCESS; int rcDispatch = CCI_SUCCESS; char xmlData[] = "<A>data</A>"; buffer = malloc(buflen); memcpy(buffer, &xmlData, sizeof(xmlData)); cniSetInputBuffer(&rc, message, buffer, buflen); } /*propagar, etc*/ free(buffer);El ejemplo anterior ilustra un área de memoria que se va a asignar (buffer = malloc(buflen);). Cuando programe en C, siempre que asigne memoria deberá liberarla cuando ya no la necesite. El intermediario puede intentar acceder a esta memoria en cualquier momento mientras el mensaje se propaga a través del flujo, así que debe liberar la memoria sólo después de invocar cniPropagate en el mismo objeto CciMessage.
Un nodo de entrada tiene la responsabilidad de finalizar el proceso de mensajes correctamente cuando un mensaje se ha propagado a través de un flujo de mensajes. Específicamente, el nodo de entrada debe hacer que se confirmen o restituyan las transacciones, y debe devolver las hebras a la agrupación de hebras.
Cada hebra de flujo de mensajes se asigna desde una agrupación de hebras mantenida para cada flujo de mensajes, y empieza a ejecutarse en la función cniRun. El usuario determina el comportamiento de una hebra utilizando la función de programa de utilidad cniDispatchThread junto con el valor de retorno adecuado.
El término transacción se utiliza aquí de forma genérica para describir una transacción coordinada globalmente o una transacción controlada por intermediario. Las transacciones coordinadas globalmente se coordinan mediante WebSphere MQ como gestor de transacciones compatible con XA , o como servicio de recuperación de recursos (RRS) en z/OS. WebSphere Business Integration Message Broker controla las transacciones confirmando (o restituyendo) los recursos de base de datos y, a continuación, confirmando las unidades de trabajo de WebSphere MQ; no obstante, si se utiliza un nodo definido por el usuario, el intermediario no podrá confirmar automáticamente las actualizaciones de recursos. El nodo definido por el usuario utiliza valores de retorno para indicar si una transacción se ha procesado correctamente, y para controlar si las transacciones se confirman o restituyen. Las excepciones que no se hayan manejado las recibe la infraestructura de intermediario, y la transacción se restituye.
En la tabla siguiente se describe cada uno de los valores de retorno a los que se da soporte, el efecto que tienen en las transacciones, y las acciones que realiza el intermediario con la hebra actual.
Valor de retorno | Efecto en transacción | Acción realizada en la hebra por intermediario |
---|---|---|
CCI_SUCCESS_CONTINUE | Confirmado | Vuelve a invocar la misma hebra en la función cniRun. |
CCI_SUCCESS_RETURN | Confirmado | Devuelve la hebra a la agrupación de hebras. |
CCI_FAILURE_CONTINUE | Restituido | Vuelve a invocar la misma hebra en la función cniRun. |
CCI_FAILURE_RETURN | Restituido | Devuelve la hebra a la agrupación de hebras. |
CCI_TIMEOUT | No aplicable. La función sobrepasa periódicamente su tiempo de espera mientras espera un mensaje de entrada. | Vuelve a invocar la misma hebra en la función cniRun. |
{ ... cniDispatchThread(&rcDispatch, ((NODE_CONTEXT_ST *)context)->nodeObject); ... if (rcDispatch == CCI_NO_THREADS_AVAILABLE) return CCI_SUCCESS_CONTINUE; else return CCI_SUCCESS_RETURN; }
Antes de propagar un mensaje, debe decidir qué datos de flujo de mensajes desea propagar, y qué terminal va a recibir los datos.
El objeto terminalObject se deriva de una lista que el nodo definido por el usuario mantiene para sí mismo.
if (terminalObject) { if (cniIsTerminalAttached(&rc, terminalObject)) { if (rc == CCI_SUCCESS) { cniPropagate(&rc, terminalObject, destinationList, exceptionList, message); } }
En el ejemplo anterior, la función cniIsTerminalAttached se utiliza para probar si el mensaje puede propagarse al terminal especificado. Si no utiliza la función cniIsTerminalAttached, y el terminal no está conectado a otro nodo mediante un conector, el mensaje no se propagará. Si utiliza esta función, podrá modificar el comportamiento del nodo cuando un terminal no esté conectado.
El procedimiento siguiente muestra cómo inicializar el nodo:
CciFactory LilFactoryExportPrefix * LilFactoryExportSuffix bipGetMessageflowNodeFactory()
{ CciFactory* factoryObject; int rc = 0; CciChar factoryName[] = L"SwitchNodeFactory"; CCI_EXCEPTION_ST exception_st; /* Crear la fábrica del nodo para este nodo */ factoryObject = cniCreateNodeFactory(0, factoryName); if (factoryObject == CCI_NULL_ADDR) { if (rc == CCI_EXCEPTION) { /* Obtener detalles de la excepción */ cciGetLastExceptionData(&rc, &exception_st); /* El manejo de errores local puede incluirse aquí */ /* Volver a emitir la excepción */ cciRethrowLastException(&rc); } /* El manejo de errores local adicional puede incluirse aquí */ } else { /* Definir los nodos a los que da soporte esta fábrica */ defineSwitchNode(factoryObject); } /* Devolver dirección de este objeto de fábrica al intermediario */ return(factoryObject); }
void defineInputxNode(void* factoryObject){ static CNI_VFT vftable = {CNI_VFT_DEFAULT}; /* Configurar tabla de funciones con punteros a las funciones */ /* de implementación de nodo */ vftable.iFpCreateNodeContext = _createNodeContext; vftable.iFpDeleteNodeContext = _deleteNodeContext; vftable.iFpGetAttributeName = _getAttributeName; vftable.iFpSetAttribute = _setAttribute; vftable.iFpGetAttribute = _getAttribute; vftable.iFpRun = _run; cniDefineNodeClass(0, factoryObject, L"InputxNode", &vftable); return; }
Un nodo definido por el usuario se identifica como nodo que proporciona la capacidad de nodo de entrada mediante la implementación de la función de implementación cniRun. Los nodos de entrada definidos por el usuario deben implementar una función cniRun, de lo contrario, el intermediario no carga el nodo definido por el usuario y la función de programa de utilidad cniDefineNodeClass no se ejecuta correctamente y devuelve CCI_MISSING_IMPL_FUNCTION. Cuando un flujo de mensajes que contiene un nodo de entrada definido por el usuario se difunde correctamente, el intermediario invoca la función de implementación cniRun del nodo a intervalos regulares.
int cniRun( CciContext* context, CciMessage* destinationList, CciMessage* exceptionList, CciMessage* message ){ ... /* Obtener datos de origen externo */ return CCI_SUCCESS_CONTINUE; }El valor de retorno debe utilizarse para devolver, periódicamente, el control al intermediario.
Cuando un flujo de mensajes que contiene un nodo de entrada definido por el usuario se difunde correctamente, se invoca la función cniRun del nodo para cada mensaje que se pasa a través del flujo de mensajes.
Los nodos de entrada también pueden implementar la función cniEvaluate, no obstante, esto no es recomendable.
El procedimiento siguiente muestra cómo crear una instancia del nodo:
CciContext* _Switch_createNodeContext( CciFactory* factoryObject, CciChar* nodeName, CciNode* nodeObject ){ static char* functionName = (char *)"_Switch_createNodeContext()"; NODE_CONTEXT_ST* p; CciChar buffer[256];
p = (NODE_CONTEXT_ST *)malloc(sizeof(NODE_CONTEXT_ST)); if (p) { memset(p, 0, sizeof(NODE_CONTEXT_ST));
p->nodeObject = nodeObject;
CciCharNCpy((CciChar*)&p->nodeName, nodeName, MAX_NODE_NAME_LEN);
{ 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) ; }
Los atributos se configuran siempre que se inicia el intermediario, o al volver a difundir el flujo de mensajes con nuevos valores.
{ 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) ; }
Cuando el intermediario sabe que tiene un nodo de entrada, invoca la función cniRun de dicho nodo a intervalos regulares. La función cniRun debe entonces decidir las acciones que va a llevar a cabo. Si hay datos disponibles para procesarse, la función cniRun debe invocar cniDispatchThread y procesar el mensaje, o devolver CCI_TIMEOUT para que el intermediario pueda continuar procesando otros mensajes en otras hebras. Si no se envía una hebra, el intermediario invierte todo su tiempo en esta hebra, la cual le impide hacer ninguna otra cosa.
If ( anything to do ) CniDispatchThread; /* hacer el trabajo */ If ( work done O.K.) Return CCI_SUCCESS_CONTINUE; Else Return CCI_FAILURE_CONTINUE; Else Return CCI_TIMEOUT;
Normalmente, la implementación de un nodo de entrada determina qué analizador de mensajes analiza inicialmente un mensaje de entrada. Por ejemplo, el nodo MQInput primitivo dicta que es necesario un analizador MQMD para analizar la cabecera MQMD. Un nodo de entrada definido por el usuario puede seleccionar una cabecera o un analizador de mensajes adecuado, y la modalidad en que se controlará el análisis, utilizando los atributos siguientes que se incluyen por omisión, pero que pueden alterarse temporalmente:
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); }
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) ; }
Los nodos se destruyen cuando un flujo de mensajes vuelve a difundirse o cuando el proceso de grupo de ejecución se detiene (mediante el mandato mqsistop). Al destruirse, el nodo debe liberar la memoria utilizada, así como los recursos retenidos. Para destruirlo, debe utilizarse la función cniDeleteNodeContext. Por ejemplo:
void _deleteNodeContext( CciContext* context ){ static char* functionName = (char *)"_deleteNodeContext()"; return; }
Conceptos relacionados
Planificación de extensiones definidas por el usuario
Extensiones definidas por el usuario en el entorno de ejecución
Diseño de extensiones definidas por el usuario
Nodos de entrada definidos por el usuario
Tareas relacionadas
Desarrollo de extensiones definidas por el usuario
Implementación de los ejemplos proporcionados
Compilación de una extensión definida por el usuario en C
Referencia relacionada
Nodo MQInput
API de nodo C definido por el usuario
Avisos |
Marcas registradas |
Descargas |
Biblioteca |
Soporte |
Información de retorno (feedback)
![]() ![]() |
as09960_ |