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.
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 de entrada definidos por el usuario tienen que implementar una función 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 contiene un nodo de entrada definido por el usuario, 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 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 pasado a través del flujo de mensajes.
Aunque los nodos de entrada también pueden implementar cniEvaluate, no se recomienda que lo hagan.
El procedimiento siguiente muestra cómo crear instancias del nodo:
Se establecen atributos siempre que se inicia el intermediario o cuando se vuelve a desplegar el flujo de mensajes con valores nuevos.
{ 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 este nodo a intervalos regulares. Entonces la función cniRun debe decidir qué acción debe realizar. Si hay datos disponibles para procesarlos, la función cniRun debe invocar cniDispatchThread y procesar el mensaje o volver con CCI_TIMEOUT para que el intermediario pueda continuar procesando otros mensajes en otras hebras. Si no se despacha una hebra, el intermediario emplea todo su tiempo en esta hebra, lo que le impide realizar nada más.
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;
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:
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 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; }