Empfangsknoten in C erstellen

Vorbereitungen

Stellen Sie sicher, dass Sie die folgenden Abschnitte gelesen und verstanden haben:

Eine ladbare Implementierungsbibliothek (Loadable Implementation Library, LIL) ist das Implementierungsmodul für einen C-Knoten (oder -Parser). Eine LIL-Datei wird als DLL-Datei (Dynamic Link Library) implementiert. Die Dateierweiterung ist dabei nicht DLL, sondern LIL.

Die Implementierungsfunktionen, die vom Entwickler geschrieben werden müssen, sind unter Implementierungsfunktionen für C-Knoten aufgelistet. Die Dienstprogrammfunktionen, die von WebSphere Message Broker bereitgestellt werden, um diesen Prozess zu unterstützen, sind unter Dienstprogrammfunktionen für C-Knoten aufgelistet.

WebSphere Message Broker stellt die Quelle für die zwei benutzerdefinierten Beispielknoten 'Umschaltungsknoten' und 'Umsetzungsknoten' bereit. Diese Knoten können in ihrem aktuellen Zustand verwendet oder von Ihnen angepasst werden.

In diesem Abschnitt wird beschrieben, wie Sie zur Erstellung eines Empfangsknotens mit Hilfe von C vorgehen müssen. Sie finden darin die folgenden Themen:
  1. Knoten deklarieren und definieren
  2. Instanz des Knotens erstellen
  3. Attribute festlegen
  4. Knotenfunktionen implementieren
  5. Standardmäßige Attribute des Nachrichtparsers überschreiben (optional)
  6. Knoteninstanz löschen

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. Im Folgenden wird erläutert, wie die Initialisierungsfunktion vom Broker aufgerufen wird und wie durch diese Funktion der benutzerdefinierte Knoten deklariert und definiert wird:

  1. Die Funktion bipGetMessageflowNodeFactory zur Initialisierung wird aufgerufen, 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 soll. Beispiel:
    CciFactory LilFactoryExportPrefix * LilFactoryExportSuffix
    bipGetMessageflowNodeFactory()
  2. Durch die Funktion bipGetMessageflowNodeFactory wird anschließend die Dienstprogrammfunktion cniCreateNodeFactory aufgerufen. Diese Funktion gibt für alle Knoten, die von der LIL-Datei unterstützt werden, einen eindeutigen Factory-Namen (oder Gruppennamen) zurück. Jeder zurückgegebene Factory-Name (oder Gruppenname) muss für alle LIL-Dateien im Broker eindeutig sein.
  3. Durch die LIL-Datei muss anschließend die Dienstprogrammfunktion cniDefineNodeClass für die Übergabe der eindeutigen Namen der einzelnen Knotenund eine virtuelle Funktionstabelle mit den Adressen der Implementierungsfunktionen aufgerufen werden.
    Der folgende Code deklariert und definiert beispielsweise einen einzelnen Knoten namens 'InputxNode':
    {
    	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.iFpRun = _run;
    
    	cniDefineNodeClass(0, factoryObject, L"InputxNode", &vftable);
    	}
    
    	/* Return address of this factory object to the broker */
    	return(factoryObject);
    }

    Ein benutzerdefinierter Knoten stellt die Funktion eines Empfangsknotens bereit, indem er die Implementierungsfunktion cniRun implementiert.

    Benutzerdefinierte Knoten müssen entweder eine cniRun- oder eine cniEvaluate-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.

    Beispiel:
    int cniRun(                       
      CciContext* context,                
      CciMessage* destinationList,        
      CciMessage* exceptionList,          
      CciMessage* message
    ){          
      ...
      /* Get data from external source */
      return CCI_SUCCESS_CONTINUE;                                    
    }
    Der Rückgabewert sollte dazu verwendet werden, die Steuerung regelmäßig an den Broker zurückzugeben.

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

    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

