Aus Linux-Magazin 02/2003

Corba-Clients und -Server mit Combat in Tcl programmieren

Corba verbindet Software-Objekte als Client und Server über Rechner- und Plattformgrenzen hinweg. Mit Combat (Maskottchen ist die Fledermaus) lässt sich diese leistungsfähige Middleware auch in Tcl nutzen: Nicht nur Clients, auch Corba-Server kann man so vollständig in Tcl implementieren.

Zurzeit sind viele Lösungen für den Zugriff auf verteile Objekte in Gebrauch. Große Bedeutung haben Microsofts DCOM, SOAP, Java RMI und Corba. Corba gilt als die am besten ausgereifte Lösung, anders als die anderen drei ist es wirklich für beinahe jedes Betriebssystem und jede Programmiersprache erhältlich, so auch für Tcl.

Mäusefutter

Unter Tcl gibt es mehrere Möglichkeiten, Corba zu verwenden. Die beste Lösung ist Combat von Frank Pilhofer[3]. Combat basiert auf dem in C++ geschriebenen ORB Mico, dessen Entwicklung an der Frankfurter Universität begann und das als Open-Source-Projekt fortgeführt wird[4]. Um Server zu implementieren, ist außerdem die objektorientierte Erweiterung iTcl (auch [incr Tcl] genannt) erforderlich[5],[9].

Mico und iTcl gehören bei den meisten Linux-Distributionen zum Standardumfang – falls sie fehlen, lassen sie sich aus den Quellen mit dem üblichen Dreisprung »./configure && make && make install« installieren. Danach kommt die Installation von Combat an die Reihe, auch hier genügt dies gewohnte Verfahren. Damit Combat als ladbare Erweiterung kompiliert wird, darf jedoch die Option »–enable-shared« bei »./configure« nicht fehlen.

Wer die Installation von Mico und Combat-Bibliotheken auf mehreren Rechner zu aufwändig findet, dem sei ein Blick auf die reine Tcl-Implementierung von Combat empfohlen. Hier ist der ORB selbst in Tcl implementiert und an die Stelle von iTcl tritt das reine Tcl-Paket Tcl++[6]. Allerdings ist diese Lösung auf die Client-Seite beschränkt und enthält nicht alle Entwicklungswerkzeuge, sodass Combat zumindest einmal auf Mico-Basis installiert sein muss.

Per definitionem

Der grundlegende Aufbau von Corba ist in Abbildung 1 dargestellt. Client und Server enthalten je einen ORB, der zwischen Netzwerk und Programmierumgebung vermittelt. Auf beiden Seiten ist die Programmierung fast so, als ob es sich um ein normales, lokales Objekt handelte. Damit dies nahtlos bei Server und Clients funktioniert, ist ein Kontrakt über die Klassen, Methoden und Felder notwendig. Bei Corba geschieht dies mit Hilfe der Interface Definition Language (IDL). Die IDL beschreibt die Schnittstellen der Objekte, sie dient nicht zur Implementierung von Client oder Server. IDL-Dateien ähneln auf den ersten Blick C++-Headerfiles, ein Beispiel findet sich in Listing 1.

In dem Beispiel ist eine Klasse (Interface) »Beispiel« definiert, sie hat ein Attribut sowie die Methode »HelloWorld«. Das Attribut hat den Namen »einWert«, es ist nur lesbar und vom Typ »short«. Die Methode gibt einen Returnwert vom Typ »string« zurück, ihre Parameter sind vom Typ »unsigned long« und »double«. Neben Namen und Typ definiert die IDL auch, in welcher Richtung die Methodenparameter verwendet werden. Möglich sind die Richtungen »in«, »out« und »inout«. Parameter mit »in« sind herkömmliche Werteübergaben an eine Methode, bei »out« und »inout« wird stattdessen eine Variable übergeben, die den eigentlichen Wert enthält.

