Creative Commons License Dieser Inhalt ist unter einer Creative Commons-Lizenz lizenziert.

Abstrakt: Dieser Text bietet eine Einführung in die Common Object Request Broker Architecture (CORBA) der Object Management Group (OMG) als Beispiel für objektbasierte verteilte Systeme. 1)

1) Grundlagentext: Andrew S. Tanenbaum. Verteilte Systeme: Grundlagen und Paradigmen. Pearson Studium. 2003

1. Einführung

1.1 Was ist CORBA

1 CORBA Version 3.0.2 siehe auch: http://www.omg.org/technology/documents/corba_spec_catalog.htm in der Fassung vom 20.11.2004

Die "Common Object Request Broker Architecture" (CORBA) ist eine Spezifikation zur Implementierung objektbasierter verteilter Systeme, welche 1997 von der "Object Management Group" (OMG) formuliert wurde. Die OMG ist eine gemeinnützige Organisation, welche 1989 mit 11 Mitgliedern gegründet wurde. Im Jahr 2004 umfasst die Organisation bereits mehr als 6000 Mitglieder. Darunter hochrangige Vertreter der Wirtschaft, wie SUN, IBM oder Hewlett Packard. Ziel der OMG ist unter anderem die Formulierung einheitlicher Standards für die Realisierung verteilter Softwaresysteme aus miteinander interagierenden und kooperierenden Softwarekomponenten basierend auf einem objektorientierten Ansatz. In diesem Zusammenhang stellt sich CORBA dar als Spezifikation einer Architektur für Entwicklung und Einsatz objektorientierter Softwarebausteine im Kontext verteilter, heterogener Systeme.
Durch die breite, herstellerübergreifende Akzeptanz der OMG hat sich CORBA zu einem Standard entwickeln können. Über CORBA hinaus wurden von der OMG weitere Spezifikation veröffentlicht. Einer breiten Öffentlichkeit bekannt ist dabei vor allem die Unified Modeling Language (UML).

Zu CORBA sind mehrere überarbeitete Fassungen verfügbar. In diesem Text beziehe ich mich primär auf die Grundlagen, welche allen Fassungen gemeinsam sind. Die im November 2004 aktuelle Fassung von CORBA ist die Version 3.0.2, welche auf der Website der OMG eingesehen werden kann. 1

1.2 Eigenschaften

Mit CORBA wurden (wie eingangs bereits erwähnt) eine Reihe von Zielen verfolgt, welche sich in den Eigenschaften der Spezifikation bzw. deren Implementierungen wiederspiegeln. Zu den wichtigsten Zielen und Eigenschaften zählen:

1.3 verfügbare Implementierungen

Die OMG liefert mit CORBA lediglich eine Spezifikation, jedoch keine Implementierung. Die Umsetzung obliegt dem jeweiligen Hersteller. Es sind daher zurzeit eine Reihe verschiedener Implementierungen für eine Vielzahl von Programmiersprachen erhältlich. Als Beispiele seien hier die folgenden genannt: 2

2 Für weitere Beispiele siehe auch: http://omg.org/technology/corba in der Fassung vom 20.11.2004

Weite Verbreitung gefunden hat vor allem die Implementierung von CORBA durch die Java SDK von Sun, wozu wahrscheinlich nicht zuletzt die Popularität der Programmiersprache Java einen nicht unerheblichen Teil beigetragen haben dürfte.

1.4 grundlegende Strukturierung

Von zentraler Bedeutung in CORBA ist die Betrachtung der Software, welche auf dieser Architektur eingesetzt werden soll. In diesem Sinne spezifiziert CORBA eine komponentenorientierte Programmstruktur. Wobei die jeweiligen Komponenten durch den Austausch von Nachrichten miteinander kommunizieren und interagieren.

1.4.1 Komponenten

Unter "Komponenten" werden autonome semantische Softwareeinheiten verstanden. Diese Softwareeinheiten sind in der Regel identisch mit Objekten. CORBA gestattet eine dynamische Zusammenstellung von Komponenten zur Laufzeit. Diese Komponenten dürfen sich auf verschiedene Knotenpunkten (Rechnern) des verteilten Systems befinden und müssen nicht anwendungsspezifisch sein. Zur Laufzeit interagieren diese Komponenten miteinander. Das Verhalten eines Programms zur Laufzeit definiert sich folglich dynamisch aus der Menge der aktiv beteiligten Komponenten.

