Lade...
 

CORBA Schnittstelle

Die Corba-Schnittstelle des ClassiX®-Systems

Einleitung

ClassiX® bietet eine Schnittstelle, über die Fremdsysteme mit ClassiX® oder mehrere ClassiX®-Systeme untereinander kommunizieren können. Die Kommunikation beschränkt sich auf das Senden einer Message an ClassiX®. An diese Message können beliebig viele Parameter in Form von Zahlen oder Zeichenketten gehängt werden1. Diese Parameter werden, wie in InstantView® üblich, auf den Stack gelegt. Auf diese Weise ist mit ClassiX® auch der Aufbau von serviceorientierten Architekturen bzw. die Integration von ClassiX® in bereits bestehende dienstorientierte Umgebungen möglich.

Naming Service

Jede ClassiX®-Instanz, die mindestens einen Dienst anbietet, muss sich beim Naming Service von CORBA anmelden. Der Naming Service ist im Falle von TAO ein Programm oder ein Windows-Dienst. Es ist ausreichend, wenn auf einem Rechner irgendwo im Netz der Naming Service läuft.

Standardmäßig benutzen sowohl Naming Service als auch alle ClassiX®-Instanzen IP-Multicast, um den Naming Service im Netz zu finden. Alternativ kann über die Umgebungsvariable NameServiceIOR die Adresse des Naming Services gesetzt werden:

SET NameServiceIOR=iioploc://hostname:port/NameService

Die Umgebungsvariable ist auf jeder Maschine zu setzen, auf der der Naming Service und/oder eine ClassiX®-Instanz läuft.

Alternativ kann der Kommandozeilenparameter -ORBInitRef NameService=iioploc://hostname:port/NameService benutzt werden, bzw. laut TAO-Doku -ORBInitRef NameService=corbaloc:iiop:hostname:port/NameService. Dabei sind für hostname und für port gültige Werte einzutragen, corbaloc, iiop und NameService werden so hingeschrieben. Diese Option gilt für "normale" Client und Server Programme. Um den NamingService selber mit einer Bestimmten Adresse zu starten, kann folgende Kommandozeilenoption verwendet werden: -ORBEndpoint iiop://hostname:port.

Corba-Interface

interface CX_REMOTEMSG_RECEIVER_CORBA
{
exception Timeout
{};

typedef sequence MarshaledStack;

void AcceptMsg_CORBA(in string message, in MarshaledStack stack);

// A variant of the the two-parameter AcceptMsg_CORBA method above. This method expects that the sender
// passes an object reference to itself.
void AcceptMsg_CORBA_BiDi(in string message, in MarshaledStack stack, in CX_REMOTEMSG_RECEIVER_CORBA sender);

// A variant of the the two-parameter AcceptMsg_CORBA method above. This call sends a message synchronously
// and returns another stack with the result send in ClassiX as NULL SendMsg(MSG ,REMOTE)
MarshaledStack SendMsg_CORBA(in string message, in MarshaledStack stack) raises (Timeout);

void Ping();
};

Dieser Dienst hat eine Methode, die den Namen einer Message aufnimmt und einen Stack. Der Stack kann auch leer sein. Zusätzliches gibt es eine Method "Ping()" die als eine Art Dummy fungiert, über den andere ClassiX®-Instanzen im Netzwerk schnell testen können, ob eine gegebene Instanz antwortet. Die Ping-Methode wird in ClassiX® einfach leer implementiert.

ClassiX® erzeugt ein solches Objekt und registriert es beim CORBA Namingservice. Jede ClassiX®-Instanz offeriert diesen Dienst immer nur einmal.

Der Dienst wird innerhalb einer Struktur beim Naming Service angemeldet. Unterhalb der Wurzel (Root) wird ein Ordner "ClassiX" angelegt, darunter (optional) ein Ordner für das aktuelle ClassiX-Projekt. Darunter wird unter dem Namen "RemoteMsg" ein für alle Server mit dem Dienst zum Empfangen von Messages angeboten. Darin sind die aktuelle ClassiX®-Instanzen. Der Name setzt sich aus Computernamen, IP-Adresse, PID der ClassiX®-Instanz, User-ID, Anmeldedatum und dem/den Name(n) der Datenbank(en) zusammen, getrennt durch " | ". Beispiel (In diesem Bild sind RemoteMsg und Namen (TH...) vertauscht, ein Projekt ist nicht angegeben)