Das Beispiel enthält nur einen kleinen Auszug der Möglichkeiten, neben Vererbung, Namensräumen und Exceptions sind auch Typedefs und benutzerdefinierte Strukturen wie »struct«, typisierte Listen oder Unions möglich. Hier empfiehlt sich bei Bedarf ein Blick in die umfangreiche Literatur über Corba. Besonders empfehlenswert ist, unabhängig von der verwendeten Programmiersprache, das Buch von Henning und Vinovski[7].

Aus IDL mach Tcl

Die IDL definiert nur die Schnittstellen, Clients wie Server werden in Tcl oder einer anderen Sprache implementiert. Dazu ist eine Sprachabbildung notwendig, die aus der IDL-Beschreibung Code und Schnittstellen in der jeweiligen Programmiersprache generiert. Da es für Tcl keine offizielle Sprachabbildung der OMG gibt, hat Frank Pilhofer selbst eine definiert. Die Dokumentation von Combat enthält hierzu mehrere Seiten, im Folgenden werden nur die für Listing 2 und 4 benötigten Teile vorgestellt.

Der einfachste Weg, aus der IDL-Beschreibung Tcl-Code zu generieren, ist das Entwicklungswerkzeug »idl2tcl«:

idl2tcl --impl beispiel.idl

Aus der IDL-Vorlage »beispiel.idl« erzeugt das Kommando die Files »beispiel .tcl« und »beispiel_impl.tcl«. Das erste enthält eine Beschreibung der Schnittstellen für den ORB, das zweite ist die Basis für die Serverentwicklung. Sie enthält die iTcl-Klassen, Attribute und Methoden und muss nur noch mit einem Konstruktor sowie der Implementierung der Methoden ergänzt werden.

Das Ergebnis ist in Listing 2 zu sehen. »idl2tcl« erzeugt aus dem IDL-Interface eine gleichnamige iTcl-Klasse. Das Attribut »einWert« mutiert zu einer öffentlichen Variablen, die Interface-Methode entsprechend zu einer iTcl-Methode. Da Tcl als Sprache kein Typsystem hat, werden alle Variablen unabhängig vom Typ zu normalen Tcl-Variablen. Während Tcl recht großzügig mit Typen umgeht, schreibt Corba eine strenge Typprüfung vor. Sobald ein Wert an Corba übergeben wird, muss er dem in der IDL beschriebenen Typ entsprechen, sonst gibt es zur Laufzeit einen Fehler.

Die »in«-Parameter bei den Methoden verhalten sich wie normale Parameter, sie enthalten Werte. Bei den »out«- und »inout«-Parametern wird statt des Werts der Name der Variablen an die Methode übergeben. Somit kann die Methode den Wert der Variablen lesen und ändern: Die »upvar«-Zeile erreicht, dass innerhalb der Methode eine Variable »b« existiert, die sich wie die Variable in der aufrufenden Funktion verhält. Sie hat denselben Wert, Änderungen wirken sich in beiden Richtungen aus.

Auf dieses Vorspiel folgt ab Zeile 24 die normale Implementierung der Methode, in diesem Fall erhält die »out«-Variable »b« die Wurzel der Eingabe. Der Rückgabewert lautet »Hello World« gefolgt von der als Datum formatierten Eingabe. Obwohl später ein beliebiger Client in einer anderen Sprache auf einem anderen Rechner diese Methode aufrufen kann, ist die Implementierung nicht anders als bei einer normalen iTcl-Methode.

Die Implementierung enthält zusätzlich noch einen Konstruktor, der den Wert der Variablen »einWert« setzt, sowie die »_Interface«-Methode. Letztere ist für die Koppelung mit Corba notwendig, ebenso das Erben der Klasse »::PortableServer::ServantBase«. Listing 2 enthält also die Implementierung der in der IDL beschriebenen Schnittstellen in Form einer iTcl-Klasse, dies wird als Servant bezeichnet. Damit ein Client auf den Servant über Corba zugreifen kann, muss eine Server-Instanz den Servant im Netzwerk zur Verfügung stellen. Das geschieht in Listing 3.

