Empfangsknoten in C erstellen

Vorbereitungen

Stellen Sie sicher, dass Sie folgende Abschnitte gelesen und verstanden haben:

Eine ladbare Implementierungsbibliothek (Loadable Implementation Library, LIL) ist das Implementierungsmodul für einen C-Knoten (oder -Parser). Eine LIL ist als DLL-Datei implementiert. Sie hat jedoch nicht die Dateierweiterung .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. Sie können diese Knoten in ihrem aktuellen Zustand verwenden, oder Sie können sie ändern.

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. Standardattribute für Nachrichtenparser überschreiben (optional)
  6. Instanz des Knotens löschen

Knoten deklarieren und definieren

Beginn der ÄnderungSie 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:Ende der Änderung

  1. Die Initialisierungsfunktion bipGetMessageflowNodeFactory wird vom Broker aufgerufen, nachdem die LIL vom Betriebssystem geladen und initialisiert wurde. Der Broker ruft diese Funktion auf, um zu erkennen, wozu Ihre LIL in der Lage ist und wie der Broker die LIL aufrufen 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};
    	/* Definition der Funktionstabelle mit Zeigern zu Funktionen für Knotenimplementierung */
    vftable.iFpCreateNodeContext = _createNodeContext;
    vftable.iFpDeleteNodeContext = _deleteNodeContext;
    vftable.iFpGetAttributeName2 = _getAttributeName2;
    vftable.iFpSetAttribute      = _setAttribute;
    vftable.iFpGetAttribute2     = _getAttribute2;
    	vftable.iFpRun = _run;
    	cniDefineNodeClass(0, factoryObject, L"InputxNode", &vftable);
    }
    /* Rückkehradresse dieses Factory-Objekts zum Broker */
    return(factoryObject);
    }

    Ein benutzerdefinierter Knoten identifiziert sich selbst als Empfangsknoten, indem er die Implementierungsfunktion cniRun implementiert.

    Beginn der ÄnderungBenutzerdefinierte 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. Ende der Änderung

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

    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.

    Beginn der ÄnderungInformationen zum Code, der mindestens erforderlich ist, um einen benutzerdefinierten Knoten in C zu kompilieren, finden Sie unter Basiscode für C-Knoten. Ende der Änderung

Instanz des Knotens erstellen

Im Folgenden wird die Vorgehensweise bei der Erstellung einer Instanz des Knotens beschrieben:

  1. Wenn der Broker die Tabelle mit den Funktionszeigern empfangen hat, ruft er für jede Instanzerstellung des benutzerdefinierten Knotens die Funktion cniCreateNodeContext auf. Wenn Sie über drei Nachrichtenflüsse verfügen, die Ihren benutzerdefinierten Knoten verwenden, wird Ihre Funktion cniCreateNodeContext für jeden Nachrichtenfluss 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. 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 Knotenobjektzeiger im Kontext:
      p->nodeObject = nodeObject;
    4. Speichern Sie den Knotennamen:
      CciCharNCpy((CciChar*) &p->nodeName, nodeName, MAX_NODE_NAME_LEN);
    5. Beginn der ÄnderungGeben Sie den Knotenkontext zurück:
      return (CciContext*) p;
      Ende der Änderung
  2. Einem Empfangsknoten sind eine Reihe von Ausgabeterminals zugeordnet, er verfügt jedoch normalerweise nicht über Eingabeterminals. Verwenden Sie die Dienstprogrammfunktion cniCreateOutputTerminal, um einem Empfangsknoten Ausgabeterminals hinzuzufügen, wenn eine Instanz des Knotens erstellt wird. Diese Funktionen müssen in der Implementierungsfunktion cniCreateNodeContext aufgerufen werden. Im Folgenden finden Sie ein Beispiel für die Definition eines Empfangsknotens mit drei Ausgabeterminals:
    {
    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) ;    }
    Beginn der ÄnderungInformationen zum Code, der mindestens erforderlich ist, um einen benutzerdefinierten Knoten in C zu kompilieren, finden Sie unter Basiscode für C-Knoten. Ende der Änderung

Attribute festlegen

Attribute werden festgelegt, wenn Sie den Broker starten oder wenn Sie den Nachrichtenfluss mit neuen Werten erneut implementieren.

Nach der Erstellung von Ausgabeterminals ruft der Broker die Funktion cniSetAttribute auf, um die Werte für die 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) ;
}

Knotenfunktionen implementieren

Wenn der Broker weiß, dass er über einen Empfangsknoten verfügt, ruft er die Funktion cniRun dieses Knotens in regelmäßigen Abständen auf. Die Funktion cniRun muss dann entscheiden, welche Maßnahme ergriffen werden soll. Beginn der ÄnderungWenn 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. Ende der Änderung

Im Folgenden finden Sie ein Beispiel für die Konfiguration des Knotens für den Aufruf von cniDispatchThread und die Verarbeitung der Nachricht oder die Rückgabe mit CCI_TIMEOUT:
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;

Standardattribute für Nachrichtenparser überschreiben (optional)

Eine Empfangsknotenimplementierung bestimmt normalerweise, welcher Nachrichtenparser eine Eingabenachricht anfangs analysiert. Der primitive MQEmpfangsknoten gibt beispielsweise vor, dass für die Syntaxanalyse des MQMD-Headers ein MQMD-Parser erforderlich ist. Ein benutzerdefinierter Empfangsknoten kann einen geeigneten Header oder Nachrichtenparser und den Modus, in dem die Syntaxanalyse gesteuert wird, mit Hilfe der folgenden Attribute auswählen, die standardmäßig enthalten sind und die Sie überschreiben können:

rootParserClassName
Definiert den Namen des Stamm-Parsers, der Nachrichtenformate analysiert, die vom benutzerdefinierten Empfangsknoten unterstützt werden. Der Standardwert ist GenericRoot, ein bereitgestellter Stamm-Parser, der den Broker veranlasst, Parser zuzuordnen und zu verketten. Es ist unwahrscheinlich, dass ein Knoten diesen Attributwert ändern muss.
firstParserClassName
Definiert den Namen des ersten Parsers in einer möglichen Kette von Parsern, die für die Syntaxanalyse des Bitstroms verantwortlich sind. Der Standardwert ist XML.
messageDomainProperty
Ein optionales Attribut, das den Namen des Nachrichtenparsers definiert, der für die Syntaxanalyse der Eingabenachricht erforderlich ist. Die unterstützten Werten entsprechen den vom MQEmpfangsknoten unterstützten Werten. (Weitere Informationen zum MQEmpfangsknoten finden Sie unter MQEmpfangsknoten.)
messageSetProperty
Ein optionales Attribut, das die Nachrichtengruppen-ID oder den Nachrichtengruppennamen im Feld Nachrichtengruppe definiert, aber nur wenn der MRM-Parser durch das Attribut messageDomainProperty angegeben wurde.
messageTypeProperty
Ein optionales Attribut, das die Nachrichten-ID im Feld Nachrichtenart definiert, aber nur wenn der MRM-Parser durch das Attribut messageDomainProperty angegeben wurde.
messageFormatProperty
Ein optionales Attribut, das das Format der Nachricht im Feld Nachrichtenformat definiert, aber nur wenn der MRM-Parser durch das Attribut messageDomainProperty angegeben wurde.
Wenn Sie einen benutzerdefinierten Empfangsknoten geschrieben haben, der immer mit Daten einer bekannten Struktur beginnt, können Sie sicherstellen, dass sich ein bestimmter Parser mit dem Anfang der Daten befasst. Der MQEmpfangsknoten liest beispielsweise nur Daten aus WebSphere MQ-Warteschlangen, deshalb beginnen diese Daten immer mit MQMD, und der MQEmpfangsknoten legt für 'firstParserClassName' MQHMD fest. Wenn Ihr benutzerdefinierter Knoten immer für Daten zuständig ist, die mit einer Struktur beginnen, die von einem bestimmten Parser analysiert werden kann, z. B. "MYPARSER", setzen Sie 'firstParserClassName' wie folgt auf MYPARSER:
  1. Deklarieren Sie die 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 Implementierungsfunktion cniCreateNodeContext 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.

Instanz des Knotens löschen

Knoten werden gelöscht, wenn ein Nachrichtenfluss erneut implementiert wird oder wenn der Ausführungsgruppenprozess gestoppt wird (mit dem Befehl mqsistop). Wenn ein Knoten gelöscht wird, sollte der gesamte verwendete Speicher und die verwendeten Ressourcen freigegeben werden. Dazu verwenden Sie 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: 18.05.2006
as09960_