1.4.2 Kommunikation

CORBA ist ein nachrichtengekoppeltes, heterogenes System mit verteiltem Speicher. Insbesondere gibt es keine zentrale Steuereinheit, welche die Synchronisierung der einzelnen Komponenten übernimmt. Es findet das klassische Server-Client-Modell Anwendung. Die Trennung zwischen den Rollen ist jedoch nicht so streng spezifiziert wie in anderen Systemen.

Komponenten in CORBA kommunizieren über öffentliche Schnittstellen. Diese müssen zu diesem Zweck bekannt gemacht werden. Schnittstellen, welche bereits zur Entwurfszeit bekannt sind, heißen "statisch". Es stehen statische Methodenstubs auf der Clientseite zur Verfügung, welche die gleiche Schnittstelle implementieren. Falls eine Schnittstelle zur Laufzeit noch unbekannt ist, so heißt sie "dynamisch". Dynamische Schnittstellen können zwischen Client und Server zur Laufzeit ausgehandelt werden.

Die Kommunikation zwischen den einzelnen Komponenten kann laut Spezifikation auf drei verschiedene Arten erfolgen.

  1. (voll-)synchrone Kommunikation
  2. asynchrone Kommunikation
  3. verzögerte (quasisynchrone) Kommunikation

Im ersten Fall blockiert der Sender nach dem Versand einer Nachricht bis er vom Empfänger eine Antwort erhalten hat. Dieses Verhalten ist allgemein üblich bei Methodenaufrufen, bei denen ein Rückgabewert erwartet wird, welcher für den weiteren Programmablauf von Bedeutung ist.

Im zweiten Fall blockiert der Sender nicht nach dem Versand einer Nachricht, sondern setzt die Programmausführung fort. Zu beachten ist hierbei, dass CORBA keine Garantie dafür verlangt, dass in diesem Fall die Nachricht überhaupt weitergeleitet wird. Dieses Art der Kommunikation wird vor allem für Aufrufe verwendet, bei denen eine zeitnahe Ausführung nicht notwendigerweise erwartet wird. Beispielsweise im Einzelfall der Versand von Mails oder das Mitführen von redundanten Statusinformationen in einer Log-Datei.

Im dritten Fall blockiert der Sender zunächst ebenfalls nicht nach dem Versand der Nachricht. Statt auf eine Antwort zu warten wie im ersten Fall, werden zunächst solche Anweisungen ausgeführt, welche von der Antwort unabhängig sind. Erst wenn die Liste dieser Anweisungen erschöpft ist und zu diesem Zeitpunkt immer noch keine Antwort verfügbar ist, blockiert der Sender auch in diesem Fall solange, bis er die Antwort erhalten hat.

Anzumerken ist in allen drei Fällen, dass CORBA eine "try-only-once" Semantik spezifiziert. Dies bedeutet, dass nur ein einziges Mal versucht werden muss eine Verbindung herzustellen. Falls dies nicht möglich ist, soll keine Wiederholung des Verbindungsversuchs vorgenommen werden.

2. Object Management Architecture

Die "Object Management Architecture" (OMA) spezifiziert eine Top-Level Beschreibung der beteiligten Schnittstellen, Komponenten und Protokolle.

* siehe Abbildung 1


Abbildung 1: Object Management Architecture

Im Folgenden eine nähere Erläuterungen der Punkte eins bis vier.

2.1 Anwendungsschnittstellen

Anwendungsschnittstellen sind solche, welche durch die jeweilige Applikation selbst definiert werden. Sie sind statisch und in der Regel nicht standardisiert, da sie vom Entwickler der Anwendung selbst festgelegt werden.

2.2 Objektdienste

CORBA bietet eine Reihe von nicht anwendungs- bzw. domänenspezifischen Basisdiensten. Dies ist beispielsweise der in CORBA spezifizierte Namensdienst. Objektdienste werden durch in Interface Defintion Language (IDL) beschrieben.

2.3 vertikale Facilities

Vertikale Facilities sind Mengen von High-Level Diensten, welche Schnittstellen für domänenspezifische Aufgaben anbieten. Beispielsweise die Bereiche ECommerce, Banking, usw.

Die Schnittstellen sind ebenfalls in IDL beschrieben.

2.4 horizontale Facilities