Ein als Dienst laufendes, am Corba Namignservice registriertes Objekt wird "Servant" genannt. In diesem Beispiel haben sich 2 ClassiX®-Instanzen beim Naming Service von CORBA angemeldet, d.h. 2 ClassiX®-Instanzen auf demselben Rechner (TH) haben die Funktion 'EnableRemoteMessages' von CX_REMOTE_MANAGER (oder CX_CORBA_MANAGER) aufgerufen, bzw. haben sich auf andere Weise angemeldet.

Die Instanzen selbst bieten verschiedene Dienste an. Bisher ist nur der Dienst "RemoteMsg" implementiert. Ein Dienst ist aus technischer Sicht ein Objekt, das ein oder mehrere Methoden anbietet. Dies ist das Interface eines solchen Objektes. Dieses Objekt stellt einen Dienst öffentlich zur Verfügung, nämlich den Dienst, eine Message entgegen zu nehmen.

Zur Terminologie von Corba bezüglich Namen: Der Corba-Dienst der die Bindung von Namen im Netzwerk anbietet wird Naming Service genannt, aber auch NamingService, Namingservice, NameService, ... evtl. auch mit vorangestelltem Corba oder Corba-. Gemeint ist immer das gleiche. Ein Corba-Name (Corbaname) kann aus mehreren Komponenten bestehen (eben den Namenskomponenten). Wie in der obigen Abbildung zu erahnen ist, kann durch die Wahl von Namenskomponenten eine Hierarchie entstehen. Ganz oben die Wurzel, die keinen ausdrücklichen Namen hat (Das "Root" dient hier nur der Anschauung). Eine Namenskomponente kann entweder ein Naming Context sein, dann können in der Hierarchie unter diesem Namen weitere Nameskomponenten gebunden werden, oder eine Namenskomponente kann ein "Objekt" bezeichnen, unter dem keine weiteren Namen mehr möglich sind. Die Namenskontexte müssen von den Anwendungen nur beim Naming Service registriert (gebunden) werden. Hinter Objekten hingegen steht eine Implementierung, ein sogenannter Servant. Ein Servant implementiert dabei ein Corba-Interface. In Classix implementieren Objekte, die unterhalb des Naming-Contexts RemoteMsg registriert werden, das Interface CX_REMOTEMSG_RECEIVER_CORBA implementieren (siehe nächsten Abschnitt). Die Implementierung eines Interfaces muss nicht überall gleich sein, z. B. implementiert Cxsendmsg das gleiche Interface wie Classix, aber in einer unterschiedlichen Weise. Um diese Objekte zu unterscheiden können sie in unterschiedlichen Projekten aufgehängt sein (s.o, der mittlere Teil des Namensbaums) oder eindeutige Namen besitzen.

Nicht jede Corba-Anwendung muss auch einen Servant implementieren, sondern Corba kann auch eine Objektreferenz (nicht zu verwechseln mit einer Referenz in C++) liefern, über die ohne größere Umschweife Funktionen aufgerufen werden können.

Parameter

Beim Aufruf der AcceptMsg_CORBA-Funktion des obigen Interfaces enthält das Argument "message"  den Namen der Message im Klartext (z.B. SELECT). Entsprechendes gilt auch für die anderen Methoden.

"stack" enthält den Stack, den der Empfänger der Nachricht vorfinden soll. Das letzte Element der Sequenz liegt ganz oben auf dem Stack, d.h. die Elemente werden von vorne nach hinten auf den Stack gepusht.
Ein Element wird immer als Corba-String kodiert. Die folgende Tabelle erklärt die Zuordnung zwischen String und Typ:

Typ String Beispiel
Integer i i123
String s"" s"Ein Beispiel"
NULL-Objekt n n
  m1 m1
  m2 m2
Persistentes Objekt p p<0|38|0|228|10000>
Persistente Collection c c<0|38|0|228|10000>

Im Moment können nur Integers und Strings und NULL-Objekte übertragen werden. Vektoren können bei Bedarf schnell implementiert werden.

Der Stack kann natürlich auch leer sein. Der genaue Aufbau des Stacks hängt von der Nachricht ab, die gesendet wird. Empfänger und Sender müssen sich diesbezüglich einigen.

In ClassiX®  liefert die Methode GetLocation() von CX_DB_UTILITY den String, der ein persistentes Objekt oder eine persistente Collection beschreibt. Beispiel:

CreatePersObject(CX_PERSON) GetManager(OBJECT) Call(GetDBUtility) Call(GetLocation)
// Nun liegt ein String mit der stringifizierten Adresse des persistenten Objektes auf dem Stack

Rückgabewerte

AcceptMsg_CORBA kehrt sofort zurück und gibt kein Ergebnis zurück. Die Ausführung des getriggerten InstantView®-Codes erfolgt asynchron zu einem späteren Zeitpunkt.

Die Rückgabe eines Ergebnisses lässt sich dennoch erreichen, indem eine Message das Ergebnis einer anderen Anwendung übermittelt. Diese Anwendung muss nicht zwingend eine ClassiX®-Instanz sein, sondern kann eine beliebige sein, solange sie

  • ein identisches CORBA-Interface besitzt und
  • sich wie der ClassiX®-Dienst "RemoteMsg" beim Naming Service anmeldet.

Beispiel: Eine Applikation namens XYZ erzeugt ein Objekt mit obigem Interface und meldet es unter dem Namen "ClassiX/RemoteMsg/XYZ" beim Naming Service an. Anschließend sendet XYZ der gewünschten ClassiX®-Instanz die Message. Im InstantView®-Code wird die Message nun verarbeitet, und das Ergebnis wird anschließend mit CX_CORBA_MANAGER::SendMsg an XYZ gesendet.

Alternativ (und einfacher zu implementieren) kann die Methode SendMsg_CORBA verwendet werden. Diese verarbeitet die Message synchron und kehrt erst zurück wenn entweder

  • die gesamte Verarbeitung beendet wurde. Dann wird ein leerer Stack zurückgegeben.
  • oder ein return Stack mittels NULL SendMsg(MSG, REMOTE) angemeldet wird.

Skalierung

ClassiX® erzeugt für die Bearbeitung von Corba-Messages einen eigenen Thread. So werden Messages jederzeit angenommen, unabhängig davon, welcher InstantView®-Code gerade ausgeführt wird.

Neue Messages werden ganz hinten in die Warteschlange gestellt, d.h. die Messages werden erst dann verarbeitet, wenn keine anderen Befehle zur Ausführung anstehen. Das bedeutet, dass nicht gesagt werden kann, wann eine Message verarbeitet wird. Es wird nur garantiert, dass die Messages in der Reihenfolge verarbeitet werden in der sie eintreffen. Zu beachten ist, das Corba selber nicht immer garantiert, das zuerst gesendete Nachrichten auch zuerst zugestellt werden.

Mehrere ClassiX-®-Server können mit demselben Projektnamen gestartet werden. Der Client (z.B. cxsendmsg) kann dann mithilfe des NamingService nach "ClassiX//RemoteMsg" suchen und eine beliebige dort angemeldete Instanz verwenden.

Beispielcode in C++

Der untenstehende Code zeigt das Versenden einer Message an eine ClassiX®-Instanz. Zusätzlich zu diesem Code sind die vom IDL-Compiler aus dem obigen Interface generierten Quellen nötig.

