Nachrichtenverarbeitungs- oder Sendeknoten in C erstellen

Ein Nachrichtenverarbeitungsknoten wird zur Verarbeitung einer Nachricht und ein Sendeknoten zur Ausgabe einer Nachricht als Bitstrom verwendet.

Vorbereitungen

Im Hinblick auf die Codierung eines Nachrichtenverarbeitungsknotens und eines Sendeknotens lässt sich feststellen, dass die Knoten im Wesentlichen dieselben Services bereitstellen. Sie können an einem Sendeknoten Nachrichten verarbeiten, und ebenso können Sie mit einem Nachrichtenverarbeitungsknoten eine Nachricht als Bitstrom ausgeben. Zur Vereinfachung wird in diesem Abschnitt von einem Nachrichtenverarbeitungsknoten gesprochen, es sind jedoch auch Informationen zu den Funktionen beider Knotentypen enthalten.

Eine ladbare Implementierungsbibliothek (LIL) ist das Implementierungsmodul für einen C-Knoten. Eine LIL wird als gemeinsam genutzte Bibliothek oder als Dynamic Link Library (DLL) implementiert, hat jedoch die Dateierweiterung .lil, nicht .dll.

Weitere Informationen zu den C-Knoten-Implementierungsfunktionen, die Sie für den Knoten schreiben, erhalten Sie im Abschnitt Implementierungsfunktionen für C-Knoten. Sie können C-Knoten-Dienstprogrammfunktionen aufrufen, die im Laufzeitbroker implementiert sind, um die Knotenoperation zu unterstützen; weitere Informationen erhalten Sie im Abschnitt Dienstprogrammfunktionen für C-Knoten.

WebSphere Message Broker stellt die Quelle für die zwei benutzerdefinierten Beispielknoten 'Umschaltungsknoten' und 'Umsetzungsknoten' bereit. Sie können diese Knoten in ihrem aktuellen Zustand verwenden oder sie ändern. Desweiteren können Sie das folgende Beispiel anzeigen, in dem die Verwendung benutzerdefinierter Knoten einschließlich eines in C geschriebenen Nachrichtenverarbeitungsknotens veranschaulicht wird. Sie können Beispiele nur anzeigen, wenn Sie das Information Center verwenden, das im Message Brokers Toolkit integriert ist.

Knoten deklarieren und definieren

Sie müssen zum Deklarieren und Definieren eines benutzerdefinierten Knotens für den Broker eine Initialisierungsfunktion, bipGetMessageflowNodeFactory, in Ihrer LIL einfügen. Die folgenden Schritte werden im Konfigurations-Thread ausgeführt und legen fest, wie die Initialisierungsfunktion vom Broker aufgerufen wird und wie durch diese Funktion der benutzerdefinierte Knoten deklariert und definiert wird:

  1. Der Broker ruft die Funktion bipGetMessageflowNodeFactory zur Initialisierung auf, nachdem die LIL-Datei vom Betriebssystem geladen und initialisiert wurde. Der Broker ruft diese Funktion auf, um die Möglichkeiten der LIL-Datei zu überprüfen und um herauszufinden, wie die LIL-Datei vom Broker aufgerufen werden kann. Beispiel:
    CciFactory LilFactoryExportPrefix * LilFactoryExportSuffix
    bipGetMessageflowNodeFactory()
  2. Durch die Funktion bipGetMessageflowNodeFactory wird die Dienstprogrammfunktion cniCreateNodeFactory aufgerufen. Diese Funktion gibt für alle Knoten, die von der LIL-Datei unterstützt werden, einen Factory-Namen (oder Gruppennamen) zurück. Jeder Factory-Name (oder Gruppenname) muss für alle LIL-Dateien in einem einzelnen Laufzeit-Broker eindeutig sein.
  3. Die LIL muss die Dienstprogrammfunktion cniDefineNodeClass aufrufen, um die eindeutigen Namen der einzelnen Knoten und eine virtuelle Funktionstabelle der Adressen der Implementierungsfunktionen zu übergeben.
    Der folgende Code deklariert und definiert beispielsweise einen einzelnen Knoten namens 'MessageProcessingxNode':
    {
    	CciFactory* factoryObject;
    	int rc = 0;
    	CciChar factoryName[] = L"MyNodeFactory";
    	CCI_EXCEPTION_ST exception_st;
    
    	/* Create the Node Factory for this node */
    	factoryObject = cniCreateNodeFactory(0, factoryName);
    	if (factoryObject == CCI_NULL_ADDR) {
    		/* Any local error handling can go here */
    	}
    	else {
    		/* Define the nodes supported by this factory */
    	static CNI_VFT vftable = {CNI_VFT_DEFAULT};
    
    	/* Setup function table with pointers to node implementation functions */
    	vftable.iFpCreateNodeContext = _createNodeContext;
    	vftable.iFpDeleteNodeContext = _deleteNodeContext;
    	vftable.iFpGetAttributeName2 = _getAttributeName2;
    	vftable.iFpSetAttribute = _setAttribute;
    	vftable.iFpGetAttribute2     = _getAttribute2;
    	vftable.iFpEvaluate = _evaluate;
    
    	cniDefineNodeClass(0, factoryObject, L"MessageProcessingxNode", &vftable);
    
    	}
    
    	/* Return address of this factory object to the broker */
    	return(factoryObject);
    }

    Ein benutzerdefinierter Knoten identifiziert sich selbst als Nachrichtenverarbeitungsknoten oder Sendeknoten, indem er die Funktion cniEvaluate implementiert. Benutzerdefinierte Knoten müssen entweder eine cniEvaluate- oder eine cniRun-Implementierungsfunktion implementieren, ansonsten lädt der Broker den benutzerdefinierten Knoten nicht, und die Dienstprogrammfunktion cniDefineNodeClass schlägt mit dem Rückkehrcode CCI_MISSING_IMPL_FUNCTION fehl.

    Nach dem erfolgreichen Einsetzten eines Nachrichtenflusses mit benutzerdefiniertem Nachrichtenverarbeitungsknoten wird die Funktion cniEvaluate des Knotens für jede Nachricht aufgerufen, die an den Knoten weitergegeben wird.

    Die Nachrichtenflussdaten (d. h. Nachricht, Umgebung, lokale Umgebung und Ausnahmeliste) werden auf dem Eingabeterminal des Knotens empfangen.

    Beispiel:
    void cniEvaluate(                
      CciContext* context,                
      CciMessage*      localEnvironment,        
      CciMessage* exceptionList,          
      CciMessage* message                 
    ){                                    
      ...
    }
    Informationen zum Code, der mindestens erforderlich ist, um einen benutzerdefinierten Knoten in C zu kompilieren, finden Sie unter Basiscode für C-Knoten.