Unter horizontalen Facilities werden allgemeine, nicht-domänenspezifische Dienste verstanden. Dies beinhaltet beispielsweise Druckdienste, Datenbanken, EMail und weitere.

Die Beschreibung der Schnittstellen erfolgt ebenfalls in IDL.

2.5 Interface Definition Language

Die IDL ist eine Schnittstellenbeschreibungssprache welche verwendet wird, um Schnittstellen von Komponenten in CORBA festzulegen. Es handelt sich dabei um eine rein deskriptive Sprache, welche auf jegliche algorithmische Komponenten verzichtet. Die IDL-Syntax orientiert sich in weiten Teilen an der Familie von C++ und den syntaktisch verwandten Sprachen wie Java. Eine als IDL vorliegende Beschreibung einer (Objekt-)Schnittstelle kann durch Verwendung eines geeigneten Compilers in Quelltext einer konkreten Programmiersprache übersetzt werden.

2.5.1 Beispiel

Betrachtet werden soll eine Bank welche Konten enthalten kann. Jedes Konto soll ein Objekt sein welches ein nicht-öffentliches Attribut "Kontostand" vom Typ "float" enthält. Außerdem soll das Einzahlen und Abheben von Geldbeträgen über entsprechende Methoden möglich sein. Daraus ergibt sich folgende Schnittstellenbeschreibung in IDL:

module Bank {

interface Konto {

readonly attribute float kontostand;

void einzahlen(in float betrag);

void abheben(in float betrag);

};

};

3. Object Request Broker

Der Object Request Broker kann vereinfacht als eine Art "Middleware"-Schicht,oder "Laufzeitsystem" angesehen werden, welches die Kommunikation zwischen den verschiedenen Komponenten einer Software ermöglicht. Abbildung 2 zeigt eine schematische Darstellung des allgemeinen Aufbaus eines CORBA-Systems.


Abbildung 2: Object Request Broker

Im Folgenden eine nähere Erläuterungen der Punkte eins bis sieben.

3.1 statische IDL-Stubs

IDL-Stubs sind statische, clientseitige, in IDL formulierte Schnittstellen-beschreibungen. Die Gesamtmenge der kompilierten statischen Stubs auf Clientseite wird unter anderem von [Tanenbaum] auch als "Proxy" bezeichnet. Jeder Aufruf einer Methode auf einen Stub erscheint auf Clientseite wie ein lokaler Methodenaufruf. Der Stub implementiert dabei die gleiche Schnittstelle wie die Komponente auf welche er verweist. Er enthält weiterhin Code, welcher für das Marshaling des Methodenaufrufes auf der Clientseite verantwortlich ist.

3.2 dynamic Invocation Interfaces

Nicht immer stehen zur Laufzeit bereits statische Schnittstellenbeschreibungungen für eine Komponente zur Verfügung, mit welcher ein Client kommunizieren möchte. In diesem Fall erlaubt CORBA ein dynamisches Aushandeln der Schnittstelle zur Laufzeit. Dazu verwendet der Client eine dynamische Aufrufschnittstelle (dynamic invocation interface). Diese ist in der Lage, die erforderlichen Informationen zur Laufzeit dynamisch aus einer Menge abgespeicherter Schnittstellenbeschreibungen, dem so genannten "Interface Repository", zu laden. Dadurch wird ein Zugriff auf ein Objekt auch in diesem Fall ermöglicht.

3.3 static Skeletons

Das Gegenstück zu den statischen IDL-Stubs, bzw. dem statischen Proxy, auf der Clientseite bilden auf der Serverseite die so genannten "statischen Rahmen" (static Skeletons). Diese Rahmen bilden eine Schnittstelle für die Dienste, welche der jeweilige Server anbietet. Sie enthalten außerdem Code, um das Unmarshaling der empfangenen Aufrufe durchzuführen und einen empfangenen Aufruf in die jeweils erforderliche Programmiersprache, in welcher die Implementierung vorliegt, zu übersetzen.

3.4 dynamic Skeletons

Analog zu den "dynamic invocation interfaces" auf der Clientseite, finden sich auf der Serverseite "dynamisch Rahmen" (dynamic Skeletons). Diese sind mit statischen Rahmen im Bezug auf das Unmarshaling empfangener Aufrufe identisch. Sie unterscheiden sich jedoch semantisch insofern, als statische Rahmen Schnittstellen bereitstellen, welche zur Entwurfszeit noch nicht bekannt waren.

