Creazione di un nodo di input in C

Prima di iniziare

Una libreria di implementazione caricabile, o LIL (Loadable Implementation Library), è il modulo di implementazione di un nodo C (o programma di analisi). Una LIL è implementata come una DLL (Dynamic Link Library). La sua estensione file non è .dll, ma .lil.

Le funzioni di implementazione che devono essere scritte dallo sviluppatore sono riportate in Funzioni di implementazione del nodo in C. Le funzioni di utilità che sono fornite da WebSphere Message Broker per facilitare questa elaborazione sono riportate in Funzioni di utilità del nodo in C.

WebSphere Message Broker fornisce l'origine per due nodi definiti dall'utente di esempio denominati SwitchNode e TransformNode. E' possibile utilizzare tali nodi nel loro stato attuale oppure modificarli.

Dichiarazione e definizione del nodo

Per dichiarare e definire un nodo definito dall'utente al broker è necessario includere una funzione di inizializzazione, bipGetMessageflowNodeFactory nella LIL. I passi seguenti evidenziano il modo in cui il broker chiama la funzione di inizializzazione e come tale funzione dichiara e definisce il nodo definito dall'utente:

  1. La funzione di inizializzazione, bipGetMessageflowNodeFactory, viene richiamata dal broker dopo il caricamento e l'inizializzazione della LIL da parte del sistema operativo. Il broker richiama tale funzione per individuare l'operatività della LIL e come è possibile eseguirne la chiamata. Ad esempio:
    CciFactory LilFactoryExportPrefix * LilFactoryExportSuffix
    bipGetMessageflowNodeFactory()
  2. La funzione bipGetMessageflowNodeFactory deve poi richiamare la funzione di utilità cniCreateNodeFactory. Questa funzione trasferisce un nome di produttore univoco (o nome di gruppo) per tutti i nodi supportati dalla LIL in uso. Ogni nome di produttore (o nome di gruppo) ripassato deve essere univoco nell'ambito di tutte le LIL nel broker.
  3. La LIL deve richiamare poi la funzione di utilità cniDefineNodeClass per trasferire il nome univoco di ciascun nodo e una tabella di funzioni virtuale degli indirizzi delle funzioni di implementazione.
    Ad esempio, nel codice riportato di seguito viene dichiarato e definito un singolo nodo denominato InputxNode:
    {
    CciFactory*     factoryObject;
    int                  rc = 0;
    	CciChar factoryName[] = L"MyNodeFactory";
    	CCI_EXCEPTION_ST exception_st;
    
    	/* Crea il produttore per questo nodo */
    	factoryObject = cniCreateNodeFactory(0, factoryName);
    if (factoryObject == CCI_NULL_ADDR) {
    		
    		/* L'eventuale gestione errori locale può essere inserita qui */
    	}
    	else {
    		/* Definisce i nodi previsti da questo produttore */
    		static CNI_VFT vftable = {CNI_VFT_DEFAULT};
    
    	/* Imposta tabella funzioni con puntatori sulle funzioni di implementazione del 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);
    	}
    
    /* Restituisce l'indirizzo di questo oggetto produttore al broker */
    return(factoryObject);
    }

    Un nodo definito dall'utente si identifica in quanto fornisce la funzionalità di un nodo di input implementando la funzione di implementazione cniRun.

    I nodi definiti dall'utente devono implementare una funzione di implementazione cniRun o cniEvaluate, altrimenti il broker non carica il nodo definito dall'utente e la funzione di utilità cniDefineNodeClass ha esito negativo, restituendo CCI_MISSING_IMPL_FUNCTION.

    Ad esempio:
    int cniRun(                       
      CciContext*  context,                
      CciMessage*   destinationList,        
      CciMessage*  exceptionList,          
      CciMessage* message
    ){          
      ...
      /* Fornisce i dati dell'origine esterna */
      return CCI_SUCCESS_CONTINUE;                                    
    }
    Il valore di restituzione deve essere utilizzato per restituire periodicamente il controllo al broker.

    Quando un flusso di messaggi contenente un nodo di input definito dall'utente viene distribuito con esito positivo, viene richiamata la funzione cniRun del nodo per ciascun messaggio trasmesso al nodo.

    In merito al codice minimo richiesto per la compilazione di un nodo definito dall'utente C, consultare Skeleton code C.

Creazione di un'istanza del nodo

La seguente procedura mostra come creare l'istanza del nodo

  1. Quando il broker riceve la tabella dei puntatori di funzione, richiama la funzione cniCreateNodeContext per ciascuna istanza creata del nodo definito dall'utente. Se si dispone di tre flussi di messaggi che utilizzano il nodo definito dall'utente, per ciascuno di questi, viene richiamata la funzione cniCreateNodeContext. Questa funzione assegna la memoria per la creazione dell'istanza del nodo definito dall'utente. Ad esempio:
    1. Richiamare la funzione cniCreateNodeContext:
      CciContext* _createNodeContext(
        CciFactory*  factoryObject,
        CciChar*     nodeName,
        CciNode*    nodeObject
      ){
        static char* functionName = (char *)"_createNodeContext()";
        NODE_CONTEXT_ST* p;
        CciChar          buffer[256];
    2. Assegnare un puntatore al contesto locale ed eliminare il contenuto nell'area del contesto:
        p = (NODE_CONTEXT_ST *)malloc(sizeof(NODE_CONTEXT_ST));
      
        if (p) {
           memset(p, 0, sizeof(NODE_CONTEXT_ST));
    3. Salvare il puntatore dell'oggetto nodo nel contesto:
         p->nodeObject = nodeObject;
    4. Salvare il nome del nodo:
       CciCharNCpy((CciChar*) &p->nodeName, nodeName, MAX_NODE_NAME_LEN);
    5. Restituzione del contesto del nodo:
      return (CciContext*) p;
  2. Ad un nodo di input sono associati una serie di terminali di output, ma tale nodo non dispone solitamente di alcun terminale di input. Utilizzare la funzione di utilità cniCreateOutputTerminal per aggiungere terminali di output ad un nodo di input quando viene creata l'istanza del nodo. Tali funzioni devono essere richiamate all'interno della funzione di implementazione cniCreateNodeContext. Ad esempio, per definire un nodo di input con tre terminali di output:
       {
          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) ;    }
    In merito al codice minimo richiesto per la compilazione di un nodo definito dall'utente C, consultare Skeleton code C.

Impostazione degli attributi

Gli attributi vengono impostati ogni volta che viene avviato il broker oppure quando il flusso di messaggi viene ridistribuito con nuovi valori.

Successivamente alla creazione dei terminali di output, il broker richiama la funzione cniSetAttribute per passare i valori per gli attributi configurati del nodo definito dall'utente. Ad esempio:
    {
      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) ;
    }
Inizio modificaNon esistono limiti relativi al numero di attributi di configurazione che un nodo può avere. Tuttavia, è necessario che un nodo plug-in non implementi un attributo già implementato come attributo di configurazione base. Gli attributi base sono:
  • label
  • userTraceLevel
  • traceLevel
  • userTraceFilter
  • traceFilter
Fine modifica

Implementazione della funzionalità del nodo

Quando il broker sa di disporre di un nodo di input, richiama ad intervalli regolari la funzione cniRun di questo nodo. La funzione cniRun decide quindi l'azione da intraprendere. Se i dati sono disponibili per l'elaborazione, la funzione cniRun deve cercare di propagare il messaggio. Se i dati non sono invece disponibili, la funzione cniRun deve restituire CCI_TIMEOUT in modo che il broker possa continuare le modifiche di configurazione.

Ad esempio, per configurare il nodo in modo da richiamare cniDispatchThread ed elaborare il messaggio oppure restituire CCI_TIMEOUT:
If ( anything to do )
	CniDispatchThread;

   /* esegue il lavoro */

	If ( work done O.K.)
		Return CCI_SUCCESS_CONTINUE;
	Else
		Return CCI_FAILURE_CONTINUE;
Else
  Return CCI_TIMEOUT;

Sovrascrittura degli attributi di programmi di analisi di messaggi predefiniti (facoltativo)

L'implementazione di un nodo di input determina normalmente il programma di analisi che inizialmente analizza un messaggio di input. Ad esempio, il nodo MQInput primitivo richiede che per analizzare l'intestazione MQMD venga utilizzato un programma di analisi MQMD. Un nodo di input definito dall'utente può selezionare un programma di analisi del messaggio o un'intestazione appropriata, e la modalità secondo cui viene gestita l'analisi, utilizzando i seguenti attributi inclusi come impostazione predefinita, che è comunque possibile sovrascrivere:

rootParserClassName
Definisce il nome del programma di analisi root che analizza i formati del messaggio supportati da nodo di input definito dall'utente. Il valore predefinito è GenericRoot, un programma di analisi root fornito che fa sì che il broker assegni e concateni i programmi di analisi. E' improbabile che un nodo abbia necessità di modificare questo valore di attributo.
firstParserClassName
Definisce il nome del primo programma di analisi, in quella che potrebbe essere una catena di programmi di analisi responsabili dell'analisi del flusso di bit. Il valore predefinito è XML.
messageDomainProperty
Un attributo facoltativo che definisce il nome del programma di analisi del messaggio richiesto per analizzare il messaggio di input. I valori previsti sono gli stessi previsti dal nodo MQInput. Fare riferimento a Nodo MQInput per ulteriori informazioni sul nodo MQInput.
messageSetProperty
Un attributo facoltativo che definisce l'identificativo della serie di messaggi o il nome della serie di messaggi nel campo Serie di messaggi, solo se il programma di analisi MRM è stato specificato dall'attributo messageDomainProperty.
messageTypeProperty
Un attributo facoltativo che definisce l'identificativo del messaggio nel campoTipo di messaggio, solo se il programma di analisi MRM è stato specificato dall'attributo messageDomainProperty.
messageFormatProperty
Un attributo facoltativo che definisce il formato del messaggio nel campo Formato del messaggio, solo se il programma di analisi MRM è stato specificato dall'attributo messageDomainProperty.
Se è stato scritto un nodo di input definito dall'utente che inizia sempre con dati di una determinata struttura, è possibile assicurare che un determinato programma di analisi sia relativo all'avvio dei dati. Ad esempio, MQInputNode legge solo dati dalle code WebSphere MQ, quindi questi dati hanno sempre MQMD all'inizio e MQInputNode imposta firstParserClassName su MQHMD. Se il nodo definito dall'utente è sempre relativo ai dati che iniziano con una struttura che può essere analizzata da un determinato programma di analisi, ovvero "MYPARSER", firstParserClassName viene impostato su MYPARSER come riportato di seguito:
  1. Dichiarare le funzioni di implementazione:
    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. Impostare l'attributo nella funzione di implementazione cniCreateNodeContext:
    CciContext* _createNodeContext(
      CciFactory*  factoryObject,
      CciChar*     nodeName,
      CciNode*    nodeObject
    ){
      NODE_CONTEXT_ST* p;
      ...
    
        /* Assegna un puntatore al contesto locale */
        p = (NODE_CONTEXT_ST *)malloc(sizeof(NODE_CONTEXT_ST));
        /* Crea attributi e imposta i valori predefiniti */
        {
          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);
          /*fare riferimento all'esempio BipSampPluginNode.c per l'implementazione di insAttrTblEntry*/
    
          _setAttribute(p, (CciChar*)ucsAttrName, (CciChar*)ucsAttrValue);
          free((void *)ucsAttrName) ;
          free((void *)ucsAttrValue) ;
        }
    Nell'esempio di codice precedente, viene richiamato il metodo insAttrTblEntry. Questo metodo è dichiarato nei nodi definiti dall'utente di esempio SwitchNode e TransformNode.

Eliminazione di un'istanza del nodo

I nodi vengono eliminati quando viene ridistribuito un flusso di messaggi oppure quando viene arrestata l'elaborazione del gruppo di esecuzione (utilizzando il comando mqsistop). Quando un nodo viene eliminato, deve liberare la memoria utilizzata e rilasciare le eventuali risorse impegnate. A tal fine, utilizzare la funzione cniDeleteNodeContext. Ad esempio:

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

  return;
}
Informazioni particolari | Marchi | Download | Libreria | Supporto | Commenti
Copyright IBM Corporation 1999, 2006 Ultimo aggiornamento: ago 17, 2006
as09960_