Abbildung 1: Corba-Clients verwenden Objekte, die so in dem Server liegen, als ob sie lokal auf dem Client wären. Der ORB (Object Request Broker) überträgt die Methodenaufrufe und -antworten, ein spezieller Bindecode sorgt für die erforderliche Anpassung.

Abbildung 1: Corba-Clients verwenden Objekte, die so in dem Server liegen, als ob sie lokal auf dem Client wären. Der ORB (Object Request Broker) überträgt die Methodenaufrufe und -antworten, ein spezieller Bindecode sorgt für die erforderliche Anpassung.

Hintergrund: Der Corba-Standard

In den 80er Jahren fanden sich mehrere Softwarefirmen wie HP, IBM und Sun in der Object Management Group (OMG)[1] zusammen, um einen neuen Standard für verteilte Objekte zu schaffen. Das Ergebnis ist Corba, die Common Object Request Broker Architecture[2].

In diesem Entwurf verbindet ein Softwarebus Client und Server miteinander. Der Bus funktioniert unabhängig vom verwendeten Betriebssystem und der Programmiersprache. Er besteht aus einer Softwareschicht auf jeder Seite, die dafür sorgt, dass sich entfernte Objekte fast so einfach programmieren lassen wie lokale. Die lästigen Details der Netzwerkprogrammierung – etwa unterschiedliche Prozessorarchitekturen, »bind()« und »select()« sowie die Fehlerbehandlung – übernimmt diese Schicht.

Die Softwareschicht nennt sich Object Request Broker (ORB). Die ORBs kommunizieren über ein einheitliches Protokoll, das unabhängig von der Programmiersprache ist. Die OMG selbst produziert keine Implementierung, nur die Spezifikationen. Diese werden als kommerzielle oder freie Software implementiert, dank der genauen Spezifikation ist die Kommunikation der verschiedenen Produkte recht problemlos. Außer Corba und seinen diversen Diensten wie Time- oder Nameserver stammt übrigens auch der UML-Standard von der OMG.

Neben Client-Server-Software, die speziell für Corba ausgelegt ist (verbreitet im Bankwesen), ist Corba eines der Standardprotokolle von EJB-Servern (Enterprise Java Beans). Damit kann ein Großteil moderner Server mit Corba arbeiten. Mit der Tcl-Erweiterung lassen sich schnell Client-Anwendungen oder Testprogramme schreiben, aber auch komplette Server in Tcl implementieren. Das ist besonders für Rapid Prototyping interessant.

Zu Diensten

Mit der Implementierung der Schnittstellen im Servant ist der Corba-Server fast fertig. Was fehlt, ist die Verbindung der iTcl-Klasse mit dem Netzwerk. Das erledigt der Objekt Request Broker. Er muss zuerst mit »corba::init« initialisiert werden, als Parameter dienen die Kommandozeilenargumente. Damit kann der Benutzer ORB-Parameter angeben. Der ORB selbst ist generisch. Um richtig auf die Anfragen zu reagieren, muss er die verwendete IDL kennen. Das Werkzeug »idl2tcl« hat bereits das File »beispiel.tcl« erzeugt. Dieser TCL-Code enthält den Inhalt der kompletten IDL in der Variablen »_ir_beispiel«.

Folgender Aufruf gibt die Information an das Interface-Repository weiter:

combat::ir add $_ir_beispiel

Das Repository ist jener Teil im ORB, der alle verwendeten Interfaces, benutzerdefinierten Typen und Ähnliches speichert. Ein Portable Object Adapter (POA) verbindet die Klasse (Servant) und den ORB. Der POA sorgt dafür, dass Anfragen an ein bestimmtes Corba-Objekt an den richtigen Servant geleitet werden. Zeile 19 in Listing 3 fragt den ersten POA vom ORB ab, danach wird der POA-Mechanismus aktiviert.