// If ACE/TAO is used as CORBA implementation, the following includes are necessary: #include <tao/corba.h> #include <CosNamingC.h> // Include the sources generated by the IDL compiler #include "cxRemoteMsgC.h" // This example sends the message "HELLO" together with a greeting string to a foreign ClassiX® instance const char *ClassiXInstance = "COMPUTERNAME 123"; // Initialize ACE/TAO CORBA ACE::init(); CORBA::ORB_var orb = CORBA::ORB_init(argc, argv, "ClassiX SendMsg"); // Prepare parameters for AcceptMsg_CORBA const char *message = "HELLO"; CX_REMOTEMSG_RECEIVER_CORBA::MarshaledStack stack; stack.length(1); stack[0] = CORBA::string_dup("s\"Hello World!\""); // Get reference to initial naming context CosNaming::NamingContext_var naming_root; CORBA::Object_var naming_obj = orb->resolve_initial_references("NameService"); naming_root = CosNaming::NamingContext::_narrow(naming_obj.in()); // Look up receiver object, i.e. the service "RemoteMsg" of the desired ClassiX® instance CosNaming::Name name; name.length(4); name[0].id = CORBA::string_dup("ClassiX"); name[1].id = CORBA::string_dup("Project"); name[2].id = CORBA::string_dup("RemoteMsg"); name[3].id = CORBA::string_dup(ClassiXInstance); CORBA::Object_var obj = naming_root->resolve(name); // Call AcceptMsg_CORBA CX_REMOTEMSG_RECEIVER_CORBA_var receiver = CX_REMOTEMSG_RECEIVER_CORBA::_narrow(obj.in()); receiver->AcceptMsg_CORBA(message, stack);

Notizen zur Anwendung

Eine ClassiX®-Instanz muss einen Remote- oder einen Corbamanager erzeugen und die zugehörige EnableRemoteMsg-Methode dieser Objekte aufrufen bevor Corba-Messages bearbeitet werden oder Nachrichten über Corba versendet werden können. Zum Versenden von Nachrichten dienen die SendMsg-Methoden dieser Objekte. Da Corba nur eine von mehreren denkbaren Schnittstellen für nachrichtenbasierte Kommunikation ist, wurde die Programmier-Schnittstelle von ClassiX® aufgespalten, in einen Teil der die Grundfunktionen aller Netzwerkschnittstellen nutzbar macht (dies ist der Remotemanager) und einen weiteren Teil der die spezifische Funktionalität von Corba anbietet. So kann der InstantView Programmierer in vielen Fällen die konkrete Netzwerkschnittstelle ignorieren - die Geschäftsprozesse werden gleichartig implementiert.

Als Beispiel soll hier gezeigt werden wie man mit Hilfe des Corbamanagers eine Liste der ClassiX®-Instanzen im Netzwerk erhalten kann. Die Kernoperation ist der Aufruf der RemoteClients-Methode des Corbamanagers, welche einen Vektor von beim Corba Naming Service registrierten Instanzen zurückgibt. Das Beispiel zeigt wie dieser Vektor durchlaufen wird und dabei die Instanz die das lokale ClassiX® bezeichnet herausgesucht wird. Die Variable 'thisComputer' wurde dazu mit dem Rechnernamen (GetComputerName) belegt, und 'thisPID' mit der Prozess-Id (GetPID). Weiterhin wird angenommen das die lokale Instanz sich beim Naming Service gemäß dem obigen Schema registriert hat, der Corbaname also tatsächlich Rechnernamen und PID enthält.

Var(CorbaManager)
GetManager(CORBA) -> CorbaManager
CorbaManager Call(RemoteClients) #
iterate {
  Dup thisComputer StringFind if {
    // found 'thisComputer'-substring
    Dup thisPID StringFind if {
      // found 'thisPID'-substring
      break // found the right string
    }
  }
  Drop // wrong name
}

// store the correct name
-> thisComputer

Nach dem Ausführen dieses Codes enthält 'thisComputer' nicht mehr den Rechnernamen, sondern den Corbanamen der ClassiX®-Instanz.

 

_

1 In der Zukunft kann auch das Übertragen anderer InstantView-Typen implementiert werden, zum Beispiel von Vektoren und NULL-Objekten.