Instanz des Knotens erstellen

So instanziieren Sie Ihren Knoten:

  1. Wenn der Broker die Zeiger der Funktionstabelle erhalten hat, ruft er für jede Erstellung einer Instanz des benutzerdefinierten Knotens die Funktion cniCreateNodeContext auf. Wenn beispielsweise drei Nachrichtenflüsse vorhanden sind, die Ihren benutzerdefinierten Knoten verwenden, wird die Funktion cniCreateNodeContext für jeden dieser Knoten aufgerufen. Diese Funktion sollte Speicher für diese Instanzerstellung des benutzerdefinierten Knotens reservieren, um die Werte für die konfigurierten Attribute zu speichern. Beispiel:
    1. Die Benutzerfunktion cniCreateNodeContext wird aufgerufen:
      CciContext* _Switch_createNodeContext(
        CciFactory* factoryObject,
        CciChar* nodeName,
        CciNode* nodeObject
      ){
        static char* functionName = (char *)"_Switch_createNodeContext()";
        NODE_CONTEXT_ST* p;
        CciChar          buffer[256];
      
    2. Ordnen Sie dem lokalen Kontext einen Zeiger zu, und löschen Sie den Kontextbereich:
        p = (NODE_CONTEXT_ST *)malloc(sizeof(NODE_CONTEXT_ST));
      
        if (p) {
           memset(p, 0, sizeof(NODE_CONTEXT_ST));
    3. Speichern Sie den Zeiger des Knotenobjekts im Kontext:
         p->nodeObject = nodeObject;
    4. Speichern Sie den Knotennamen:
       CciCharNCpy((CciChar*) &p->nodeName, nodeName, MAX_NODE_NAME_LEN);
    5. Geben Sie den Knotenkontext zurück:
      return (CciContext*) p;
  2. Der Broker ruft die entsprechenden Dienstprogrammfunktionen auf, um die Eingabe- und Ausgabeterminals des Knotens zu überprüfen. Ein Knoten enthält eine Reihe von Eingabe- und Ausgabeterminals, die dem Knoten zugeordnet sind. In der Benutzerfunktion cniCreateNodeContext sollten cniCreateInputTerminal und cniCreateOutputTerminal aufgerufen werden, um die Terminals des Benutzerknotens zu definieren. Diese Funktionen müssen in der Implementierungsfunktion cniCreateNodeContext gestartet werden. Gehen Sie beispielweise bei der Definition eines Knotens mit einem Eingabeterminal und zwei Ausgabeterminals folgendermaßen vor:
        {
          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) ;
        }

    Der vorige Code startet die Funktionen insInputTerminalListEntry und insOutputTerminalListEntry. Sie finden diese Funktion im Mustercode Common.c; siehe Beispielknotendateien. Diese Funktionen definieren die Terminals beim Broker und speichern die Kennungen bei den Terminals. Die Kennungen werden in der Struktur gespeichert, auf die durch den in CciContext* gelieferten Wert Bezug genommen wird. Der Knoten kann dann aus den übrigen Implementierungsfunktionen heraus auf die Terminalkennungen zugreifen (beispielsweise CciEvaluate), da CciContext an diese Implementierungsfunktionen übergeben wird.

    Der folgende Code zeigt die Definition von insInputTerminalListEntry:

    TERMINAL_LIST_ENTRY *insInputTerminalListEntry( 
      NODE_CONTEXT_ST* context, 
      CciChar*         terminalName 
    ){ 
      static char* functionName = (char *)"insInputTerminalListEntry()"; 
      TERMINAL_LIST_ENTRY* entry; 
      int             rc; 
     
      entry = (TERMINAL_LIST_ENTRY *)malloc(sizeof(TERMINAL_LIST_ENTRY)); 
      if (entry) { 
     
        /* This entry is the current end of the list */ 
        entry->next = 0; 
     
        /* Store the terminal name */ 
        CciCharCpy(entry->name, terminalName); 
     
        /* Create terminal and save its handle */ 
        entry->handle = cniCreateInputTerminal(&rc, context->nodeObject, (CciChar*)terminalName); 
     
        /* Link an existing previous element to this one */ 
        if (context->inputTerminalListPrevious) context->inputTerminalListPrevious->next = entry; 
        else if ((context->inputTerminalListHead) == 0) context->inputTerminalListHead = entry; 
     
        /* Save the pointer to the previous element */ 
        context->inputTerminalListPrevious = entry; 
      } 
      else { 
        /* Error: Unable to allocate memory */ 
      } 
     
      return(entry); 
    } 

    Im Folgenden finden Sie ein Codebeispiel für insOutputTerminalListEntry:

    TERMINAL_LIST_ENTRY *insOutputTerminalListEntry( 
      NODE_CONTEXT_ST* context, 
      CciChar*         terminalName 
    ){ 
      static char* functionName = (char *)"insOutputTerminalListEntry()"; 
      TERMINAL_LIST_ENTRY* entry; 
      int             rc; 
     
      entry = (TERMINAL_LIST_ENTRY *)malloc(sizeof(TERMINAL_LIST_ENTRY)); 
      if (entry) { 
     
        /* This entry is the current end of the list */ 
        entry->next = 0; 
     
        /* Store the terminal name */ 
        CciCharCpy(entry->name, terminalName); 
     
        /* Create terminal and save its handle */ 
        entry->handle = cniCreateOutputTerminal(&rc, context->nodeObject, (CciChar*)terminalName); 
     
        /* Link an existing previous element to this one */ 
        if (context->outputTerminalListPrevious) context->outputTerminalListPrevious->next = entry; 
        else if ((context->outputTerminalListHead) == 0) context->outputTerminalListHead = entry; 
     
        /* Save the pointer to the previous element */ 
        context->outputTerminalListPrevious = entry; 
      } 
      else { 
        /* Error: Unable to allocate memory */ 
      } 
     
      return(entry); 
    } 
    Informationen zum Code, der mindestens erforderlich ist, um einen benutzerdefinierten Knoten in C zu kompilieren, finden Sie unter Basiscode für C-Knoten.

Attribute festlegen

Attribute werden festgelegt, wenn Sie den Broker starten oder einen Nachrichtenfluss mit neuen Werten erneut implementieren. Die Attribute werden von dem Broker festgelegt, der den Benutzercode im Konfigurations-Thread aufruft. Der Benutzercode muss diese Attribute zur späteren Verwendung bei der Verarbeitung von Nachrichten im Knotenkontextbereich speichern.

Nach der Erstellung von Eingabe- und Ausgabeterminals wird vom Broker die Funktion cniSetAttribute aufgerufen, um die Werte der konfigurierten Attribute für diese Instanzerstellung des benutzerdefinierten Knotens zu übergeben. Beispiel:
    {
      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) ;
    }