3.5 ORB-Interface

Die "ORB-Schnittstelle" (ORB-Interface) stellt eine Reihe von allgemeinen Diensten zur Verfügung, welche vom Object Request Broker angeboten und sowohl von Server als auch Client gleichermaßen genutzt werden können. Dazu gehört die Suche nach Diensten, Operationen zum verpacken, bzw. entpacken von Objektreferenzen, sowie Operationen, um Referenzen zu vergleichen.

3.6 Object Adapter

Der "Objektadapter" (Object Adapter) ist zuständig für die Weitergabe eines eingehenden Aufrufes an die aufgerufene Implementierung. Um die Zuordnung von Aufruf zu Implementierung zu gewährleisten, hat der Objektadapter Zugriff auf ein so genanntes "Implementation Repository".

3.7 General Inter-ORB Protokoll

Für die Kommunikation über die Grenzen eines Rechners hinweg ist es notwendig ein geeignetes Protokoll zu verwenden, welches die Kommunikation zwischen verschiedenen ORBs in einem heterogenen Netzwerk nicht beeinträchtigt. Die OMG lieferte mit CORBA ein Reihe von Spezifikationen, welche ein geeignetes Protokoll beschreiben. Die Menge dieser Spezifikationen wird "General Inter-ORB Protokoll" (GIOP) genannt. Diese Spezifikationen verlangen jedoch lediglich, dass es sich um ein verbindungsorientiertes Protokoll handeln muss, schreiben allerdings keine konkrete Implementierung vor. Geeignet für diesen Zweck ist zum Beispiel TCP. Diese Implementierung von TCP im Kontext der GIOP-Spezifikation wird als "Internet Inter-ORB Protokoll" (IIOP) bezeichnet.

4. Implementierung in Java

Für viele verschiedene Programmiersprachen sind Implementierungen verfügbar. CORBA verlangt dabei nicht notwendigerweise, dass es sich bei den Komponenten um verteilte Objekte handeln muss. Diese können sich durchaus auf dem gleichen Netzwerkknoten (Rechner) befinden. Darüber hinaus verlangt das verwendete Objektmodell ebenfalls nicht, dass die Implementierung einer Schnittstelle zwangsläufig in einer objektorientierten Programmiersprache verfasst werden muss. Daher existieren auch Implementierungen für primär prozedurale Sprachen.

Im Zuge der Implementierung einer Anwendung auf einem CORBA-System müssen für verschiedene Programmiersprachen vorhandene IDL-Schnittstellen-beschreibungen in Code der jeweiligen Sprache übersetzt werden. Eine der Aufgaben dieses Schrittes ist unter anderem clientseitige Stubs bzw. serverseitige Rahmen zu erzeugen, welche in der jeweiligen Programmiersprache der Implementierung der Schnittstelle bzw. der Anwendung geschrieben sind. Dadurch wird zum Beispiel auf der Clientseite eine Konformität zwischen der (virtuellen) lokalen Schnittstelle eines Objekts und den syntaktischen Eigenschaften der für die jeweilige Anwendung verwendeten Programmiersprache erreicht. Durch diese Konformität erscheinen Methodenaufrufe auf der kompilierten Schnittstelle für den Programmierer wie lokale Methodenaufrufe, egal in welcher Programmiersprache die Implementierung vorliegt, bzw. ob sie sich auf dem selben Netzwerkknoten wie die aufrufende Anwendung befindet oder nicht.

Im Folgenden wird exemplarisch die Umwandlung einer IDL-Beschreibung in Java-Quellcode erläutert.

4.1 Übersicht

Ein Muster für eine Übersetzung von IDL nach Java finden Sie in Abbildung 3.

Wie Sie Abbildung 3 entnehmen können, bilden IDL-Beschreibungen der Schnittstellen die Grundlage für alle Übersetzungen - egal welche Programmiersprache zum Einsatz kommt. Das Endergebnis ist für jede Programmiersprache ein Paar aus statischen Schnittstellen und Implementierungen für den Einsatz entweder in der Rolle als Client oder als Server.

Die Übersetzung benötigt zwei Schritte. Im ersten Schritt kommt ein speziell für Java entwickelter Precompiler zum Einsatz, welcher aus gegebenen IDL-Beschreibungen eine Menge von Java-Dateien erzeugt, welchen Quellcode für entsprechende statische Interfaces bzw. Rahmen enthalten. Für viele Programmiersprachen stehen geeignete Compiler im Internet zur Verfügung.
Im zweiten Schritt kommt der "gewöhnliche"Java-Compiler zum Einsatz, welcher aus dem gelieferten Java-Code für Schnittstellen bzw. Rahmen und den erforderlichen Implementierungen eine Zwischencoderepräsentation, in diesem Fall Class-Dateien, erzeugt.


Abbildung 3: IDL nach Java

4.2 Beispiel: Umwandlung von IDL in Java

Im Folgenden ein Beispiel für die Übersetzung einer IDL-Schnittstelle in Java-Code, wie sie ein geeigneter Compiler erzeugen könnte.

4.2.1 IDL-Spezifikation

Angenommen sei eine Bank mit mehreren Konten. Diese Konten haben ein nicht-öffentliches Attribut "Kontostand", sowie drei Methoden zum Abfragen des Kontostandes, sowie zum Einzahlen bzw. Abheben von Geldbeträgen. Im Fall, dass ein Konto überzogen ist, soll außerdem eine Ausnahme erzeugt werden. Eine zu dazu passende IDL-Schnittstellenbeschreibung könnte die folgende Form haben:

module Bank {

interface Konto {

readonly attribute float kontostand;

void abheben(in float betrag) raises (KontoException);

void einzahlen(in float betrag);

float kontoStand();

};

};

4.2.2 erzeugter Java-Code

Aus der in Punkt 4.2.1 genannten Beschreibung könnte ein geeigneter Compiler nun den folgenden Java-Code generieren. Beachten Sie dabei, dass das Modul "Bank" als Package übersetzt wurde und das Interface "Konto" als Klasse in diesem Package interpretiert wurde. Das Attribut "kontostand" ist zu einem privaten instanzspezifischen Attribut der Klasse "Konto" geworden. Die Methoden wurden in entsprechende Methoden der Klasse überführt. Die Ausnahme "KontoException" hat zusätzlich eine eigene Klasse erzeugt.

import Bank.*;

public class Konto {

private float kontostand;

public void Konto() { … }

public void abheben(float betrag) throws KontoException { … }

public void einzahlen(float betrag) { … }

public float kontoStand() { … }

}

public class KontoException extends Exception { }

4.2.3 Client-Implementierung

3 Siehe http://www.informatik.fh-muenchen.de/~schieder/seminar-java-ss98/corba/ausarbeitung/entwicklung.htm (Fachhochschule München) in der Fassung vom 20.11.2004

Das folgende Beispiel zeigt, wie das Objekt auf Clientseite durch eine geeignete Implementierung verwendet werden kann. Dieses Beispiel finden Sie auch in einer ausführlicheren Fassung auf der Webseite der Fachhochschule München. 3

import Bank.*;
import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*;
import org.omg.CORBA.*;

/**
 * Die Klasse stellt einen Client zur Verfuegung, der mit der
 * Server-Anwendung BankServer kommuniziert. Es koennen die Dienste,
 * die vom Server angeboten hier genutzt. In diesem Fall die Dienste
 * fuer die Verwaltung eines 'light' Kontos.
 * @author Tin-Ho Chan
 * @version 1.00 12.4.1998 Ersterstellung
 */

public class BankClient extends Applet implements ActionListener {

...

private Konto konto;

/**
 * Initialisiert das GUI-Interface und stellt die Verbindung zum
 * Server der Bank her.
 */

public void init() {

super.init();
...
initBankConnection();

}

/**
 * Die Verbindung zum Server wird aufgebaut.
 */

private void initBankConnection() {

try {

// Create and initialize the ORB
ORB orb = ORB.init(this, null);

// Get the root naming context
org.omg.CORBA.Object objRef = orb.resolve_initial_references("NameService");
NamingContext ncRef = NamingContextHelper.narrow(objRef);

// Resolve the object reference in naming
NameComponent nc = new NameComponent("Konto", "");
NameComponent path[] = {nc};
konto = KontoHelper.narrow(ncRef.resolve(path));
if (konto==null)
connectionError(new Exception());

} catch(Exception e) {

connectionError(e);

}

}

...

}

Literaturverzeichnis

(ac/tom) Diskussion