Nun fehlt noch der Servant selbst: Er wird wie jedes normale iTcl-Objekt erzeugt. Das Schlüsselwort »#auto« in Zeile 24 sorgt dafür, dass das neue Objekt automatisch einen Namen erhält, den Wert »42« speichert der Konstruktor in der Variablen »einWert«. Als letzten Schritt muss der POA noch erfahren, dass er diesen Servant als Corba-Objekt exportieren soll – bei Corba als Aktivierung bezeichnet. Die Aktivierung kann auf unterschiedlichen Wegen geschehen, der einfachste ist:

$servant _this

Diese Methode stammt aus der Basisklasse »PortableServer::Servant«, sie aktiviert den Servant und gibt eine Objektreferenz zurück. Die Objektreferenz ist kein Verweis mehr auf den Servant, sondern auf ein Objekt, das die Schnittstellen der IDL implementiert. Wer als Corba-Experte andere Aktivierungsmethoden verwenden will, kann das natürlich tun, Combat stellt alle POA-Methoden zur Verfügung.

Listing 1: Die IDL-Datei

01 #ifndef _Beispiel
02 #define _Beispiel
03 
04 interface Beispiel {
05 
06   readonly attribute short einWert;
07 
08   string helloWorld(
09       in unsigned long a,
10       out double b);
11 };
12 
13 #endif /* _Beispiel */

Listing 2: Der Servant als iTcl-Klasse

01 class ::Beispiel {
02     inherit ::PortableServer::ServantBase
03 
04     constructor {ewert} { set einWert $ewert }
05 
06     public method _Interface {} {
07         return "IDL:Beispiel:1.0"
08     }
09 
10     # type of einWert is short
11     public variable einWert 42
12 
13     public method helloWorld {a b}
14 }
15 
16 #   parameters:
17 #     a: in ulong, b: out double
18 #   returns:
19 #     string
20 #
21 body ::Beispiel::helloWorld {a b_var} {
22     upvar $b_var b
23 
24     puts stdout "nhelloWorld, Eingabe $a"
25     set b [expr sqrt( $a )]
26     set retval "Hello World at [clock format $a]"
27     return $retval
28 }

Listing 3: Der Server

