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 'TransformNode' bereit. Sie können diese Knoten in ihrem aktuellen Zustand verwenden, oder Sie können sie ändern.

In diesem Abschnitt werden die Schritte beschrieben, die Sie ausführen müssen, um einen Empfangsknoten mit C zu erstellen. Folgende Schritte werden erläutert:
  1. Knoten initialisieren
  2. Knoten als Empfangsknoten definieren
  3. Instanz des Knotens erstellen
  4. Attribute festlegen
  5. Knotenfunktionen implementieren
  6. Standardattribute für Nachrichten-Parser überschreiben (optional)
  7. Instanz des Knotens löschen

Knoten initialisieren

Im Folgenden wird die Vorgehensweise bei der Initialisierung des Knotens beschrieben:

  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. Die Funktion bipGetMessageflowNodeFactory ruft dann die Dienstprogrammfunktion cniCreateNodeFactory auf. Diese Funktion gibt einen Factory-Namen (oder Gruppennamen) für alle Knoten zurück, die Ihre LIL unterstützt. Beispiel:
    {
    	CciFactory* factoryObject;
    	int rc = 0;
    	CciChar factoryName[] = L"SwitchNodeFactory";
    	CCI_EXCEPTION_ST exception_st;
    
    	/* Create the Node Factory for this node */
    	factoryObject = cniCreateNodeFactory(0, factoryName);
    	if (factoryObject == CCI_NULL_ADDR) {
    		if (rc == CCI_EXCEPTION) {
    			/* Get details of the exception */
    			cciGetLastExceptionData(&rc, &exception_st);
    
    			/* Any local error handling can go here */
    
    			/* Rethrow the exception */
    			cciRethrowLastException(&rc);
    		}
    
    		/* Any further local error handling can go here */
    	}
    	else {
    		/* Define the nodes supported by this factory */
    		defineSwitchNode(factoryObject);
    	}
    
    	/* Return address of this factory object to the broker */
    	return(factoryObject);
    }

    Werden in diesem Beispiel Trace-Information im Format UTF-16 erforderlich, müssen Sie CCI_EXCEPTION_ST durch CCI_EXCEPTION_WIDE_ST und cciGetLastExceptionData durchcciGetLastExceptionDataW ersetzen.

  3. Anschließend sollte die LIL die Dienstprogrammfunktion cniDefineNodeClass aufrufen, um die Namen der einzelnen Knoten und eine virtuelle Funktionstabelle der Adressen der Implementierungsfunktionen zu übergeben. So definieren Sie beispielsweise einen einzelnen Knoten mit dem Namen 'InputxNode' und dessen Funktionstabelle:
    void defineInputxNode(void* factoryObject){
    	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;
    }

Knoten als Empfangsknoten definieren

Ein benutzerdefinierter Knoten identifiziert sich selbst als Empfangsknoten, indem er die Implementierungsfunktion cniRun implementiert. Benutzerdefinierte Empfangsknoten müssen eine cniRun-Funktion 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. Wenn ein Nachrichtenfluss, der einen benutzerdefinierten Empfangsknoten enthält, erfolgreich implementiert wird, ruft der Broker die Implementierungsfunktion cniRun in regelmäßigen Abständen auf.

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.

Wenn ein Nachrichtenfluss, der einen benutzerdefinierten Empfangsknoten enthält, erfolgreich implementiert wird, wird die Funktion cniRun des Knotens für jede Nachricht, die den Nachrichtenfluss durchläuft, aufgerufen.

Empfangsknoten können auch cniEvaluate implementieren, dies wird jedoch nicht empfohlen.

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* _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 Knotenobjektzeiger im Kontext:
         p->nodeObject = nodeObject;
    4. Speichern Sie den Knotennamen:
       CciCharNCpy((CciChar*)&p->nodeName, nodeName, MAX_NODE_NAME_LEN);
  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) ;    }

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. Wenn Daten zur Verarbeitung verfügbar sind, sollte die FunktioncniRun cniDispatchThread aufrufen und die Nachricht verarbeiten oder mit CCI_TIMEOUT zurückgeben, so dass der Broker mit der Verarbeitung anderer Nachrichten in anderen Threads fortfahren kann. Wenn ein Thread nicht zugeteilt wird, verbringt der Broker seine gesamte Zeit in diesem Thread, was ihn daran hindert, etwas anderes zu tun.

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 Nachrichten-Parser überschreiben (optional)

Eine Empfangsknotenimplementierung bestimmt normalerweise, welcher Nachrichten-Parser 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 Nachrichten-Parser 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 Nachrichten-Parsers 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) ;
        }

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, 2005 Letzte Aktualisierung: Nov 17, 2005
as09960_