Die Anzahl der Konfigurationsattribute in einem Knoten ist nicht begrenzt. Ein Knoten darf jedoch kein Attribut implementieren, das bereits als Basiskonfigurationsattribut implementiert ist. Die folgende Liste enthält Basisattribute:
  • label
  • userTraceLevel
  • traceLevel
  • userTraceFilter
  • traceFilter

Knotenfunktionen implementieren

Wenn der Broker eine Nachricht aus der Warteschlange empfängt und diese Nachricht den Eingabeterminal Ihres benutzerdefinierten Nachrichtenverarbeitungs- oder Sendeknoten erreicht, wird vom Broker die Implementierungsfunktion cniEvaluate aufgerufen. Diese Funktion wird im Nachrichtenverarbeitungs-Thread aufgerufen und muss entscheiden, welche Maßnahmen für die Nachricht ergriffen werden. Möglicherweise wird diese Funktion in mehreren Threads aufgerufen, besonders bei Verwendung von zusätzlichen Instanzen.

Instanz des Knotens löschen

Wird ein Knoten gelöscht, ruft der Broker die Funktion cniDeleteNodeContext auf. Diese Funktion wird im gleichen Thread gestartet wie cniCreateNodeContext. Von dieser Funktion müssen alle Ressourcen freigegeben werden, die von Ihrem benutzerdefinierter Knoten verwendet werden. Beispiel:

void _deleteNodeContext(
  CciContext* context
){
  static char* functionName = (char *)"_deleteNodeContext()";
  free ((void*) context);
  return;
}
Bemerkungen | Marken | Downloads | Bibliothek | Unterstützung | Feedback

Copyright IBM Corporation 1999, 2009Copyright IBM Corporation 1999, 2009.
Letzte Aktualisierung : 2009-02-17 15:30:01

as09980_