In der folgenden Prozedur wird beschrieben, wie Sie für Ihren Knoten eine Instanz erstellen können:

  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 drei Nachrichtenflüsse vorhanden sind, die Ihren benutzerdefinierten Knoten verwenden, wird die Funktion cniCreateNodeContext für jeden dieser Knoten aufgerufen. Durch diese Funktion soll Speicher für die Instanzerstellung der benutzerdefinierten Knoten reserviert werden, damit dort die Werte der konfigurierten Attribute gespeichert werden können. Beispiel:
    1. Rufen Sie die Funktion cniCreateNodeContext auf:
      CciContext* _createNodeContext(
        CciFactory* factoryObject,
        CciChar*    nodeName,
        CciNode*    nodeObject
      ){
        static char* functionName = (char *)"_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. Ein Empfangsknoten enthält eine Reihe von zugeordneten Ausgabeterminals, normalerweise aber keine Eingabeterminals. Verwenden Sie die Dienstprogrammfunktion cniCreateOutputTerminal, um beim Erstellen der Knoteninstanz Ausgabeterminals zu einem Empfangsknoten hinzuzufügen. Diese Funktionen müssen innerhalb der Implementierungsfunktion cniCreateNodeContext aufgerufen werden. Gehen Sie beispielweise bei der Definition eines Knotens mit drei Ausgabeterminals folgendermaßen vor:
       {
          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) ;    }
    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 den Nachrichtenfluss mit neuen Werten erneut einsetzen.

Nach der Erstellung von Ausgabeterminals wird vom Broker die Funktion cniSetAttribute aufgerufen, um die Werte der konfigurierten Attribute 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) ;
    }
Beginn der ÄnderungDie Anzahl der Konfigurationsattribute in einem Knoten ist nicht begrenzt. Ein Plug-in-Knoten darf jedoch kein Attribut implementieren, das bereits als Basiskonfigurationsattribut implementiert ist. Zu diesen Basisattributen zählen:
  • label
  • userTraceLevel
  • traceLevel
  • userTraceFilter
  • traceFilter
Ende der Änderung

Knotenfunktionen implementieren

Wenn der Broker einen Empfangsknoten entdeckt, ruft er in regelmäßigen Intervallen die Funktion cniRun dieses Knotens auf. Durch die Funktion cniRun wird die weitere Vorgehensweise entschieden. Wenn Daten zur Verarbeitung vorhanden sind, sollte die cniRun-Funktion versuchen, die Nachricht weiterzugeben. Wenn keine Daten zur Verarbeitung vorhanden sind, sollte von der cniRun-Funktion CCI_TIMEOUT zurückgegeben werden, damit der Broker mit den Konfigurationsänderungen fortfahren kann.

Gehen Sie beispielsweise folgendermaßen vor, um den Knoten für den Aufruf von cniDispatchThread und die Verarbeitung der Nachricht oder für die Rückgabe mit CCI_TIMEOUT zu konfigurieren:
If ( anything to do )
	CniDispatchThread;

   /* do the work */

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

Standardmäßige Attribute des Nachrichtparsers überschreiben (optional)

Die Implementierung eines Empfangsknotens bestimmt normalerweise, durch welchen Nachrichtenparser eine Eingabenachricht anfangs syntaktisch analysiert wird. Beispiel: Der Primitive-Knoten MQInput gibt an, dass ein MQMD-Parser für die Syntaxanalyse des MQMD-Headers erforderlich ist. Durch einen benutzerdefinierten Empfangsknoten können ein entsprechender Header oder Nachrichtenparser sowie der Modus ausgewählt werden, durch den die Syntaxanalyse gesteuert wird, indem die folgenden, standardmäßig enthaltenen Attribute verwendet werden, die überschrieben werden können:

rootParserClassName
Definiert den Namen des Root-Parsers, durch den die vom benutzerdefinierten Empfangsknoten unterstützten Nachrichtenformate syntaktisch analysiert werden. Die Standardeinstellung ist der bereitgestellte Root-Parser GenericRoot, durch den der Broker die verschiedenen Parser zuordnet und verkettet. Dieser Attributwert wird von einem Knoten normalerweise nicht geändert.
firstParserClassName
Definiert den Namen des ersten Parsers in einer Parser-Kette, durch die die Syntaxanalyse des Bitstroms durchgeführt wird. Die Standardeinstellung ist XML.
messageDomainProperty
Optionales Attribut, durch das der Name des Nachrichtenparsers definiert wird, der für die Syntaxanalyse der Eingabenachricht erforderlich ist. Die unterstützten Werte entsprechen den Werten, die vom MQInput-Knoten unterstützt werden. (Weitere Informationen zum MQEmpfangsknoten finden Sie unter MQEmpfangsknoten.)
messageSetProperty
Optionales Attribut, das die Nachrichtengruppen-ID oder den Nachrichtengruppennamen im Feld Nachrichtengruppe definiert, falls der MRM-Parser durch das Attribut messageDomainProperty angegeben wurde.
messageTypeProperty
Optionales Attribut, das die Nachrichten-ID im Feld Nachrichtentyp definiert, falls der MRM-Parser durch das Attribut messageDomainProperty angegeben wurde.
messageFormatProperty
Optionales Attribut, das das Nachrichtenformat im Feld Nachrichtenformat definiert, falls der MRM-Parser durch das Attribut messageDomainProperty angegeben wurde.
Durch das Schreiben eines benutzerdefinierten Empfangsknotens, der immer mit den Daten einer bekannten Struktur beginnt, können Sie sicherstellen, dass ein bestimmter Parser die Ausführung am Beginn der Daten startet. Der Knoten 'MQInputNode' liest beispielsweise nur Daten aus den WebSphere MQ-Warteschlangen, somit beginnen diese Daten immer mit 'MQMD', und für 'firstParserClassName' wird vom Knoten 'MQInputNode' 'MQHMD' festgelegt. Wenn Ihr benutzerdefinierter Knoten immer Daten bearbeitet, die mit einer Struktur beginnen, für die von einem bestimmten Parser (z. B. "MYPARSER") eine Syntaxanalyse durchgeführt werden kann, wird 'firstParserClassName' folgendermaßen auf 'MYPARSER' festgelegt:
  1. Deklarieren Sie die folgenden Implementierungsfunktionen:
    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. Legen Sie das Attribut in der cniCreateNodeContext-Implementierungsfunktion fest:
    CciContext* _createNodeContext(
      CciFactory* factoryObject,
      CciChar*    nodeName,
      CciNode*    nodeObject
    ){
      NODE_CONTEXT_ST* p;
      ...
    
        /* Allocate a pointer to the local context */
        p = (NODE_CONTEXT_ST *)malloc(sizeof(NODE_CONTEXT_ST));
        /* Create attributes and set default values */
        {
          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);
          /*see sample BipSampPluginNode.c for implementation of insAttrTblEntry*/
    
          _setAttribute(p, (CciChar*)ucsAttrName, (CciChar*)ucsAttrValue);
          free((void *)ucsAttrName) ;
          free((void *)ucsAttrValue) ;
        }
    Im Codebeispiel oben wird die Methode insAttrTblEntry aufgerufen. Sie ist in den benutzerdefinierten Beispielknoten 'Umschaltungsknoten' und 'Umsetzungsknoten' deklariert.

Knoteninstanz löschen

Knoten werden beim erneuten Einsetzen eines Nachrichtenflusses oder beim Stoppen des Prozesses der Ausführungsgruppe mit Hilfe des Befehls mqsistop gelöscht. Durch das Löschen des Knotens sollte der verwendete Speicher und darin enthaltene Ressourcen freigegeben werden. Verwenden Sie dazu die Funktion cniDeleteNodeContext. Beispiel:

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

  return;
}
Bemerkungen | Marken | Downloads | Bibliothek | Unterstützung | Rückmeldung
Copyright IBM Corporation 1999, 2006 Letzte Aktualisierung: 23. Aug. 2006
as09960_