01 #!/bin/sh
02 #
03 # 
04 exec tclsh  "$0" ${1+"$@"}
05 
06 package require Itcl
07 package require combat
08 
09 source beispiel_impl.tcl
10 source beispiel.tcl
11 
12 # Initialisiere Corba
13 puts "Initialisiere Corba"
14 ::corba::init $argv
15 ::combat::ir add $_ir_beispiel
16 
17 # POA verbindet Servant und ORB
18 puts  "Starte POA"
19 set poa [::corba::resolve_initial_references RootPOA]
20 set mgr [$poa the_POAManager]
21 $mgr activate
22 
23 # Servant erzeugen und aktivieren
24 set servant [Beispiel #auto 42]
25 puts "Servant $servant"
26 set obj [$servant _this]
27 
28 # IOR ausgeben
29 puts "Objektreferenz als IOR in server.ior"
30 set fd [open server.ior w]
31 puts $fd [::corba::object_to_string $obj]
32 close $fd
33 
34 vwait forever
Abbildung 2: Das Client-Programm liest die »meinWert«-Variable des Server-Objekts und ruft dann die »helloWorld«-Methode auf.

Abbildung 2: Das Client-Programm liest die »meinWert«-Variable des Server-Objekts und ruft dann die »helloWorld«-Methode auf.

Wo bist du?

Sinn und Zweck von Corba ist, dass ein eigenständiges Client-Programm auf das Objekt zugreifen kann. Hierzu muss die Objektreferenz im Client verfügbar sein. Bei Corba gibt es viele verschiedene Wege, um eine Referenz auf ein Objekt zu finden. Der einfachste benutzt eine IOR, die im Grunde ein langer String mit der Adresse ist.

Der Server erzeugt in Zeile 31 aus seiner Objektreferenz mit »object_to_string« eine IOR und speichert sie in einer Datei. Der IOR-String ist problemlos überallhin transportierbar, jeder ORB kann wieder ein Objekt für diese Referenz erzeugen (wenn er die IDL kennt). Neben dieser einfachen Lösung sind elegantere Varianten wie ein Corba-Nameserver, das Speichern in einem LDAP-Server oder Ähnliches möglich.

Die IOR enthält eine Menge nützlicher Informationen, sie lassen sich mit dem Programm »iordump« ausgeben. Das Tool ist ein reines Analysewerkzeug, es zeigt alle Informationen an, die in der IOR stecken. Neben der Kennzeichnung des Interface (Repository-ID) sind das vor allem die Netzwerkadresse und der Port sowie das Encoding für Strings.

Bevor der Servercode abgeschlossen ist, kommt zu guter Letzt noch die Zeile »vwait forever« (immer noch in Listing 3). Normalerweise beendet sich die »tclsh« selbst, wenn sie alle Kommandos abgearbeit hat (im Gegensatz zur »wish«). Ein Server muss jedoch weiterlaufen, auf Clients warten und dabei seine Event-Queue bearbeiten. Genau das erreicht dieses Kommando.

Nach dem Aufwand für den Server ist der Client in Listing 4 erfreulich klein. Wie der Server initialisiert er Corba und füllt das Interface-Repository. Auch der Client kann nur mit Objekten arbeiten, deren IDL er kennt. Danach liest er in Zeile 16 die IOR aus der Datei und entfernt überflüssige Leerzeichen und Zeilenumbrüche. »corba::string_to_object« wandelt die IOR in eine Objektreferenz um. Sie entspricht einem normalen iTcl-Objekt und hat genau die in der IDL beschriebene Schnittstelle. Wo und wie das Objekt implementiert ist, ist für den Client vollkommen transparent – der große Vorteil von Corba.

Ab Zeile 25 ruft der Client einige Methoden des Objekts auf, die Ausgaben sind in Abbildung 2 zu sehen. Zuerst liest das Programm die Variable, in der Tcl-Sprachabbildung lautet das »$obj Variablenname«. Danach ruft es die »helloWorld«-Methode auf. Als Eingabe »a« dient die Unix-Epoche, also die Anzahl der Sekunden seit 1970. Da es eine »in«-Variable ist, wird der Wert übergeben. Anders bei der »out«-Variablen »b«, hier ist der Name der Variablen angegeben. Die Variable erhält beim Aufruf den vom Server berechneten Wert, im Beispiel die Quadratwurzel von »a«. Der Rückgabewert ist der im Server erzeugte String.

Fazit

Die knapp 100 Zeilen Tcl-Code implementieren sowohl die Server- als auch die Client-Seite. Dank Corba ist kein Unterschied festzustellen, wenn statt des Tcl-Servers einer in Java implementiert ist oder ein C++-Client auf den Tcl-Server zugreift. Bei größeren Systemen werden die Objektreferenzen der Server nicht über eine Datei exportiert, dann kommt meist der Corba-Nameservice zur Anwendung. Dieser und andere Corba-Dienste lassen sich mit Tcl-GUI Cossclient[8] kontaktieren. Von Frank Pilhofer[3] stammt auch ein Browser für das Interface Repository. Mit seiner Hilfe lassen sich alle in der IDL definierten Namensräume, Klassen und Weiteres bequem in einem Klassenbrowser untersuchen. (fjl)

Infos

[1] OMG: [http://www.omg.org]

[2] Corba: [http://www.corba.org]

[3] Combat: [http://www.fpx.de/Combat]

[4] Mico: [http://www.mico.org]

[5] iTcl: [http://www.tcltk.com/itcl/]

[6] Tcl++: [http://www.sensus.org/tcl/]

[7] M. Henning und S. Vinovski: Advanced Corba Programming with C++. Addison-Wesley, 1999

[8] Cossclient: [http://www.groy-groy.de/czerbst/corba.html]

[9] C. Zerbst, “Klasse Federn: Objektorientierung mit Tcl”: Linux-Magazin 7/01, S. 131

Der Autor

Carsten Zerbst arbeitet bei Atlantec an einem PDM-System für den Schiffbau. Daneben beschäftigt er sich mit dem Einsatz von Tcl/Tk.

Das Neueste

Die neue Tcl-Version 8.4 ist bei einigen Kommandos wesentlich schneller als ihre Vorgänger. Bei Acrodesign bahnt sich noch Schnelleres an: Ein spezieller Prozessor spricht Tcl ohne ein dazwischen liegendes Betriebssystem [http://www.gmvhdl.com/acrodesign /research.html]. Besonders im Embedded-Markt wäre dies traumhaft, denn Tcl ist leicht zu entwickeln und stellt gleichzeitig geringe Anforderungen an den Prozessor.

Ein Entwicklerteam um Gustaf Neumann entwickelt seit einigen Jahren XOTcl [http://media.wu-wien.ac.at]. Die Fähigkeiten dieser objektorientierten Erweiterung gehen weit über die von iTcl hinaus. Diverse Designpattern, die der Entwickler bei iTcl (ähnlich wie bei Java oder C++) selbst implementieren muss, sind hier in der Sprache integriert. XOTcl hat Version 1.0 erreicht und soll bald zum Lieferumfang von Active Tcl gehören. Bei Active State finden sich inzwischen auch die Papers der letzten Tcl Conference [http://aspn .activestate.com/ASPN/Tcl/Reference/], unter anderem geht’s um die Rolle von Tcl/Tk beim Publizieren des Deutschen Wörterbuchs oder bei der Testautomatisierung.

Die DOM-, XPath- und XQuery-Erweiterung Tdom liegt in einer neuen Version vor [http://loewerj.freeshell.org/tdom.cgi], zudem gibt es mit Star DOM eine Benutzeroberfläche [http://wiki.tcl.tk/stardom]. Auch Steve Ball war nicht untätig, Tcl DOM und Tcl XSLT gibt es in neuen Versionen [http://www.zveno.com/open_source/].

Neben diesen nützlichen Dingen zeigt Keth Vetters auf dem Wiki [http://wiki.tcl.tk/4560], wie sich fraktale Gebirge mit Tcl erzeugen lassen (Abbildung 3). Die Ausgabe erfolgt entweder im Canvas oder per OpenGL. Auf dem Wiki findet sich auch Tcl Brix [http://wiki.tcl.tk/TclBrix], damit auch große Jungs noch mit Bauklötzen spielen können (Abbildung 4).

Abbildung 3: Ein kleines Tcl-Programm berechnet fraktale Gebirgslandschaften.

Abbildung 3: Ein kleines Tcl-Programm berechnet fraktale Gebirgslandschaften.

Abbildung 4: Tcl Brix ist ein einfaches Spiel mit wenigen Zeilen Tcl-Code.

Abbildung 4: Tcl Brix ist ein einfaches Spiel mit wenigen Zeilen Tcl-Code.

Listing 4: Der Client

01 #!/bin/sh
02 # Client für das Corba-Beispiel
03 # 
04 exec tclsh $0  ${1+"$@"}
05 
06 package require combat
07 
08 # Corba initialisieren
09 puts "Initialisiere Corba"
10 source beispiel.tcl
11 set argv [::corba::init $argv]
12 ::combat::ir add $_ir_beispiel
13 
14 #Objekt aus IOR
15 set fd [open server.ior]
16 set ior [string trim [read $fd]]
17 close $fd
18 
19 if {! [regexp {^IOR:} $ior]} {
20     puts stderr "Ungültige IOR : $ior"
21     exit 1
22 }
23 set obj [::corba::string_to_object $ior]
24 
25 # Attribute
26 puts "meinWert   :  [$obj einWert]"
27 
28 # helloWorld
29 set a [clock seconds]
30 puts "helloWorld : [$obj helloWorld $a b]"
31 puts "         b : $b"
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben