Corba in 14 Tagen: Inhaltsverzeichnis Danksagung 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14.
Tag: CORBA kennenlernen Tag: Die Architektur von CORBA Tag: Beherrschen der Sprache Interface Definition Language (IDL) Tag: Entwicklung einer CORBA-Anwendung Tag: Entwerfen des Systems: ein Schnellkurs in objektorientierter Analyse und Entwurfstechnik Tag: Implementierung grundlegender Anwendungsfunktionen Tag: Verwendung von Exceptions zur Fehlerprüfung Tag: Hinzufügen von Geldautomat-Fähigkeit Tag: Verwendung von Callbacks zum Hinzufügen von Push-Fähigkeit Tag: Lernen über CORBA-Entwurfsfragen Tag: Verwendung der dynamischen Aufrufschnittstelle (Dynamic Invocation Interface, DII) Tag: CORBAservices und CORBAfacilities Tag: Entwicklung von Internet-Anwendungen mit CORBA und Java Tag: Einrichten von Web-Fähigkeit für das Beispiel Bank mit Java Anhang A: Antworten auf die Quizfragen und Lösungen zu den Übungen Anhang B: CORBA-Werkzeuge und -Dienstprogramme Anhang C: Was steht bevor? Die Zukunft von CORBA
Ein Imprint des Markt&Technik Buch- und Software-Verlag GmbH. Elektronische Fassung des Titels: Corba in 14 Tagen, ISBN: 3-8272-2031-9
Voraussetzungen
Danksagung An allererster Stelle möchte ich mich bei den hilfsbereiten Mitarbeitern von Sams Publishing ganz herzlich dafür bedanken, daß sie mir das Schreiben dieses Buches ermöglicht haben. Mein besonderer Dank gilt dabei meinem Redakteur Steve Straiger für seine großzügige Auslegung des Zeitplans bei der Erstellung dieses Buchs. Dann möchte ich meinem persönlichen Freund und Kollegen, Michael Jones, danken, der mich ermutigte, die Herausforderung zum Schreiben dieses Buches anzunehmen. Dank gebührt weiterhin noch meinen übrigen Kollegen bei Hughes Information Technology Systems, die so liebenswürdig waren, meine äußerst unregelmäßigen Arbeitszeiten (vor allem kurz vor der Abgabe des Entwurfs) ohne Murren zu tolerieren. Anerkennung verdienen auch die Mitarbeiter bei Visigenic Software für ihre hervorragende Software und dafür, daß sie mir diese für einen ausreichenden Zeitraum zum Ausprobieren zur Verfügung gestellt haben. Abschließend möchte ich noch Stonehenge Internet und GTE Internet wegen der Bereitstellung stets zuverlässiger Internet-Dienste - auf die man im heutigen Informationszeitalter beim Schreiben eines Buches nicht mehr verzichten kann - lobend erwähnen.
Der Autor Jeremy Rosenberger wurde in Toledo, Ohio, geboren und wuchs dort auch auf. Er legte sein Examen in Computerwissenschaften und -technik an der Cornell University in Ithaca, New York, ab. Seine weiteren Wirkungskreise waren Boston und St. Louis; heute lebt er zusammen mit seiner Frau Camilla in Denver. Jeremy Rosenberger arbeitet derzeit als Software-Ingenieur bei Hughes Information Systems in Aurora, Colorado, wo er Anwendungen entwickelt, und nutzt dabei eine Vielzahl von Werkzeugen, wie z.B. C++, CORBA, Java sowie Technologien für Software-Agenten. Er ist Mitentwickler von
Javology (http://www.javology.com/) und trug mit zwei Kapiteln zum Buch Special Edition Using JavaBeans (erschienen 1997 bei Que/Ziff-Davis Press) bei, das die Verwendung von Java zusammen mit CORBA behandelt. Er ist auch Autor eines Ausblicks zum zukünftigen Einsatz der Java-Plattform in den Unternehmen. Ferner plant er die Gründung einer Beratungsfirma, die Kunden in der Region Denver betreuen soll. Wenn er nicht gerade Software entwickelt oder sich dem Schreiben seines neuesten und größten Buchs widmet, verbringt Jeremy Rosenberger die Zeit zusammen mit seiner Frau, außerdem baut er gern Modellautos, er komponiert und musiziert, und er spielt Volleyball. Gelegentlich treibt es ihn auch zur sportlichen Betätigung in die Höhen der Rocky Mountains. Jeremy Rosenberger verfügt über eine Vielzahl von E-Mail-Adressen, ist aber immer unter
[email protected] erreichbar.
Einführung Willkommen bei SAMS CORBA in 14 Tagen! Dieses Buch eröffnet Ihnen die Geheimnisse der Entwicklung von verteilten Anwendungen mit Hilfe von CORBA (Common Object Request Broker Architecture). In einem Zeitraum von zwei Wochen werden Sie die in Grundkonzepte von CORBA eingeführt, schrittweise mit der Entwicklung einer einfachen CORBA-Anwendung vertraut gemacht und abschließend zur Entwicklung einer komplexeren Anwendung angeleitet. Sie werden Näheres zur Interface Definition Language (IDL) von CORBA erfahren und lernen, wie damit Schnittstellen zwischen einzelnen Anwendungskomponenten beschrieben werden. Darüber hinaus werden Sie die Object Management Group kennenlernen, die für die Entwicklung der Object Management Architecture (OMA), einer Spezifikation, in der CORBA eine Rolle spielt, verantwortlich zeichnet. Zusätzlich werden Sie noch einen Einblick in die CORBA-Architektur und die zugehörigen Komponenten der Object Management Architecture - wie z.B. die CORBAservices und die CORBAfacilities - gewinnen und erfahren, wie mit Hilfe dieser Bausteine verteilte Unternehmensanwendungen entwickelt werden können. Sie werden mit dem Schreiben von CORBAAnwendungen sowohl in C++ als auch in Java vertraut gemacht, da diese beiden die wichtigsten Sprachen für die Anwendungsentwicklung mit CORBA sind. In Ergänzung zu den 14 Lektionen enthält das Buch noch zwei Anhänge mit einer Einführung in die zur Verfügung stehenden CORBA-Entwicklungswerkzeuge und einer Vorschau auf die Zukunft von CORBA. Wenn auch die Weiterentwicklung von CORBA vom Tempo her manchmal an die Definition von Normen durch einen Ausschuß erinnert (was auch nicht überrascht, wenn man den Prozeß betrachtet, mit dem die OMG neue Standards in Kraft setzt), macht CORBA doch in einem solchen Maße Fortschritte, daß dieser Architektur ein Platz in vorderster Front bei der Entwicklung verteilter Anwendungen sicher ist.
Voraussetzungen
Sie werden von diesem Buch am meisten profitieren können, wenn Sie folgende Voraussetzungen mitbringen: ■
■
■
Kenntnisse in C++ oder Java; allerdings werden Sie auch mit Kenntnissen aus anderen Programmiersprachen in der Lage sein, die in diesem Buch vorgestellten Konzepte zu verstehen. Vertrautheit im Umgang mit Entwicklungswerkzeugen, wie z.B. Visual C++, Java Development Kit (JDK) oder anderen Entwicklungswerkzeugen auf der bzw. den von Ihnen bevorzugten Plattform(en). Verständnis der Konzepte zu objektorientierten Analyse- und Entwurfstechniken. Kenntnisse in diesem Bereich sind allerdings nicht unbedingt erforderlich, weil das vorliegende Buch eine Einführung in diese Konzepte bietet. Da es sich bei CORBA aber um eine objektorientierte Architektur handelt, werden Sie die Ausführungen leichter verstehen, wenn Sie mit den oben genannten Konzepten bereits vertraut sind.
Üben und Ausprobieren sind zwei ganz besonders wichtige Aspekte beim Erlernen jedes neuen Fachgebiets. Damit Sie die in den einzelnen Kapiteln vorgestellten Beispiele mitverfolgen, die jeweiligen Quizfragen beantworten und die angebotenen Übungen durchführen können, benötigen Sie folgende Ausrüstung: ■
■
■
Einen Computer eines beliebigen Typs. Da es sich bei CORBA um einen plattformübergreifenden Standard handelt, ist fast jeder Computer geeignet, gleichgültig, ob es sich dabei um einen Macintosh, einen PC unter Windows oder eine Unix-Workstation handelt. Kurz gesagt, jeder Rechner, der von einem CORBA-Entwicklungsprodukt (siehe den nächsten Punkt) unterstützt wird, kann verwendet werden. Ein für Ihren Computer geeignetes CORBA-Entwicklungsprodukt. Zu den beliebtesten Entwicklungsumgebungen gehören Orbix von IONA Technologies und VisiBroker von Visigenic Software. Zahlreiche Anbieter überlassen dem Anwender ihr Produkt für einen bestimmten Zeitraum zum Ausprobieren, der so lang ist, daß Sie reichlich Zeit zum Durcharbeiten dieses Buchs haben. Die C++-Beispiele wurden mit Hilfe von VisiBroker für C++ entwickelt; die Java-Beispiele mit dem Java IDL-Paket von Sun, sie können jedoch ohne oder mit nur geringen Änderungen auch unter Zuhilfenahme anderer Produkte durchgeführt werden. Die geeigneten Compiler- und/oder weiteren Software-Entwicklungswerkzeuge, die zur Unterstützung des von Ihnen gewählten CORBA-Entwicklungsprodukts erforderlich sind. Beispielsweise benötigen Sie für VisiBroker für C++ unter Windows 95 oder Windows NT das Produkt Visual C++ 4.2 oder höher (allerdings funktioniert das Ganze auch mit Compilern anderer Anbieter). Für Java-gestützte CORBA-Produkte benötigen Sie in der Regel lediglich eine aktuelle Version des Java Development Kit (JDK).
Ein Imprint des Markt&Technik Buch- und Software-Verlag GmbH. Elektronische Fassung des Titels: Corba in 14 Tagen, ISBN: 3-8272-2031-9
Corba in 14 Tagen: Inhaltsverzeichnis Danksagung 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14.
Tag: CORBA kennenlernen Tag: Die Architektur von CORBA Tag: Beherrschen der Sprache Interface Definition Language (IDL) Tag: Entwicklung einer CORBA-Anwendung Tag: Entwerfen des Systems: ein Schnellkurs in objektorientierter Analyse und Entwurfstechnik Tag: Implementierung grundlegender Anwendungsfunktionen Tag: Verwendung von Exceptions zur Fehlerprüfung Tag: Hinzufügen von Geldautomat-Fähigkeit Tag: Verwendung von Callbacks zum Hinzufügen von Push-Fähigkeit Tag: Lernen über CORBA-Entwurfsfragen Tag: Verwendung der dynamischen Aufrufschnittstelle (Dynamic Invocation Interface, DII) Tag: CORBAservices und CORBAfacilities Tag: Entwicklung von Internet-Anwendungen mit CORBA und Java Tag: Einrichten von Web-Fähigkeit für das Beispiel Bank mit Java Anhang A: Antworten auf die Quizfragen und Lösungen zu den Übungen Anhang B: CORBA-Werkzeuge und -Dienstprogramme Anhang C: Was steht bevor? Die Zukunft von CORBA
Ein Imprint des Markt&Technik Buch- und Software-Verlag GmbH. Elektronische Fassung des Titels: Corba in 14 Tagen, ISBN: 3-8272-2031-9
Corba in 14 Tagen: Stichwortverzeichnis -!- | { | A | B | - | C | D | E | F | - | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | Ü | U | V | W | X | Z
-! Quelltext-Listings : BankImpl.h 1 Quelltext-Listings : BankKarte.idl 1 -{{} Geschweifte Klammern, Definitionen 1
-AAbbildung (Einrichtung) 1 Abfragedienst 1 Abfragedienst : Bankanwendung 1 Abgeleitete Klasse 1 Abhebung() (Methode) 1 Abhebung() (Methode) : Exception 1 Accessor-Methoden 1 addBanks(), Methode (Java), BankApplet 1 Agent : Task Management Common Facilities 1
Aktiensymbol 1 AktienSymbol (Klasse) 1 Aktivierungsstrategien, BOA 1 Aktor, OOA 1 aktualisiereKontostand(), Methode 1, 2 aktualisiereKontoThreadFunktion(), Funktion 1 anfordernAktualisierungsDienst() Methode 1 anfordernAktualisierungsDienst() Methode : BankImpl class 1 Anfrage 1 Anwendung : als Client und als Server 1 Anwendung : Client 1 Anwendung : CORBA in Java 1 Anwendung : DII 1 Anwendung : entwickeln 1 Anwendung : von GIOP-abgeleitetes Protokoll 1 Anwendung ausführen : Bankanwendung : BankServer (Komponente) 1 Anwendung ausführen : Bankanwendung : CORBA-Bezeichnungsdienst 1 Anwendungsentwicklung (Einrichtung) 1 Any, Typ, DII 1 any-Typ 1, 2 Aonix-Software 1
API, Socket 1 Applet (Java) 1 Applets : BankApplet 1 Applets : BankApplet : Ausführen 1 Applets : BankApplet : Client-Funktionen 1 Applets : BankApplet : Kunde, Klasse 1, 2 Applets : BankApplet : Server-Funktionen 1 appletviewer 1, 2, 3 Arbeitsablauf, Task Management Common Facilities 1 Arbeitsverwaltungssystem 1 Architektur : CORBA 1 Architektur : Java im Vergleich zu CORBA 1 Architektur : Java im Vergleich zu CORBA : Java-RMI 1, 2 arguments(), Methode, Beispiel zu DII 1 Arrays 1 Assoziation, UML 1, 2 Attribut : Datenzugriffsmethode 1 Attribut : interface-Typ 1 Attribut : Kunde (Klasse) 1 Attribut : readonly 1
Aufforderung zur Abgabe von Informationen (RFI) 1 Aufforderung zur Einreichung von Vorschlägen (RFP) 1 Aufgaben- und Prozeßautomatisierung 1 aufhebenRegistrierungBank() (Methode) 1 aufhebenRegistrierungBank() (Methode) : Exception 1 Aufzählungstyp 1 Ausführen : Client 1, 2 Ausführen : Server 1, 2 ausgebenBankKarte(), Methode 1 Auslagerungsdienst 1 Auslagerungsdienst : Bankanwendung 1 Ausschließlicher oneway-Aufruf, Muster 1 AuthentifizierungException, Exception 1 AWT 1
-BBank (Klasse) 1, 2, 3 Bank (Komponente), Bankanwendung starten 1 Bank (Schnittstelle) 1, 2, 3, 4, 5, 6, 7 Bank (Schnittstelle) : Exception 1, 2 Bank (Schnittstelle) : Methoden, Exception 1
Bank (Server) 1 Bank, Anwendung : Geldautomat : Anforderungen 1 Bank, Anwendung : Geldautomat : exceptions 1 Bank, Anwendung : Geldautomat : Implementierung von Funktionen 1 Bank, Anwendung : Geldautomat : Klassen 1 Bank, Anwendung : Geldautomat : Schritte beim Hinzufügen 1 Bank, Anwendung : Geldautomat : zusätzliche Anforderungen 1 Bank, Anwendung : Kontoaktualisierung : Ausführen 1, 2, 3 Bank, Anwendung : Kontoaktualisierung : Implementierung 1 Bank, Anwendung : Kontoaktualisierung : Klassen 1 Bank, Anwendung : Kontoaktualisierung : zukünftige Erweiterungen 1 Bank, Anwendung : Kontoaktualisierung Anforderungen 1 Bank, Klasse : BankKarte, Klasse 1 Bank, Klasse : Geldautomat, Implementierung 1, 2, 3, 4 Bank.idl 1, 2 Bank.idl : für Exceptions aufbereitet 1 Bank.idl : für Geldautomat modifiziert 1 Bank.idl : modifiziert für Kontoaktualisierung 1 Bankanwendung : ausführen 1 Bankanwendung : ausführen : Bank (Komponente) 1 Bankanwendung : ausführen : BankServer (Komponente) 1, 2
Bankanwendung : ausführen : Client-Anwendung 1, 2 Bankanwendung : ausführen : CORBA-Bezeichnungsdienst 1, 2 Bankanwendung : Bank (Schnittstelle) 1, 2, 3, 4, 5, 6, 7 Bankanwendung : BankServer (Schnittstelle) 1, 2, 3, 4, 5 Bankanwendung : Bezeichnungsdienst 1 Bankanwendung : Bezeichnungsdienst : ausführen 1 Bankanwendung : Client 1 Bankanwendung : Client : Kunde (Schnittstelle) 1, 2 Bankanwendung : Client : zusätzliche Funktionen 1 Bankanwendung : CORBAservices 1, 2 Bankanwendung : CORBAservices : implementieren 1 Bankanwendung : Exception : Bank (Schnittstelle) 1, 2 Bankanwendung : Exception : BankServer (Schnittstelle) 1, 2 Bankanwendung : Exception : Beispielanwendung ausführen 1, 2 Bankanwendung : Exception : Client-Quelltext 1, 2, 3 Bankanwendung : Exception : GiroKonto (Schnittstelle) 1 Bankanwendung : Exception : Konto (Schnittstelle) 1, 2 Bankanwendung : Exception : Kunde (Schnittstelle) 1 Bankanwendung : Exception : Server-IDL 1, 2, 3, 4 Bankanwendung : Exception : Server-Quelltext 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
Bankanwendung : Exception : SparKonto (Schnittstelle) 1 Bankanwendung : GiroKonto (Schnittstelle) 1, 2 Bankanwendung : Java-Version 1, 2, 3, 4, 5, 6, 7, 8, 9 Bankanwendung : Java-Version : Ausführung 1 Bankanwendung : Konto (Schnittstelle) 1, 2, 3 Bankanwendung : OOA 1 Bankanwendung : OOA : Klassendiagramm 1, 2 Bankanwendung : OOA : Systemanforderungen 1, 2 Bankanwendung : OOA : Systemobjekt 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 Bankanwendung : Server 1 Bankanwendung : SparKonto (Schnittstelle) 1, 2 BankApplet 1 BankApplet : Ausführen 1, 2 BankApplet : Ausführen : appletviewer 1, 2, 3 BankApplet : Ausführen : Web browser 1 BankApplet : Client-Funktionen 1 BankApplet : Client-Funktionen : Kunde, Klasse 1, 2 BankApplet : Dialogfelder 1 BankApplet : Java-Werkzeuge 1 BankApplet : Server-Funktionen 1 BankApplet.html 1
BankApplet.java 1 bankChoice_ItemStateChanged(), Methode (Java) : BankApplet 1 BankHaupt.cpp 1, 2, 3 BankHaupt.java 1 BankImpl, Exception 1, 2, 3 BankImpl, Klasse 1, 2, 3, 4 BankImpl, Klasse : Geldautomat, Klassen 1 BankImpl.cpp 1, 2, 3 BankImpl.cpp : Analyse 1, 2 BankImpl.cpp : für Exceptions aufbereitet 1 BankImpl.h 1, 2, 3 BankImpl.h : für Exceptions aufbereitet 1 BankImpl.java 1 BankKarte, Klasse 1, 2 BankKarte, Klasse : Geldautomat-Server 1 BankKarte.idl 1 BankKarteImpl, Schnittstelle 1 BankKarteImpl.cpp 1 BankKarteImpl.h 1 BankKarteImpl.java 1
BankServer (Klasse) 1 BankServer (Komponente) : Bankanwendung ausführen 1, 2 BankServer (Schnittstelle) 1, 2, 3, 4, 5 BankServer (Schnittstelle) : Exception 1, 2 BankServer app : Konto update 1 BankServer app : running 1 BankServer, Anwendung : Ausführen 1 BankServer, Anwendung : Ausführung 1 BankServer, Bezeichnungsdienst 1 BankServer, Schnittstelle : Geldautomat, Implementierung 1, 2 BankServer, Schnittstelle : Geldautomat, Klasse 1 BankServer, Schnittstelle : Geldautomat-Implementierung 1 BankServer.idl 1, 2 BankServer.idl : für Exceptions aufbereitet 1 BankServer-Anwendung : Java-Version 1, 2, 3, 4 BankServerHaupt.cpp 1, 2, 3 BankServerHaupt.java 1 BankServerImpl (Klasse) 1 BankServerImpl (Konstruktor), Vector (Klasse) 1 BankServerImpl, Exception 1, 2, 3 BankServerImpl.cpp 1, 2
BankServerImpl.cpp : für Exceptions aufbereitet 1 BankServerImpl.h 1, 2 BankServerImpl.h : für Exceptions aufbereitet 1 BankServerImpl.java 1 Basic Object Adapter (BOA), Objektmodell 1 Befehlszeile, Schalter, -c cpp 1 Befehlszeilenparameter, -fclient 1 Begriffe : abgeleitete Klassen 1 Begriffe : Aktivierungsstrategie 1 Begriffe : Anfrage (Request) 1 Begriffe : Assoziation 1 Begriffe : auslösen (Exceptions) 1 Begriffe : bindende Klasse 1 Begriffe : blockieren 1 Begriffe : Callback 1 Begriffe : Client 1 Begriffe : Client-Stub 1 Begriffe : Delegation 1 Begriffe : Entscheider (Discriminator) 1 Begriffe : Exception auslösen 1
Begriffe : Formatübertragung 1 Begriffe : GIOP (General Inter-ORB Protocol) 1 Begriffe : IDL (Interface Definition Language) 1 Begriffe : IIOP (Internet Inter-ORB Protocol) 1 Begriffe : Implementierungsvererbung 1 Begriffe : Klassendiagramm 1 Begriffe : Methodensignatur 1 Begriffe : Multiplizität 1 Begriffe : Partitionierung 1 Begriffe : Plattformunabhängigkeit 1 Begriffe : Polymorphie 1 Begriffe : Remote-Prozedurenaufruf (RPC) 1 Begriffe : Schnittstelle 1 Begriffe : Schnittstellenvererbung 1 Begriffe : Serialisierung 1 Begriffe : Server 1 Begriffe : Server-Skeleton 1 Begriffe : Sprachabbildung 1 Begriffe : Sprachenunabhängigkeit 1 Begriffe : Transaktionsmonitor 1 Begriffe : Übergabe durch den Wert 1
Begriffe : Übergabe durch Referenz 1 Begriffe : übergeordnete Klassen 1 Begriffe : Übertragungsformat 1 Begriffe : umgekehrte Formatübertragung 1 Begriffe : Vererbung 1 Begriffe : Verzeichnisdienst 1 Begriffe : zirkuläre Abhängigkeit 1 Benutzeroberfläche : Aktivatoren 1 Benutzeroberfläche : Aussehen 1 Bezeichner, Gültigkeitsbereichsbeschränkungen 1 Bezeichnungsdienst 1 Bezeichnungsdienst : Bankanwendung 1, 2, 3 Bezeichnungsdienst : Bankanwendung 1 Bezeichnungsdienst : Client, Server-Objekt auffinden 1 Bezeichnungsdienst : IDL-Schnittstelle 1 Bezeichnungskontext, Bankanwendung 1 Bezeichnungs-Server, Ausführung aus Java 1 Beziehung, Klassen 1 Beziehungsdienst 1 Beziehungsdienst : Bankanwendung 1
Bilddatenverarbeitung 1 bind(), Methode 1 bind(), Methode : Beispiel zu DII 1 Binden 1 Bindung, module-Konstrukt 1, 2 Black &White Software 1 blockieren 1 Blocking 1 Börsen-Server (Beispiel) : Server-Schnittstelle 1 Börsen-Server (Beispiel), Server-Schnittstelle 1, 2 BOA, Java 1 BoersenMarktClient.java, Quelltext-Listing 1 BoersenServer (Schnittstelle) 1 BoersenServer.idl, Stub 1 BoersenServer.java (Datei) 1 BoersenServerImpl.java (Datei) : Analyse 1 BoersenServerImpl.java (Datei) : Quelltext-Listing 1 BoersenServerImplBase.java (Datei) 1 BoersenServer-Schnittstelle, Client-Stub 1 boolean-Typ 1 Botschaft 1
Buchhaltung (Einrichtung) 1 Business Object Domain Task Force 1, 2 Bytecode (Java) 1
---c cpp (Schalter) 1
-CC++ : Exception deklarieren 1 C++ : ORBstreams.h++ 1 Callbacks 1 char-Typ 1 CHORUS/COOL ORB 1 Client 1, 2 Client : Anwendung als Client und als Server 1, 2 Client : Bank 1 Client : Bank : Kunde (Schnittstelle) 1, 2 Client : Bank : zusätzliche Funktionen 1 Client : Bankanwendung ausführen 1, 2 Client : Bezeichnungsdienst, IDL-Schnittstelle 1 Client : entwickeln 1
Client : implementieren 1 Client : implementieren : Server-Objekt 1 Client : implementieren : Server-Objektschnittstelle 1, 2 Client : implementieren : Stub 1, 2 Client : kompilieren/ausführen 1, 2 Client : Objektverteilung 1 Client : Plattformunabhängigkeit, ORB 1 Client-Anwendungen : Bankanwendung, Java 1 Client-Quelltext, Exception 1, 2, 3 Clients : BankApplet 1 Clients : BankApplet : Kunde, Klasse 1, 2 Clients : DII 1 Clients : Einfach-Thread-Anwendungen 1 Clients : Geldautomat 1, 2, 3 Clients : Konversionskonstruktoren 1 Client-Seite, Java-Anwendungen 1 Client-Server-Architektur : Geschichte : mehrschichtige Architektur 1, 2 Client-Server-Architektur, Geschichte 1, 2 Client-Server-Architektur, Geschichte : mehrschichtige Architektur 1 Client-Stub 1, 2, 3
Cobol : mehrschichtige Client-Server-Architektur 1 Communicator 4.0 1 Concurrency-Steuerungsdienst 1 Concurrency-Steuerungsdienst : Bankanwendung 1 connect (Methode) 1 const-Modifizierer 1 Container-Typen 1 Container-Typen : Arrays 1 Container-Typen : sequence 1 CORBA : Alternativen 1 CORBA : Alternativen : DCE 1 CORBA : Alternativen : DCOM 1, 2 CORBA : Alternativen : RMI 1 CORBA : Alternativen : RPC 1 CORBA : Alternativen : Socket-Programmierung 1 CORBA : Geschichte : OMG 1 CORBA : Geschichte : Version 1.0 1 CORBA : Geschichte : Version 2.0 1 CORBA : Überblick 1, 2 CORBA : Überblick über die Architektur 1 CORBA : Übersicht über die Architektur 1
CORBA : Übersicht über die Architektur : Clients/Server 1 CORBA : Übersicht über die Architektur : CORBAfacilities 1 CORBA : Übersicht über die Architektur : CORBAservices 1 CORBA : Übersicht über die Architektur : IDL 1 CORBA : Übersicht über die Architektur : Kommunikationsmodell 1, 2 CORBA : Übersicht über die Architektur : Objektmodell 1 CORBA : Übersicht über die Architektur : ORB 1 CORBA : Übersicht über die Architektur : Stubs/Skeletons 1 CORBA 2.1 1 CORBA 3.0 1 CORBAAlgorithms.java 1 CORBA-Bezeichnungsdienst : Bankanwendung 1, 2 CORBAfacilities 1, 2, 3, 4 CORBAfacilities : Domain Technology Committee 1 CORBAfacilities : horizontal 1 CORBAfacilities : horizontal : Information Management Common Facilities 1 CORBAfacilities : horizontal : Systems Management Common Facilities 1 CORBAfacilities : horizontal : Task Management Common Facilities 1 CORBAfacilities : horizontal : User Interface Common Facilities 1 CORBAfacilities : vertikaler Markt 1, 2
CORBA-kompatibel : IIOP 1 CORBAmed Domain Task Force 1, 2 CORBAplus 1 CORBAservices 1, 2, 3 CORBAservices : Abfragedienst 1 CORBAservices : Auslagerungsdienst 1 CORBAservices : Bankanwendung 1, 2 CORBAservices : Bankanwendung : implementieren 1 CORBAservices : Bezeichnungsdienst 1 CORBAservices : Beziehungsdienst 1 CORBAservices : Concurrency-Steuerungsdienst 1 CORBAservices : Eigenschaftendienst 1 CORBAservices : Ereignisdienst 1 CORBAservices : Lebenszyklusdienst 1 CORBAservices : Lizenzierungsdienst 1 CORBAservices : Objektpersistenzdienst 1 CORBAservices : Objektvermittlungsdienst 1 CORBAservices : Sicherheitsdienst 1 CORBAservices : Transaktionsdienst 1 CORBAservices : Zeitgeberdienst 1 Corbus 1
C-Präprozessor : IDL 1, 2 create_request(), Methode 1 createCustomer(), Methode (Java) : BankApplet 1 CustomerDialog.java 1
-DDaemon 1 Datenautobahnen (Einrichtung) 1 Datensammlung 1 Datentyp : IDL 1 Datentypen : Arrays 1 Datenzugriffsmethode : Objektattribute 1 DCE 1 DCOM 1, 2 Definitionen, IDL 1 Definitionssyntax, interface-Typ 1, 2 Delegation, IDL-Schnittstellen 1 Destruktoren, Schnittstellensyntax 1 Dialogfelder, BankApplet 1 Dienstprogramme : CORBA-relevant : Black &White Software 1 Dienstprogramme : CORBA-relevant : Distributed Smalltalk 1
Dienstprogramme : CORBA-relevant : Rose 1 Dienstprogramme : CORBA-relevant : SNiFF+ 1 Dienstprogramme : CORBA-relevant : Software through Pictures 1 Dienstprogramme : CORBA-relevant : Universal Network Architecture Services (UNAS) 1 Dienstprogramme : ORB : CHORUS/COOL ORB 1 Dienstprogramme : ORB : Communicator 4.0 1 Dienstprogramme : ORB : CORBAplus 1 Dienstprogramme : ORB : Corbus 1 Dienstprogramme : ORB : DOME 1 Dienstprogramme : ORB : Enterprise Server 1 Dienstprogramme : ORB : ILU 1 Dienstprogramme : ORB : Jaguar CTS 1 Dienstprogramme : ORB : NEO 1 Dienstprogramme : ORB : ObjectBroker 1 Dienstprogramme : ORB : OmniBroker 1 Dienstprogramme : ORB : omniORB2 1 Dienstprogramme : ORB : ORB Plus 1 Dienstprogramme : ORB : Orbix 1, 2 Dienstprogramme : ORB : SmalltalkBroker 1 Dienstprogramme : ORB : SOMobjects 1
Dienstprogramme : ORB : TIB/ObjectBus 1 Dienstprogramme : ORB : VisiBroker 1 Dienstprogramme : ORB : Voyager 1 Dienstqualitätsverwaltung 1 DII 1 DII : (Dynamic Invocation Interface) 1 DII : Any/TypeCode, Typen 1 DII : Beispiel 1, 2, 3 DII : Nachteile 1 DII : Request, Objekt 1, 2 DII : Vgl. mit statischen Schnittstellen/IDL 1, 2 DII : Vorteile 1 DII : Zweck 1 DII : Zweck : hypothetisches Entwurfswerkzeug 1 DII : Zweck : Objekt-Browser 1, 2 Discriminator 1, 2 Distributed Application, Platform Architecture, (Visigenic/DAP) 1 Distributed Smalltalk 1 Distributed Smalltalk (ParcPlace) 1 Domain Technology Committee 1 Domain Technology Committee : Electronic Commerce Domain Task Force 1
Domain Technology Committee : Business Object Domain Task Force 1, 2 Domain Technology Committee : CORBAmed Task Force (CORBAmed) 1, 2 Domain Technology Committee : Electronic Commerce Domain Task Force 1 Domain Technology Committee : Financial Domain Task Force 1, 2 Domain Technology Committee : Manufacturing Domain Task Force 1 Domain Technology Committee : Telecommunications Domain Task Force 1, 2 Domain Technology Committee : Transportation Domain Task Force (CORBAtransport) 1 DOME 1 double-Typ 1 Dreischichtige Client-Server-Architektur 1 duplicate() (Methode) 1 Dynamic Invocation Interface, siehe DII 1
-EEckige Klammern, Array 1 Eigenschaftendienst 1 Eigenschaftendienst : Bankanwendung 1 Einfache Typen 1 Einfache Typen : boolean 1 Einfache Typen : char 1 Einfache Typen : const-Modifizierer 1
Einfache Typen : double 1 Einfache Typen : float 1 Einfache Typen : IDL 1 Einfache Typen : Integer 1 Einfache Typen : long 1 Einfache Typen : long double 1 Einfache Typen : long long 1 Einfache Typen : octet 1 Einfache Typen : short 1 Einfache Typen : string 1 Einfache Typen : unsigned long 1 Einfache Typen : unsigned long long 1 Einfache Typen : unsigned short 1 Einfache Typen : void 1 Einfache Typen : wchar 1 Einfach-Thread-Anwendungen 1 Einfach-Thread-Anwendungen : X Window, System 1 Einfach-Thread-Anwendungen : Clients 1 Einfach-Thread-Anwendungen : Server 1, 2 Einfach-Thread-Anwendungen : Server/Client 1, 2
Einfach-Thread-Anwendungen : Server/Client : Object Factory, Muster 1, 2 Einfach-Thread-Anwendungen : Server/Client : oneway-Aufruf, Muster 1, 2, 3 Einfach-Thread-Anwendungen : X Window, System : Multithread-Anwendungen 1 Einfach-Thread-Anwendungen : X Window, System : Multithread-Anwendungen 1 Einrichtungen für den vertikalen Markt 1, 2 Eins-zu-Eins-Assoziation 1 Eins-zu-Viele-Assoziation 1 Einzahlung() (Methode), Exception 1 Electronic Commerce Domain Task Force 1, 2 enableKontoFeatures(), Methode (Java), BankApplet 1 Enge Bindung 1 Enterprise Server 1 Entscheider 1 Entwicklung, IDL, Java 1 Entwurf : Einfach-Thread-Anwendungen 1 Entwurf : Einfach-Thread-Anwendungen : Clients 1 Entwurf : Einfach-Thread-Anwendungen : Server 1, 2 Entwurf : Einfach-Thread-Anwendungen : Server/Client 1, 2, 3, 4, 5, 6, 7 Entwurf : enge Bindung 1 Entwurf : IDL creep 1 Entwurf : IDL Creep 1
Entwurf : Lebensdauer von Objekten 1, 2, 3 Entwurf : Server-Schnittstelle 1 Entwurfsmuster 1 env(), Methode, Request, Objekt 1 erase() (Methode) 1 Ereignisdienst 1 Ereignisdienst : Bankanwendung 1 Ereignisschleifen, Einfach-Thread-Anwendungen und : X Window, System 1 Ereignisverwaltung 1 Erschließung und Förderung in der %@%l- und Gasindustrie (Einrichtung) 1 erstelleKonto() (Methode) 1, 2 erstelleKonto(), Methode (Java), BankApplet 1 Evictor 1 Exception : Bank (Schnittstelle) 1, 2 Exception : BankServer (Schnittstelle) 1, 2 Exception : Client-Quelltext 1, 2, 3 Exception : definieren 1 Exception : GiroKonto (Schnittstelle) 1 Exception : Java 1 Exception : Konto (Schnittstelle) 1, 2
Exception : Kunde (Schnittstelle) 1 Exception : Objektverteilung 1 Exception : Server-IDL 1, 2, 3, 4 Exception : Server-Quelltext 1 Exception : Server-Quelltext : BankImpl 1, 2, 3 Exception : Server-Quelltext : BankServerImpl 1, 2, 3 Exception : Server-Quelltext : GiroKontoImpl 1, 2, 3 Exception : Server-Quelltext : KontoImpl 1, 2, 3 Exception : Server-Quelltext : SparKontoImpl 1, 2, 3 Exception : SparKonto (Schnittstelle) 1 Exception : verbesserte Beispieldatei 1, 2 Exceptions : AuthentifizierungException 1 Exceptions : BankApplet 1 Exceptions : UngueltigerGeldautomatException 1 Exceptions.idl 1 Exceptions.idl : für Geldautomat modifiziert 1 Exception-Typen 1, 2 Exception-Typen : Exception 1 Exception-Typen : Standard 1, 2
-F-
Factory-Objekte 1
---fclient (Schalter) 1
-FFehler, Exception, siehe auch Exception 1 Fehlerbehandlung : Exception-Typ 1 Fertigung (Einrichtung) 1 Financial Domain Task Force 1, 2 float-Typ 1 Format, Übertragungsformat 1 Formatübertragung 1, 2 Formatübertragung : Plattformunabhängigkeit 1 forward-Deklarationen 1, 2 Funktionen, aktualisiereKontoThreadFunktion() 1
-GGarbage Collection (Java) 1 GateKeeper (VisiBroker) 1 Geldautomat : Anforderungen 1
Geldautomat : Ausführen 1, 2 Geldautomat : Ausführung 1 Geldautomat : Exceptions : AuthentifizierungException 1 Geldautomat : Exceptions : UngueltigerGeldautomatException 1 Geldautomat : Grundfunktionen 1 Geldautomat : IDL-Spezifikation 1, 2, 3, 4 Geldautomat : Implementierung der Funktionen : BankServer, Schnittstelle 1 Geldautomat : Implementierung von Funktionen 1 Geldautomat : Implementierung von Funktionen : Bank, Klasse 1, 2, 3 Geldautomat : Implementierung von Funktionen : BankServer, Schnittstelle 1 Geldautomat : Implementierung von Funktionen : Geldautomat-Client 1, 2, 3 Geldautomat : Implementierung von Funktionen : Geldautomat-Server 1, 2, 3 Geldautomat : Implementierung, Funktionalität, BankServer, Schnittstelle 1 Geldautomat : Klassen : bestehende Klassen 1, 2 Geldautomat : Klassen : Geldautomat/BankKarte 1 Geldautomat : Klassen : GeldAutomat/BankKarte 1 Geldautomat, Anwendung, Kontoaktualisierung, Anwendung 1 Geldautomat, Klasse 1 GeldAutomat, Klasse 1 Geldautomat, Klasse : Schnittstelle 1 Geldautomat.idl 1
Geldautomat-Anwendung, Java-Version 1 Geldautomat-Client, Geldautomat, Implementierung 1, 2, 3 GeldautomatClient, Schnittstelle 1 GeldautomatClientHaupt.cpp 1, 2, 3, 4, 5, 6 GeldautomatClientHaupt.cpp : Analyse 1, 2 GeldautomatClientHaupt.java 1 GeldautomatHaupt.cpp 1, 2 GeldautomatHaupt.java 1 GeldautomatImpl, Objekt 1 GeldautomatImpl.cpp 1 GeldautomatImpl.h 1 GeldautomatImpl.java 1 Geldautomat-Server, Geldautomat, Implementierung 1, 2, 3 gemischte Server-/Client-Anwendungen 1 generischer Objekt-Browser (DII) 1 Geringe Kopplung 1 Geschäftslogik, Client-Server-Anwendungen 1 Geschweifte Klammern, Definitionen 1 getAktienSymbole (Methode) 1, 2 getAktienWert (Methode) 1
getBanken() (Methode), Exception 1 getKonten() (Methode) 1 getKonten() (Methode) : Exception 1 getKunden() (Methode), Exception 1 GIOP (General Inter-ORB Protocol) 1, 2 GiroKonto (Klasse) 1 GiroKonto (Schnittstelle) 1, 2 GiroKonto (Schnittstelle) : Exception 1 GiroKonto, Objekt, GeldautomatClient, Anwendung 1 GiroKonto.idl 1, 2 GiroKonto.idl : für Exceptions aufbereitet 1 GiroKontoImpl : Exception 1, 2, 3 GiroKontoImpl.cpp 1 GiroKontoImpl.cpp : für Exceptions aufbereitet 1 GiroKontoImpl.h 1 GiroKontoImpl.h : für Exceptions aufbereitet 1 GiroKontoImpl.java 1 GiroKontos 1 GNU General Public License (GPL) 1 Groß-/Kleinschreibung, IDL 1 Großrechner 1
Gruppenverwaltung 1 GUI, Java-Werkzeuge 1
-HHashtable, Klasse 1 helloWorld(), Methode, Beispiel zu DII 1 Horizontale Einrichtungen 1 Horizontale Einrichtungen : Information Management Common Facilities 1 Horizontale Einrichtungen : Systems Management Common Facilities 1 Horizontale Einrichtungen : Task Management Common Facilities 1 Horizontale Einrichtungen : User Interface Common Facilities 1 HTML : \\>-Tag (BankApplet) 1
-IIDL 1, 2 IDL : any-Typ 1, 2 IDL : Arrays 1 IDL : BoersenServer-Schnittstelle 1 IDL : Client-Stub 1, 2 IDL : Compiler : Funktion als Übersetzer 1 IDL : Compiler : Java 1, 2
IDL : Compiler : Java-Anwendungen 1 IDL : Compiler : Java-Klasse 1 IDL : Compiler : Server 1, 2, 3 IDL : Compiler : Server-Schnittstelle 1, 2, 3, 4, 5, 6 IDL : Container-Typen 1 IDL : CORBA 1.0 1 IDL : C-Präprozessor 1, 2 IDL : Einfache Typen 1 IDL : enum-Typ 1 IDL : Exception 1, 2, 3, 4 IDL : Exception-Typen 1, 2, 3 IDL : Exception-Typen : Standard 1 IDL : forward-Deklarationen 1 IDL : forward-Deklarationen 1 IDL : GiroKonto (Schnittstelle) 1 IDL : Groß-/Kleinschreibung 1 IDL : interface 1, 2, 3, 4 IDL : interface-Typ 1, 2, 3, 4 IDL : Java-Klassen 1 IDL : Kommentare 1
IDL : Konstrukt module 1 IDL : Kopplung und Bindung 1, 2 IDL : module-Konstrukt 1 IDL : Schnittstelle 1, 2, 3 IDL : Schnittstelle : Implementierungsmechanismen 1 IDL : Schnittstelle : Quelltextdateien 1 IDL : Schnittstellen 1 IDL : sequence-Typ 1 IDL : Server-Skeleton 1, 2 IDL : siehe auch Bankanwendung 1 IDL : SparKonto (Schnittstelle) 1 IDL : Spezifikation : Geldautomat 1, 2, 3, 4 IDL : Spezifikation : Kontoaktualisierung 1, 2, 3 IDL : Sprachenunabhängigkeit 1, 2 IDL : Standard-Exception-Typen 1 IDL : struct-Typ 1 IDL : TypeCode-Typ 1 IDL : typedef-Konstrukt 1 IDL : union-Typ 1, 2 IDL : Vgl. mit DII 1, 2 IDL : Zusammengesetzte Typen 1
IDL Creep 1, 2 IDL-Definitionssyntax 1 IDL-Definitionssyntax : Semikolon 1 idlGlobal (Package) 1 IDL-Typen : boolean 1 IDL-Typen : char 1 IDL-Typen : const-Modifizierer 1 IDL-Typen : double 1 IDL-Typen : float 1 IDL-Typen : Integer 1 IDL-Typen : long 1 IDL-Typen : long double 1 IDL-Typen : long long 1 IDL-Typen : octet 1 IDL-Typen : short 1 IDL-Typen : string 1 IDL-Typen : unsigned long 1 IDL-Typen : unsigned long long 1 IDL-Typen : unsigned short 1 IDL-Typen : void 1
IDL-Typen : wchar 1 IIOP : CORBA 2.0 1 IIOP : GIOP 1 IIOP : Java-RMI 1 ILU 1 impl_is_ready() (Methode) 1 Implementierung : Client 1 Implementierung : Client : Server-Objekt 1 Implementierung : Client : Server-Objektschnittstelle 1, 2 Implementierung : Client : Stub 1 Implementierung : Client : Stubs 1 Implementierung : Server : Ansatz wählen 1 Implementierung : Server, Ansatz wählen 1 Implementierung : Server-Schnittstelle 1, 2, 3 Implementierung : Server-Schnittstelle : Quelltext 1, 2, 3 Implementierung von Funktionen, Bank, Klasse 1 Implementierungsvererbung 1 import (Java-Anweisung) 1 Information Management Common Facilities 1 init(), Methode (Java), BankApplet 1 inout-Parameter 1
in-Parameter 1 Instanzenverwaltung 1 Instrumentierung 1 Integer-Typen 1 interface-Typ 1, 2 interface-Typ : Attribute 1, 2 interface-Typ : Definitionssyntax 1, 2 interface-Typ : Methoden und Parameter 1, 2 interface-Typ : Methoden/Parameter 1, 2 interface-Typ : readonly-Attribute 1 interface-Typ : Vererbung 1, 2 invoke(), Methode, Request, Objekt 1 IONA (Produkte) 1 IstBankGleich (Klasse) 1, 2
-JJ++ 1 Jaguar CTS 1 Java 1, 2 Java : Bankanwendung 1, 2, 3, 4, 5, 6, 7, 8 Java : BankApplet 1
Java : BankApplet : Client-Funktionen 1 Java : BankApplet : Kunde, Klasse 1, 2 Java : BankApplet : Server-Funktionen 1 Java : BankServer-Anwendung 1, 2, 3, 4 Java : Client-Funktionalität 1 Java : CORBA-Anwendungen 1 Java : Entfernen nicht benötigter Objekte 1 Java : Geldautomat-Anwendung 1 Java : IDL 1 Java : IDL-Compiler 1, 2 Java : Klasse, IDL-Compiler 1 Java : Konstruktor, IDL-Schnittstelle 1 Java : nicht-CORBA-Anwendung : statische Methode 1 Java : OrbixWeb for Java (Dienstprogramm) 1 Java : RMI 1 Java : Server-Schnittstellen implementieren 1 Java : Vergleich mit CORBA 1 Java : Vergleich mit CORBA : Java-RMI 1, 2 Java : VisiBroker 1 Java : VisiBroker für Java 1
Java : Voyager 1 Java Workshop 1 java.awt, Package 1 java.util, Package 1 JBuilder, BankApplet 1 Joe 1
-KkillObject() (Methode) 1 Klasse : Assoziation 1, 2 Klasse : Attribut 1 Klasse : Java : IDL-Implementierung 1 Klasse : Java : import (Anweisung) 1 Klasse : Java-RMI 1 Klasse : Sichtbarkeitsmodifizierer 1 Klasse : übergeordnete Klasse 1 Klasse : UML 1, 2 Klasse : Vererbung 1 Klassen : AktienSymbol 1 Klassen : BankImpl, Kontoaktualisierung 1, 2, 3, 4 Klassen : BankServerImpl 1
Klassen : Geldautomat/BankKarte 1 Klassen : GeldAutomat/BankKarte 1 Klassen : IDL creep 1 Klassen : IstBankGleich 1, 2 Klassen : Kontoaktualisierung 1, 2 Klassen : KundenImpl, Kontoaktualisierung 1, 2 Klassen : Vector 1 Klassenbeschreibung 1 Klassendiagramm 1 Klassendiagramm : Bankanwendung 1 Klassendiagramm : Bankanwendung 1 Kommentare, IDL 1 Kommunikationsmodell 1, 2 Kommunikationsmodell : Netzwerkmodell 1 Kommunikationsmodell : Protokolle für die Kommunikation zwischen ORBs 1, 2 Kompilieren : Client 1, 2 Kompilieren : Server 1, 2 Konstrukte : forward-Deklarationen 1, 2 Konstrukte : module 1 Konstrukte : typedef 1 Konstruktor : IDL-Schnittstelle 1
Konstruktor : Schnittstellensyntax 1 Konto (Klasse) 1, 2 Konto (Klasse) 1 Konto (Schnittstelle) 1, 2, 3, 4 Konto (Schnittstelle) : Exception 1, 2 Konto.idl 1, 2 Konto.idl : für Exceptions aufbereitet 1 Kontoaktualisierung : Ausführen 1, 2, 3 Kontoaktualisierung : BankImpl, Klasse 1, 2, 3 Kontoaktualisierung : BankImpl,Klasse 1 Kontoaktualisierung : GeldautomatClient, Schnittstelle 1 Kontoaktualisierung : IDL-Spezifikation 1, 2, 3 Kontoaktualisierung : Implementierung 1 Kontoaktualisierung : Klassen 1, 2 Kontoaktualisierung : KundenImpl, Klasse 1, 2 Kontoaktualisierung : zukünftige Erweiterungen 1 KontoDialog, Fenster 1 KontoDialog.java 1 KontoImpl, Exception 1, 2, 3 KontoImpl.cpp 1
KontoImpl.cpp : für Exceptions aufbereitet 1 KontoImpl.h 1 KontoImpl.h : für Exceptions aufbereitet 1 KontoImpl.java 1 KontoUpdateListener interface 1 Kontrollfluß, oneway-Methoden 1 Konversionskonstruktoren : Übergabe als Wert 1, 2 Kopplung, module-Konstrukt 1, 2 Kunde (Klasse) 1, 2 Kunde (Schnittstelle) 1, 2 Kunde (Schnittstelle) : Exception 1 Kunde interface, GeldautomatClientHaupt.cpp 1 Kunde, Klasse : BankApplet 1, 2 Kunde, Klasse : BankKarte, Klasse 1 Kunde, Klasse : Kontoaktualisierung 1 Kunde.idl 1, 2 Kunde.idl : für Exceptions aufbereitet 1 Kunde.idl : modifiziert für Kontoaktualisierung 1 KundenImpl, Klasse 1, 2 KundenImpl.cpp 1, 2 KundenImpl.h 1, 2
KundenImpl.java 1
-LLanguage Mapping 1 Lebenszyklusdienst 1 Lebenszyklusdienst : Bankanwendung 1 Listing, siehe Quelltext-Listings 1 Lizenzierungsdienst 1 Lizenzierungsdienst : Bankanwendung 1 loescheKonto() (Methode) 1 loescheKonto() (Methode) : Exception 1 loescheKonto() (Methode) : try...catch-Block 1 long double-Typ 1 long long-Typen 1 long-Typ 1 loose coupling 1
-Mmain() (Java-Methode) 1 Makro-Definitionen, C-Präprozessor 1 Manufacturing Domain Task Force 1
Marshaling 1, 2, 3 Mehrfachvererbung 1 Mehrprozessorverarbeitung, Multithreading 1 Mehrschichtige Client-Server-Architektur, Geschichte 1, 2, 3 meineAktualisiertenKonten, Datenelement 1 Methode : Abhebung() 1 Methode : aktualisiereKontostand() 1, 2 Methode : anfordernAktualisierungsDienst() 1 Methode : arguments(), Beispiel zu DII 1 Methode : aufhebenRegistrierungBank() 1 Methode : ausgebenBankKarte() 1 Methode : BankServer (Schnittstelle) 1 Methode : bind() 1, 2 Methode : bind() : Beispiel zu DII 1 Methode : Callback-Methoden 1 Methode : connect 1 Methode : CORBAAlgorithms.java 1 Methode : create_request() 1 Methode : DII, Rückgabeparameter 1 Methode : duplicate() 1
Methode : env(), Request, Objekt 1 Methode : erase() 1 Methode : erstelleKonto() 1, 2 Methode : Exceptions 1 Methode : Formatübertragung der aufrufenden Parameter 1 Methode : Geldautomat, Klasse : AuthentifizierungException 1 Methode : getAktienSymbole 1, 2 Methode : getAktienWert 1 Methode : impl_is_ready() 1 Methode : invoke(), Request, Objekt 1 Methode : killObject() 1 Methode : listKonto() 1 Methode : loescheKonto() 1 Methode : main() (Java) 1 Methode : name() 1 Methode : Object Factory, Muster 1 Methode : oneway 1, 2, 3 Methode : poll_response(), Request, Objekt 1 Methode : registriereBank() 1, 2 Methode : registriereGeldautomat() 1 Methode : release() 1
Methode : send_deferred(), Request, Objekt 1 Methode : send_oneway(), Request, Objekt 1 Methode : setZinsSatz() 1 Methode : Skeleton, Server-Schnittstelle 1 Methode : structs 1 Methode : Ueberweisung() 1 Methodenaufrufe, TypeCode-Typ 1 Methodensignaturen 1 MFS (Message Format Service) 1 Modell 1 Modell-Sicht (Muster) 1 module (CORBA-Konstrukt) 1 Module : BoersenMarkt 1 Module : geringe Kopplung/enge Bindung 1 module-Konstrukt 1, 2 module-Konstrukt : IDL 1 module-Konstrukt : Kopplung und Bindung 1, 2 Monolithisches System 1 Motif, Multithread-Anwendungen 1 Multiplizität (Assoziation) 1
Multithread-Anwendungen, X Window, System 1, 2 Multithreading 1 Multithreading : Blockieren 1 Multithreading : gemischte Server-/Client-Anwendung 1 Multithreading : Kosten 1 Multithreading : Object Factory, Muster 1 Multithreading : Server 1, 2, 3 Multithread-Server-Architektur 1 Mutator-Methoden 1
-NNachrichtenformatdienst (MFS) 1 name() (Methode) 1 NamingContext (Objekt) 1 Navigator 1 NEO 1 Netzwerkmodell 1 Netzwerkprotokoll, Kommunikationsmodell 1 NeuerKunde (Komponente) 1 NeuerKundeHauptmodul.cpp 1 NeuerKundeHauptmodul.cpp : für Exceptions aufbereitet 1
newCustomerButton_Action(), Methode (Java), BankApplet 1 Nichtprogrammierbares Terminal 1 Notation, Array 1, 2 NVList, Objekt 1
-OObject Analysis and Design Task Force (OA&D TF) 1, 2 Object Factory, Muster 1, 2 Object Management Architecture 1 Object Management Group 1 Object Management Group (OMG) 1 Object Management Group : Geschichte von CORBA 1 Object Modeling Technique (OMT) 1 Object Oriented Software Engineering (OOSE) 1 Object Services Platform Task Force, ORB 1 Object/LM 1 Object/Observer 1 ObjectBroker 1 Object-Oriented Concepts 1 objects, lifetime 1 Objekt : Bank-Anwendung 1
Objekt : BankServer 1 Objekt : Referenzübergabe 1 Objekt : Systemobjekt definieren 1 Objekt : Übergabe durch Werte 1 Objektanfrage, Referenzen 1 Objekt-Browser, DII 1 Objekte : BankApplet 1 Objekte : Lebensdauer 1, 2 Objekte : NamingContext 1 Objekte : Request (DII) 1 Objekte : Übergabe als Wert 1 Objekte : Übergabe als Wert : Konversionskonstruktoren 1, 2 Objekte : Übergabe als Wert : ORBstreams.h++ 1 Objekte : Übergabe als Wert : structs 1, 2 Objektidentität, Bankanwendung 1, 2 Objektmodell 1, 2 Objektmodell : Basic Object Adapter (BOA) 1 Objektmodell : Objektreferenz 1, 2, 3 Objektmodell : Objektverteilung 1 Objektpersistenzdienst 1
Objektpersistenzdienst : Bankanwendung 1 Objektreferenz : Objektmodell 1, 2, 3 Objektreferenz : ORB 1 Objektserialisierung (Java-RMI) 1 Objektvermittlungsdienst 1 Objektvermittlungsdienst : Bankanwendung 1 Objektverteilung, Objektmodell 1 octet-Typ 1 OMA 1 OMA : CORBAfacilities 1 OMA : Einrichtungen für den vertikalen Markt 1 OMA : ORB 1 OMG : BOA 1 OMG : Operationen 1 OMG : Sprachabbildung 1 OMG : Task Forces (Spezialeinheiten) 1 OMG : Web-Seite 1 OMG-Geschäftsobjekt 1 OmniBroker 1 omniORB2 1 OMT (Object Modeling Technique) 1
oneway-Aufruf, Muster 1, 2, 3 oneway-Methoden 1 oneway-Methoden : DII 1 OOA 1, 2 OOA : Assoziation, Benennungsschemata 1 OOA : Bankanwendung 1 OOA : Bankanwendung : Klassendiagramm 1, 2 OOA : Bankanwendung : Systemanforderungen 1 OOA : Bankanwendung : Systemanforderungen 1 OOA : Bankanwendung : Systemobjekt 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 OOA : Entwurfsmuster 1 OOA : Object-Oriented Analysis (objektorientierte Analyse) 1 OOA : Objektidentität 1 OOA : UML 1 OOA : Use-Case 1 OOD 1 OOD : Object-Oriented Design (objektorientierter Entwurf) 1 OOSE (Object-Oriented Software Engineering) 1 Open Software Foundation (OSF), DCE 1 Operation 1
ORB 1, 2 ORB : CHORUS/COOL ORB 1 ORB : Communicator 1 ORB : CORBAplus 1 ORB : Corbus 1 ORB : DOME 1 ORB : Enterprise Server 1 ORB : Formatübertragung 1, 2 ORB : Geschichte 1 ORB : ILU 1 ORB : Jaguar CTS 1 ORB : Java 1 ORB : NEO 1 ORB : ObjectBroker 1 ORB : OmniBroker 1 ORB : omniORB2 1 ORB : ORB Plus 1 ORB : Orbix 1, 2 ORB : Plattformunabhängigkeit 1 ORB : SmalltalkBroker 1 ORB : SOMobjects 1
ORB : TIB/ObjectBus 1 ORB : VisiBroker 1 ORB : Voyager 1 ORB Plus 1 ORB(colonx2)bind(), Methode, Beispiel zu DII 1 ORB.init(), Methode (Java), BankApplet 1 Orb/Enable 1 ORB/Object Services Task Force 1 ORBeline Smart Agent 1 Orbix 1, 2 OrbixBuilder 1 OrbixWeb, Java 1 ORBstreams.h++ 1 osagent (Dienstprogramm) 1 out-Parameter 1
-Ppackage (Java-Konstrukt) 1 Parameter : Entscheider (Discriminator) 1 Parameter : Formatübertragung 1 Parameter : in, out und inout 1
PARC, ILU 1 Partitionierung 1 Partitioning 1 PATH-Variable, IDL-Compiler 1 Permanenter Server 1 persistenter Speicher, Evictor 1 Physikalische Ebene, Kommunikationsmodell 1 PIN : BankKarte, Klasse 1 PIN : GeldautomatClientHaupt.cpp 1 Planungsverwaltung 1 Platform Technology Committee, Task Forces 1 Plattformunabhängigkeit 1 Plattformunabhängigkeit : ORB 1 poll_response(), Methode, Request, Objekt 1 Polymorphie 1, 2 Polymorphismus, structs 1 Portabilität, Java 1 Protokoll : IIOP, CORBA-kompatibel 1 Protokoll : Kommunikation zwischen ORBs 1 Protokoll zur Kommunikation zwischen ORB, Kommunikationsmodell 1
Protokolle : Kommunikation zwischen ORBs 1 Protokolle für die Kommunikation zwischen ORB, Kommunikationsmodell 1 Protokollschicht für die Kommunikation zwischen ORBs 1
-QQuelltext-Beispiele, union-Typ 1 Quelltext-Listings : Ausgabe für BoersenMarktClient 1 Quelltext-Listings : Ausgabe für BoersenServer 1 Quelltext-Listings : Bank.idl 1, 2 Quelltext-Listings : Bank.idl : für Exceptions aufbereitet 1 Quelltext-Listings : Bank.idl : für Geldautomat modifiziert 1 Quelltext-Listings : Bank.idl : modifiziert für Kontoaktualisierung 1 Quelltext-Listings : BankApplet.html 1 Quelltext-Listings : BankApplet.java 1 Quelltext-Listings : BankHaupt.cpp 1, 2, 3 Quelltext-Listings : BankHaupt.java 1 Quelltext-Listings : BankImpl.cpp 1, 2, 3 Quelltext-Listings : BankImpl.cpp : für Exceptions aufbereitet 1 Quelltext-Listings : BankImpl.h 1, 2 Quelltext-Listings : BankImpl.h : für Exceptions aufbereitet 1 Quelltext-Listings : BankImpl.java 1
Quelltext-Listings : BankKarteImpl.cpp 1 Quelltext-Listings : BankKarteImpl.h 1 Quelltext-Listings : BankKarteImpl.java 1 Quelltext-Listings : BankServer.idl 1, 2 Quelltext-Listings : BankServer.idl : für Exceptions aufbereitet 1 Quelltext-Listings : BankServerHaupt.cpp 1, 2, 3 Quelltext-Listings : BankServerHaupt.java 1 Quelltext-Listings : BankServerImpl.cpp 1, 2 Quelltext-Listings : BankServerImpl.cpp : für Exceptions aufbereitet 1 Quelltext-Listings : BankServerImpl.h 1, 2 Quelltext-Listings : BankServerImpl.h : für Exceptions aufbereitet 1 Quelltext-Listings : BankServerImpl.java 1 Quelltext-Listings : BoersenMarkt.idl 1 Quelltext-Listings : BoersenMarktClient.java 1 Quelltext-Listings : BoersenServer.java 1 Quelltext-Listings : BoersenServer-Dienste 1 Quelltext-Listings : BoersenServerImpl.java 1 Quelltext-Listings : CORBAAlgorithms.java 1 Quelltext-Listings : CustomerDialog.java 1 Quelltext-Listings : enum-Typ 1 Quelltext-Listings : Exception.idl, modifiziert mit Geldautomat-Exceptions 1
Quelltext-Listings : Exception-Definition 1 Quelltext-Listings : Exceptions.idl 1 Quelltext-Listings : Geldautomat.idl 1 Quelltext-Listings : GeldautomatClientHaupt.cpp 1, 2, 3, 4, 5, 6 Quelltext-Listings : GeldautomatClientHaupt.java 1 Quelltext-Listings : GeldautomatHaupt.cpp 1, 2 Quelltext-Listings : GeldautomatHaupt.java 1 Quelltext-Listings : GeldautomatImpl.cpp 1 Quelltext-Listings : GeldautomatImpl.h 1 Quelltext-Listings : GeldautomatImpl.java 1 Quelltext-Listings : GiroKonto.idl 1, 2 Quelltext-Listings : GiroKonto.idl : für Exceptions aufbereitet 1 Quelltext-Listings : GiroKontoImpl.cpp 1 Quelltext-Listings : GiroKontoImpl.cpp : für Exceptions aufbereitet 1 Quelltext-Listings : GiroKontoImpl.h 1 Quelltext-Listings : GiroKontoImpl.h : für Exceptions aufbereitet 1 Quelltext-Listings : GiroKontoImpl.java 1 Quelltext-Listings : IDL-Schnittstellen 1 Quelltext-Listings : Kommentare in IDL 1 Quelltext-Listings : Konto.idl 1, 2
Quelltext-Listings : Konto.idl : für Exceptions aufbereitet 1 Quelltext-Listings : KontoDialog.java 1 Quelltext-Listings : KontoImpl.cpp 1 Quelltext-Listings : KontoImpl.cpp : für Exceptions aufbereitet 1 Quelltext-Listings : KontoImpl.h 1 Quelltext-Listings : KontoImpl.h : für Exceptions aufbereitet 1 Quelltext-Listings : KontoImpl.java 1 Quelltext-Listings : Kunde.idl 1, 2 Quelltext-Listings : Kunde.idl : für Exceptions aufbereitet 1 Quelltext-Listings : Kunde.idl : modifiziert für Kontoaktualisierung 1 Quelltext-Listings : KundenImpl.cpp 1, 2 Quelltext-Listings : KundenImpl.h 1, 2 Quelltext-Listings : KundenImpl.java 1 Quelltext-Listings : Mehrfachvererbungssyntax 1 Quelltext-Listings : module-Konstrukt 1 Quelltext-Listings : NeuerKundeHauptmodul.cpp 1 Quelltext-Listings : NeuerKundeHauptmodul.cpp : für Exceptions aufbereitet 1 Quelltext-Listings : Server BoersenServer 1 Quelltext-Listings : SparKonto.idl 1, 2 Quelltext-Listings : SparKonto.idl : für Exceptions aufbereitet 1 Quelltext-Listings : SparKontoImpl.cpp 1
Quelltext-Listings : SparKontoImpl.cpp : für Exceptions aufbereitet 1 Quelltext-Listings : SparKontoImpl.h 1 Quelltext-Listings : SparKontoImpl.h : für Exceptions aufbereitet 1 Quelltext-Listings : SparKontoImpl.java 1 Quelltext-Listings : struct-Typ 1 Quelltext-Listings : Typ any 1 Quelltext-Listings : UpdateAccountThread.java 1 Quelltext-Listings : Vererbungssyntax 1 Quelltext-Listings : zirkuläre Abhängigkeit 1, 2
-RRational Software Corporation 1 reference counting, object lifetime 1 Referenzübergabe 1 Referenzzähler 1 Regelverwaltung, Task Management Common Facilities 1 registriereBank() (Methode) 1, 2 registriereBank() (Methode) : Exception 1 registriereGeldautomat() Methode 1 release() (Methode) 1 remote Methoden : Object Factory pattern 1
remote Methoden : pass-by-value semantics 1 Remote-Methode, Formatübertragung 1 Remote-Objekte, structs 1 Request 1 request(), Methode, Beispiel zu DII 1 Request, Objekt : DII 1, 2 RFI 1 RFI : Asset and Content Management RFI (EC RFI1) 1 RFI : Clinical Decision Support RFI (CORBAmed RFI3) 1 RFI : Clinical Observations RFI (CORBAmed RFI2) 1 RFI : Common Business Objects RFI (CBO RFI) 1 RFI : CORBAtransport RFI 1 RFI : Enabling Technologies and Services for EC (EC RFI2) 1 RFI : HL7 RFI 1 RFI : Issues for Intelligent Networking with CORBA (Telecom RFI) 1 RFI : Lifescience RFI 1 RFI : Manufacturing DTF RFI-2 1 RFI : Manufacturing High-Level Requirements RFI (MFG RFI1) 1 RFI : Object Services Platform Task Force 1 RFI : ORB and Object Services RFI 1 1
RFI : ORB and Object Services RFI 2 1 RFP 1 RFP (Request for Proposals) : Übergabe durch Werte 1 RFP : Analysis and Design Task Force RFP1 1 RFP : Common Business Object : and Business Object Facility RFP 1 RFP : CORBA Component Model RFP (ORBOS RFP8) 1 RFP : CORBA Scripting Language RFP (ORBOS RFP9) 1 RFP : CORBA/TMN Interworking RFP 1 RFP : Currency RFP (Finance RFP31) 1 RFP : Data Interchange Facility and : Mobile Agent Facility RFP 1 RFP : DCE/CORBA Interworking RFP (ORBOS RFP6) 1 RFP : Electronic Healthcare Claims Facility RFP (EC RFP3) 1 RFP : Electronic Payment Facility (EC RFP1) 1 RFP : Finance DTF Insurance RFI 1 RFP : Finance/Insurance Party Management Facility RFP (Finance RFP2) 1 RFP : Firewall RFP 1 RFP : Healthcare Lexicon Service RFP (CORBAmed RFP2) 1 RFP : Input Methode Manager Facility RFP 1 RFP : Java to IDL RFP (ORBOS RFP5) 1 RFP : Messaging Service RFP (ORBOS RFP1) 1 RFP : Minimum CORBA RFP (ORBOS RFP10) 1
RFP : Multiple Interfaces and Composition RFP 1 RFP : Negotiation Facility RFP (EC RFP2) 1 RFP : Notification Service RFP (Telecom RFP3) 1 RFP : Object Analysis and Design Task Force 1 RFP : Objects-by-Value RFP (ORBOS RFP2) 1 RFP : Patient Identification Services RFP (CORBAmed RFP1) 1 RFP : Persistent State Service, : Version 2.0 RFP (ORBOS RFP7) 1 RFP : Printing Facility RFP 1 RFP : Product Data Management : Enablers RFP (MFG RFP1) 1 RFP : Realtime CORBA 1.0 RFP 1 RFP : Topology RFP (Telecom RFP2) 1 RFP : Workflow Management Facility RFP 1 RMI 1 RMI : CORBA im Vergleich zu Java 1, 2 RMI : Rolle von Client-Server 1 Rogue Wave Software, ORBstreams.h++ 1 Rose 1 Rose (Rational) 1 RPC 1 Rückruf 1, 2
-SSchalter : -c cpp 1 Schalter : -fclient 1 Schicht für die Benutzeroberfläche 1 Schicht für die Geschäftsregeln 1 Schicht, Protokoll für die Kommunikation zwischen ORBs 1 Schlüsselwort, virtual 1 Schnittstelle 1 Schnittstelle : Architektur der verteilten Systeme 1 Schnittstelle : Bank 1, 2, 3, 4, 5, 6, 7 Schnittstelle : Bank : Exception 1, 2 Schnittstelle : BankServer 1, 2, 3, 4, 5 Schnittstelle : BankServer : Exception 1, 2 Schnittstelle : BoersenServer 1 Schnittstelle : Client-Stub 1 Schnittstelle : CORBAfacilities 1 Schnittstelle : CORBAservices 1 Schnittstelle : GiroKonto 1, 2 Schnittstelle : GiroKonto : Exception 1 Schnittstelle : IDL 1
Schnittstelle : Konto 1, 2, 3, 4 Schnittstelle : Konto : Exception 1, 2 Schnittstelle : Kunde 1, 2 Schnittstelle : Kunde : Exception 1 Schnittstelle : module-Konstrukt 1 Schnittstelle : Server definieren 1, 2, 3 Schnittstelle : Server-Skeletons 1 Schnittstelle : Sicherheitsdienst 1 Schnittstelle : SparKonto 1, 2 Schnittstelle : SparKonto : Exception 1 Schnittstelle : Systems Management Common Facilities 1 Schnittstelle : zusammenzugruppieren 1 Schnittstellen : Geldautomat, Klasse 1 Schnittstellen : GeldautomatClient : Kontoaktualisierung 1 Schnittstellen : IDL creep 1 Schnittstellen : structs 1 Schnittstellenvererbung 1 Semikolon, IDL-Definitionssyntax 1 send_deferred(), Methode, Request, Objekt 1 send_oneway(), Methode, Request, Objekt 1 sequence-Typ 1
Serialisierung 1 Server 1, 2 Server : Aktivierungsstrategie 1 Server : Anwendung als Client und als Server 1, 2 Server : Bank 1 Server : Bank : Bank (Schnittstelle) 1, 2, 3, 4, 5, 6, 7 Server : Bank : BankServer (Schnittstelle) 1, 2, 3, 4, 5 Server : Bank : GiroKonto (Schnittstelle) 1, 2 Server : Bank : Konto (Schnittstelle) 1, 2, 3 Server : Bank : SparKonto (Schnittstelle) 1, 2 Server : BankApplet 1 Server : Einfach-Thread-Anwendungen 1, 2 Server : entwickeln 1 Server : Geldautomat 1, 2, 3 Server : IDL, Exception 1, 2, 3, 4 Server : IDL-Compiler 1, 2, 3 Server : Implementierungsansatz 1, 2 Server : kompilieren/ausführen 1, 2 Server : Objekt, Client implementieren 1 Server : Objektschnittstelle : Client implementieren 1, 2
Server : Schnittstelle : definieren 1, 2, 3 Server : Schnittstelle : implementieren 1, 2, 3, 4, 5, 6 Server-/Client-Anwendungen : Einfach-Thread-Anwendungen 1, 2 Server-/Client-Anwendungen : Einfach-Thread-Anwendungen : Object Factory, Muster 1, 2 Server-/Client-Anwendungen : Einfach-Thread-Anwendungen : oneway-Aufruf, Muster 1, 2, 3 Server-Quelltext : Exception 1 Server-Quelltext : Exception : BankImpl 1, 2, 3 Server-Quelltext : Exception : BankServerImpl 1, 2, 3 Server-Quelltext : Exception : GiroKontoImpl 1, 2, 3 Server-Quelltext : Exception : KontoImpl 1, 2, 3 Server-Quelltext : Exception : SparKontoImpl 1, 2, 3 Server-Skeleton 1, 2, 3 Server-Skeleton : implementieren 1 setStatus(), Methode (Java), BankApplet 1 setZinsSatz() (Methode) 1 setZinsSatz() (Methode) : Exception 1 short-Typ 1 Sicherheit, Java-Applet 1 Sicherheitsdienst 1 Sicherheitsdienst : Bankanwendung 1 Sicht 1
Sichtbarkeitsmodifizierer 1 Signaturen 1 Skeleton : IDL-Compiler 1 Skeleton : Server-Schnittstelle, implementieren 1 Skriptsprachen, DII-Objekt-Browser 1 SmalltalkBroker 1 SNiFF+ 1 Software through Pictures 1 Solaris NEO 1 Solstice NEO 1 SOMobjects 1 SparKonto (Klasse) 1 SparKonto (Schnittstelle) 1, 2 SparKonto (Schnittstelle) : Exception 1 SparKonto.idl 1, 2 SparKonto.idl : für Exceptions aufbereitet 1 SparKontoImpl, Exception 1, 2, 3 SparKontoImpl.cpp 1 SparKontoImpl.cpp : für Exceptions aufbereitet 1 SparKontoImpl.h 1
SparKontoImpl.h : für Exceptions aufbereitet 1 SparKontoImpl.java 1 SparKontos 1 Speicher, Lebensdauer von Objekten 1 Sprachabbildung 1, 2 Sprachabbildung : IDL-Compiler 1 Sprachenunabhängigkeit 1 Sprachenunabhängigkeit : IDL 1, 2 Standard-Exceptions in CORBA, Tabelle 1 Statische Methode (Java) 1 Statische Methode, nicht-CORBA-Anwendung 1 statische Typprüfung, DII 1 Statisches Klassenelement, Java 1 STL 1 Strategie der gemeinsam verwendeten Server 1 Strategie der nicht gemeinsam verwendeten Server 1 Strategie des permanenten Servers 1 Strategie des Server-pro-Methode 1 Strategie, Server-Aktivierung 1 Strategien zur Server-Aktivierung 1, 2 Strategieverwaltung 1
string-Typ 1 structs, Übergabe als Wert 1, 2 struct-Typ 1 Strukturtyp 1 Stub : Client implementieren 1, 2 Stub : IDL-Compiler 1 Subclass 1 Superclass 1 Sybase, Jaguar CTS 1 Syntax : Schnittstellenvererbung 1 Syntax : Vorwärtsdeklaration 1 System-Exceptions 1 Systemobjekt definieren 1 Systems Management Common Facilities 1
-TTakeFive (Software) 1 Task Force (Spezialeinheit), OMG 1 Task Management Common Facilities 1 TCP/IP, IIOP 1 Telecommunications Domain Task Force 1, 2
Terminal, nichtprogrammierbares 1 Threads 1 Thread-Warteschlangen, Multithreading 1 TIB/ObjectBus 1 TIBCO 1 tight cohesion 1 Tools.h++ 1 Transaktionen, Multithread-Server-Architektur 1 Transaktionsdienst 1 Transaktionsdienst : Bankanwendung 1 Transaktionsmonitor 1 Transportation Domain Task Force 1 Transportschicht, Kommunikationsmodell 1 try ... catch-Block : loescheKonto() (Methode) 1 TypeCode-Typ 1 TypeCode-Typ : DII 1 typedef-Konstrukt 1 Typen : any 1, 2 Typen : Arrays 1 Typen : Container 1
Typen : einfache 1 Typen : enum 1 Typen : Exception 1 Typen : Gleitkommatypen 1 Typen : Integer-Typen 1 Typen : interface 1, 2 Typen : sequence 1 Typen : Standard-Exceptions 1, 2 Typen : struct 1 Typen : TypeCode 1 Typen : union 1, 2 Typen : zusammengesetzte 1 Types, Exception 1, 2
- ÜÜbergabe als Wert 1 Übergabe als Wert : Konversionskonstruktoren 1, 2 Übergabe als Wert : ORBstreams.h++ 1 Übergabe als Wert : structs 1, 2 Übergabe durch Referenz 1 Übergabe durch Werte 1
Übergabe per Referenz 1 Übergeordnete Klasse 1, 2 Übertragungsformat 1
-UUeberweisung() (Methode) 1 UIM/Orbix 1 UML : (Unified Modeling Language) 1 UML : Assoziation 1, 2 UML : Begriffe/Symbole 1 UML : Geschichte 1 UML : Klasse 1, 2 UML : Klasse : Sichtbarkeitsmodifizierer 1 UML : Klassendiagramm 1 UML : Methodik 1, 2 UML : Modell-Sicht (Muster) 1 UML : Sicht 1 UML : Vererbung 1 UML : Zusammenfassung 1 UML Partners (Konsortium) 1 UNAS (TRW) 1
UngueltigeBankException (Exception) 1 UngueltigerGeldautomatException, Exception 1 Unified Modeling Language, siehe UML 1 union-Typ 1, 2 Universal Network Architecture Services (UNAS) 1 Unix, BankImpl.cpp 1 Unix-gestützter Server, Client-Server-Architektur 1 Unmarshaling 1 unsereZeichen (Array) 1 unsigned long long-Typ 1 unsigned long-Typ 1 unsigned short-Typ 1 UpdateAccountThread.java 1 UpdateAccountThread-Objekt (Java) 1 updateKontoInfo(), Methode (Java), BankApplet 1 Use Cases 1 User Interface Common Facilities 1
-VVector (Java-Klasse) 1 Vector (Klasse) 1
Vererbung : IDL-Schnittstellen 1 Vererbung : Schnittstellen 1, 2, 3 Vererbung : structs 1 Vererbung : UML 1 Verteilte Simulation (Einrichtung) 1 Verteiltes System : Geschichte 1 Verteiltes System : Geschichte : Client-Server-Architektur 1, 2 Verteiltes System : Geschichte : Großrechner 1 Verteiltes System : Geschichte : mehrschichtige Client-Server-Architektur 1, 2 Verteiltes System : Geschichte : verteilte Systeme 1, 2, 3 Verzeichnis, Server-Skeleton, Server-Schnittstelle 1 Verzeichnisdienst 1 Viele-zu-Viele-Assoziation 1 virtual (Schlüsselwort) 1 VisiBroker 1 VisiBroker : GateKeeper 1 VisiBroker : ORBeline Smart Agent 1 VisiBroker for Java 1 VisiBroker für C++, Exception-Behandlung 1 Visual Age für Java 1
Visual Cafe 1 Visual Cafe : BankApplet 1 Visual J++, BankApplet 1 void-Typ 1 Voyager 1
-Wwchar-Typ 1 Web browsers, BankApplet 1 Web/Enable 1 Web-Anwendungen : BankApplet 1 Web-Anwendungen : BankApplet : Client-Funktionen 1 Web-Anwendungen : BankApplet : Kunde, Klasse 1, 2 Web-Anwendungen : BankApplet : Server-Funktionen 1 Webseite, OMG 1 Werkzeuge : CORBA-relevant : Black &White Software 1 Werkzeuge : CORBA-relevant : Distributed Smalltalk 1 Werkzeuge : CORBA-relevant : Rose 1 Werkzeuge : CORBA-relevant : SNiFF+ 1 Werkzeuge : CORBA-relevant : Software through Pictures 1 Werkzeuge : CORBA-relevant : Universal Network Architecture (UNAS) 1
Werkzeuge : Java 1 Werkzeuge : ORB : CHORUS/COOL ORB 1 Werkzeuge : ORB : Communicator 4.0 1 Werkzeuge : ORB : CORBAplus 1 Werkzeuge : ORB : Corbus 1 Werkzeuge : ORB : DOME 1 Werkzeuge : ORB : Enterprise Server 1 Werkzeuge : ORB : ILU 1 Werkzeuge : ORB : Jaguar CTS 1 Werkzeuge : ORB : NEO 1 Werkzeuge : ORB : ObjectBroker 1 Werkzeuge : ORB : OmniBroker 1 Werkzeuge : ORB : omniORB2 1 Werkzeuge : ORB : ORB Plus 1 Werkzeuge : ORB : Orbix 1, 2 Werkzeuge : ORB : SmalltalkBroker 1 Werkzeuge : ORB : SOMobjects 1 Werkzeuge : ORB : TIB/ObjectBus 1 Werkzeuge : ORB : VisiBroker 1 Werkzeuge : ORB : Voyager 1 Win32-APIs, BankImpl.cpp 1
Windows, NEO Connectivity 1 WWW-Seite, PARC 1
-XX Window, System : Einfach-Thread-Anwendungen 1 X Window, System : Multithread-Anwendungen 1 X Window, System : Multithreaded-Anwendungen 1 Xerox PARC, ILU 1
-ZZeiger, Beispiel zu DII 1 Zeitgeberdienst 1 Zeitgeberdienst : Bankanwendung 1 Zirkuläre Abhängigkeit 1 Zusammengesetzte Typen 1 Zusammengesetzte Typen : Attribute 1, 2 Zusammengesetzte Typen : enum 1 Zusammengesetzte Typen : interface 1, 2 Zusammengesetzte Typen : interface-Definitionssyntax 1, 2 Zusammengesetzte Typen : Methoden/Parameter 1, 2 Zusammengesetzte Typen : Schnittstelle 1
Zusammengesetzte Typen : struct 1 Zusammengesetzte Typen : union 1, 2 Zusammengesetzte Typen : Vererbung 1, 2 Zweischichtige Client-Server-Architektur 1
Ein Imprint des Markt&Technik Buch- und Software-Verlag GmbH. Elektronische Fassung des Titels: Corba in 14 Tagen, ISBN: 3-8272-2031-9
Wir sind an Ihrer Meinung interessiert! Als Teil unseres Bestrebens, Bücher höchster Qualität zu produzieren, ist SAMS an Ihrer Meinung interessiert. Sie können Ihre Kommentare, Ideen oder Vorschläge zur Verbesserung zukünftiger Ausgaben an die unten angegebene Adresse oder per Fax an 089-46003-330 schicken. Online-Verbindung bekommen Sie über das Internet http://www.mut.com. Schon jetzt vielen Dank – Ihre Kommentare werden uns dabei helfen, weiterhin die besten Computerbücher herauszugeben.
SAMS
Fragen zur HTML-Version?
Markt&Technik Jörg Wieter Hans-Pinsel-Str. 9B 85540 Haar bei München
Markt&Technik Harald Jörg Hans-Pinsel-Str. 9b 85540 Haar bei München
Tag 1 CORBA kennenlernen Was können Sie von diesem Buch erwarten? Etwas zum Hintergrund: die Geschichte der verteilten Systeme Der Anfang: monolithische Systeme und Großrechner Die Revolution: Client-Server-Architektur Die Weiterentwicklung: mehrschichtige Client-Server-Architektur Die nächste Generation: verteilte Systeme Welche Rolle spielt hier CORBA? CORBA 1.0 Die CORBA-Architektur im Überblick Zusammenfassung Fragen & Antworten Workshop
Was können Sie von diesem Buch erwarten? Natürlich ist dies nicht das erste Buch, das über die Common Object Request Broker Architecture (CORBA) geschrieben wird - bei weitem nicht. Es dürfte jedoch unter den Büchern, die derzeit zu CORBA erhältlich sind, vom Ansatz her einzigartig sein. Als dieses Buch geschrieben wurde, waren, wenn überhaupt, nur wenige Veröffentlichungen erhältlich, die eine Einführung in CORBA enthielten. Mit dem vorliegenden Buch soll diese Lücke geschlossen werden.
CORBA ist nichts für Zauderer, das ist sicher. Obwohl Entwicklungswerkzeuge verfügbar sind, die einen Teil der Komplexität von CORBA kaschieren, stehen die Chancen gut, daß Sie bei der Inangriffnahme eines Projekts, bei dem Sie eine einigermaßen komplizierte CORBA-Anwendung entwickeln möchten, die Komplexität von CORBA am eigenen Leibe zu spüren bekommen. Trotzdem gilt: Die Lernkurve für CORBA verläuft zwar sehr steil, aber jeder kompetente Programmierer ist in der Lage, gute CORBA-Kenntnisse zu erlangen. In diesem Buch wird davon ausgegangen, daß Sie bereits über ein gewisses Maß an Programmiererfahrungen verfügen. CORBA ist zwar eine sprachenunabhängige Architektur, da aber CORBA-Anwendungen hauptsächlich mit C++ und Java entwickelt werden, sind Kenntnisse in einer dieser Sprachen von Vorteil. (Die meisten Beispiele in diesem Buch sind in C++ geschrieben, wozu sich als Ausgleich eine gewisse Anzahl an Java-Anwendungen gesellt.) Es würde außerdem nichts schaden, wenn Sie sich bereits mit objektorientierten Analyse- und Entwurfstechniken auskennen, aber wenn Sie hier ein wenig Auffrischung brauchen, wird Ihnen das Buch bei der Wiederholung dieser Materie helfen. Dieses Buch geht von der Annahme aus, daß das Lernen von CORBA für die meisten Programmierer ein (wenn auch unter Schwierigkeiten) erreichbares Ziel ist, und beginnt mit den Grundlagen von CORBA, wobei zunächst ein Überblick über die Architektur gegeben wird. Dann folgt eine Einführung in die Schnittstellen-Definitionssprache Interface Definition Language (IDL), einem Eckpfeiler, auf dem die meisten CORBA-Anwendungen basieren. Anschließend werden Sie die ersten CORBA-Anwendungen programmieren, und bevor Sie es merken, lernen Sie weiterführende Konzepte und Entwurfsaspekte kennen, zusammen mit anderen nützlichen Dingen wie CORBAservices, CORBAfacilities und der dynamischen Aufrufschnittstelle (Dynamic Invocation Interface) oder abgekürzt DII (keine Angst, Sie werden lernen, was das alles bedeutet, wenn es an der Reihe ist). All dies - und noch mehr - in nur 14 Tagen. Was dieses Buch nicht leistet und nicht leisten kann: Aus Ihnen über Nacht (oder in unserem Fall in 14 Tagen) einen CORBA-Experten machen. Das Buch zeigt Ihnen nur den Weg zur Beherrschung von CORBA. Bedenken Sie, daß es sich bei CORBA um eine komplexe Architektur handelt, bei deren Verwendung Sie viele Entwurfsaspekte und Kompromisse sowie Nuancen bei der Implementierung berücksichtigen müssen. Daher führt das Beherrschen von CORBA nur über die Erfahrung, die Sie mit dem Entwerfen und Entwickeln von CORBA-Anwendungen gewinnen. Dieses Buch wird Sie sicherlich nicht zu einem globalen CORBA-Spezialisten machen, aber es wird Ihnen helfen, eine gute Grundlage zu erlangen.
Etwas zum Hintergrund: die Geschichte der verteilten Systeme Wenn Sie so viel Interesse für CORBA aufbringen, daß Sie dieses Buch lesen, werden Sie wahrscheinlich bereits einiges über verteilte Systeme wissen. Verteilte Systeme gibt es in der einen oder anderen Form bereits eine ganze Weile, obwohl sie nicht immer so genannt wurden und mit Sicherheit nicht die Flexibilität aufwiesen, die sie heute auszeichnet. Wenn man nun herausfinden möchte, wohin in diesem Zusammenhang CORBA gehört, ist ein kurzer Blick auf die Geschichte der verteilten Systeme hilfreich. Alles begann mit dem ehrwürdigen Großrechner.
Der Anfang: monolithische Systeme und Großrechner Am Anfang (oder jedenfalls in zeitlicher Nähe dazu) war der Großrechner. Mit ihm zusammen kamen hierarchische Datenbanksysteme und nichtprogrammierbare Terminals auf , auch Einfachterminals genannt. Großrechner waren in der Regel sehr teuer in der Wartung, konnten aber eine große Anzahl von Benutzern bedienen und hatten den Vorteil (oder Nachteil, je nach Sichtweise), daß sie zentral verwaltet wurden. Die für Großrechner geschriebene Software war oft monolithisch, d.h., die Benutzeroberfläche, die Geschäftslogikfunktionen und die Funktionalität für den Datenzugriff waren in einer einzigen, großen Anwendung enthalten. Da die nichtprogrammierbaren Terminals, die für den Zugriff auf die Großrechner benutzt wurden, keine Daten verarbeiten konnten, lief die gesamte Anwendung auf dem Großrechner selbst, was die monolithische Architektur sinnvoll machte. Eine typische monolithische Architektur ist in Abbildung 1.1 zu sehen.
Abbildung 1.1: Eine typische monolithische Anwendungsarchitektur
Die Revolution: Client-Server-Architektur Das Aufkommen der PCs führte zu einer grundlegenden Abkehr vom Paradigma der monolithischen Architektur mit ihren auf Großrechnern laufenden Anwendungen. Während es bei diesen Anwendungen erforderlich war, daß der Großrechner selbst die gesamte Verarbeitung übernahm, ermöglichten es Anwendungen, die auf der Client-Server-Architektur beruhten, daß ein Teil der Verarbeitung auf die PCs an den Arbeitsplätzen der Anwender abgewälzt werden konnte. Zusammen mit der Client-Server-Revolution verbreiteten sich Unix-gestützte Server . Für viele Anwendungen war die gewaltige Rechenleistung der Großrechner einfach nicht erforderlich, und da es mit der Client-Server-Architektur möglich war, einen Großteil der Verarbeitungslast auf die PCs zu übertragen, war der Einsatz dieser kleineren Unix-gestützten Server oft kostengünstiger als der von Großrechnern. Auch waren diese Rechner für kleinere Unternehmen sehr viel preisgünstiger als Großrechner, die sich Firmen mit verhältnismäßig kleinen Umsätzen einfach nicht leisten konnten. Ein weiterer Vorteil war die Stärkung der einzelnen Abteilungen innerhalb eines Unternehmens, die eigene Server verteilten und verwalteten. Ergebnis: Diese Abteilungen konnten schneller auf ihre speziellen Bedürfnisse reagieren, indem sie ihre eigenen Anwendungen entwickelten. Dadurch entfielen die umständlichen und oft unfruchtbaren Verhandlungen, die Abteilungen, die die Kontrolle über die Großrechner hatten, dazu zu bringen, die benötigten Anwendungen zu entwickeln. Und schließlich konnten PCs, im Gegensatz zu den Einfachterminals, die auf das Ausführen von Anwendungen auf dem Großrechner beschränkt waren, viele andere Aufgaben unabhängig vom Großrechner durchführen, was ihre Nützlichkeit als Arbeitsplatzcomputer weiter erhöhte. Client-Server-Anwendungen verteilten die Komponenten der Anwendung in der Regel so, daß sich die Datenbank auf dem Server befand (unabhängig davon, ob es sich dabei um einen Unix-Rechner oder um einen Großrechner handelte), die Benutzeroberfläche befand sich auf dem Client, und die Geschäftslogik befand sich in einer oder in beiden Komponenten. Wenn an Teilen der ClientKomponente Änderungen vorgenommen wurden, mußten neue Kopien der Client-Komponente (in der Regel eine ausführbare Datei oder eine Gruppe von ausführbaren Dateien) an jeden Benutzer verteilt werden. Mit dem Aufkommen der mehrschichtigen Client-Server-Architektur (die ich im nächsten Abschnitt behandeln werde) kam es dazu, daß die »ursprüngliche« Client-Server-Architektur nun als »zweischichtige« Client-Server-Architektur bezeichnet wird. Diese zweischichtige Client-ServerArchitektur ist in Abbildung 1.2 gezeigt.
Abbildung 1.2: Zweischichtige Client-Server-Architektur
Die Weiterentwicklung: mehrschichtige Client-Server-Architektur Die Client-Server-Architektur war in vieler Hinsicht eine Revolution, eine Abkehr von herkömmlichen Verfahrensweisen. Diese Architektur löste zwar die Probleme mit Großrechneranwendungen, war aber selbst nicht ohne Nachteile. Ein Beispiel: Da sich die Funktionalität für Datenbankzugriffe (wie z.B. eingebettete Datenbankabfragen) und die Geschäftslogik oft in der Client-Komponente befanden, war bei Änderungen an der Geschäftslogik, dem Datenbankzugriff oder sogar an der Datenbank selbst nicht selten der Einsatz einer neuen ClientKomponente für alle Benutzer der Anwendung notwendig. In der Regel führten diese Änderungen dazu, daß frühere Versionen der Client-Komponente auseinandergerissen wurden, was zu einer instabilen Anwendung führte.
Diese Probleme in einer traditionellen Client-Server-Architektur (nun oft als »zweischichtige« ClientServer-Architektur bezeichnet) wurden von der mehrschichtigen Client-Server-Architektur behoben. Von der Konzeption her kann eine Anwendung eine beliebige Anzahl von Schichten aufweisen, die beliebteste Architektur ist jedoch die mit drei Schichten. Dabei wird das System in drei logische Schichten unterteilt: die Benutzeroberflächenschicht, die Geschäftslogikschicht und die Datenbankzugriffsschicht. Eine dreischichtige Client-Server-Architektur ist in Abbildung 1.3 gezeigt. FONT>
Abbildung 1.3: Dreischichtige Client-Server-Architektur Die mehrschichtige Client-Server-Architektur stellt in zweierlei Hinsicht eine Verbesserung der zweischichtigen Client-Server-Architektur dar: Einerseits (und dies ist vielleicht am wichtigsten) ist die Anwendung durch diese Architektur weniger instabil, weil der Client von Änderungen im Rest der Anwendung stärker isoliert ist. Außerdem ist beim Einsatz der Anwendungen mehr Flexibilität
möglich, weil die ausführbaren Komponenten kleiner sind. Der Einsatz mehrschichtiger Client-Server-Architekturen verringert die Instabilität der Anwendungen, weil die einzelnen Schichten stärker voneinander isoliert und getrennt sind. Die Schicht für die Benutzeroberfläche steht nur mit der Schicht für Geschäftsregeln in Verbindung, niemals direkt mit der Schicht für den Datenbankzugriff. Die Schicht für die Geschäftsregeln wiederum steht mit der Schicht für die Benutzeroberfläche auf der einen Seite und mit der Schicht für den Datenbankzugriff auf der anderen Seite in Verbindung. Somit betreffen Änderungen an der Schicht für den Datenbankzugriff die Schicht für die Benutzeroberfläche nicht, weil beide voneinander isoliert sind. Mit dieser Architektur ist es weniger wahrscheinlich, daß Änderungen an der Anwendung Auswirkungen auf die Client-Komponente haben (die, wie bereits erwähnt, nach Änderungen neu verteilt werden muß). Da bei einer mehrschichtigen Client-Server-Architektur die Anwendung in mehr Komponenten unterteilt wird als beim herkömmlichen zweischichtigen Modell, ist mehr Flexibilität beim Einsatz der Anwendung möglich. So zeigt Abbildung 1.3 ein System, in dem sich die Schichten für die Geschäftsregeln und für den Datenbankzugriff auf demselben Server-Computer befinden, obwohl es sich um getrennte logische Einheiten handelt. Es ist auch möglich, jede Server-Komponente auf einem separaten Computer unterzubringen. Tatsächlich können mehrere Geschäftslogikkomponenten (und mehrere Datenbankzugriffskomponenten, wenn mehrere Datenbanken benutzt werden) für eine einzelne Anwendung entwickelt werden, wodurch der Verarbeitungsaufwand verteilt wird und eine robustere, skalierbare Anwendung entsteht. Es ist interessant festzustellen, daß die mehrschichtige Client-ServerArchitektur ihre Wurzeln tatsächlich in Großrechneranwendungen haben könnte. Cobol-Anwendungen auf IBM-Großrechnern konnten die Benutzeroberfläche mit Hilfe eines Werkzeugs namens MFS (Message Format Service, Nachrichtenformatdienst) definieren. Durch MFS wurde der Terminaltyp (Terminals konnten z.B. eine unterschiedliche Anzahl von Zeilen und Spalten anzeigen) von der restlichen Anwendung getrennt. Auf gleiche Weise konnten Anwendungen auch die Datenbankschnittstellen definieren. Obwohl die Anwendung immer noch in einem monolithischen Block lief, ermöglichten die verfügbaren Werkzeuge den Entwurf von Anwendungen mit Hilfe einer logischen dreischichtigen Architektur.
Die nächste Generation: verteilte Systeme Der nächste logische Schritt in der Evolution von Anwendungsarchitekturen ist das Modell der verteilten Systeme. Bei dieser Architektur wird das Konzept der mehrschichtigen Client-ServerArchitektur natürlich weitergeführt. Statt zwischen Geschäftslogik und Datenbankzugriff zu unterscheiden, wird beim Modell der verteilten Systeme einfach die gesamte Funktionalität der
Anwendung in Form von Objekten dargestellt, wobei jedes von ihnen die von den übrigen Objekten im System oder sogar von Objekten in anderen Systemen bereitgestellten Dienste nutzen kann. Mit dieser Architektur wird auch die Unterscheidung zwischen »Client« und »Server« verwischt, weil die Client-Komponenten auch Objekte erzeugen können, die sich wie Server verhalten. Mit der Architektur der verteilten Systeme wird eine absolute Flexibilität zur Verfügung gestellt. Die Architektur der verteilten Systeme erhält ihre Flexibilität dadurch, daß sie die Definition spezieller Komponentenschnittstellen fördert (oder erzwingt). Die Schnittstelle einer Komponente gibt den übrigen Komponenten an, welche Dienste die Komponente anbietet und wie diese genutzt werden. Solange die Schnittstelle einer Komponente konstant bleibt, kann sich die Implementierung der Komponente grundlegend ändern, ohne daß dies Auswirkungen auf die übrigen Komponenten hat. Beispiel: Eine Komponente, die Kundendaten für eine Firma zur Verfügung stellt, speichert diese in einer relationalen Datenbank. Später beschließen die Anwendungsentwickler, daß eine objektorientierte Datenbank für diesen Zweck besser geeignet wäre. Sie können beliebige Änderungen an der Implementierung der Komponente vornehmen, selbst so grundlegende Änderungen wie die Verwendung eines anderen Datenbanktyps, vorausgesetzt, daß dabei die Schnittstelle der Komponente intakt bleibt. Auch hier gilt, daß die zugrundeliegende Implementierung der Komponente beliebig geändert werden kann, solange ihre Schnittstelle unverändert bleibt. Eine Schnittstelle dient zur Definition des Kommunikationsprotokolls zwischen zwei getrennten Komponenten eines Systems. (Bei diesen Komponenten kann es sich um getrennte Prozesse, um getrennte Objekte oder um einen Benutzer und eine Anwendung handeln, also um beliebige getrennte Einheiten, die miteinander kommunizieren müssen.) Die Schnittstelle beschreibt, welche Dienste von einer Komponente zur Verfügung gestellt werden und mit welchem Protokoll diese Dienste genutzt werden können. Bei einem Objekt kann man sich die Schnittstelle als die Gruppe von Methoden vorstellen, die von diesem Objekt definiert sind, einschließlich der Eingabe- und Ausgabeparameter. Man kann sich eine Schnittstelle wie einen Vertrag vorstellen, in dem die Komponente, die eine Schnittstelle zur Verfügung stellt, zusichert, Dienstanforderungen so zu berücksichtigen, wie dies in der Schnittstelle festgelegt ist.
Bei verteilten Systemen handelt es sich in Wirklichkeit um mehrschichtige Client-Server-Systeme, in denen die Anzahl der unterschiedlichen Clients und Server potentiell sehr hoch ist. Ein wichtiger Unterschied besteht darin, daß verteilte Systeme in der Regel zusätzliche Dienste zur Verfügung stellen, wie beispielsweise Verzeichnisdienste, die es ermöglichen, daß einzelne Komponenten der Anwendung von anderen gefunden werden können. Andere Dienste könnten z.B. einen Transaktionsmonitordienst umfassen, der es Komponenten ermöglicht, mit anderen Komponenten in Transaktion zu treten.
Unter Verzeichnisdiensten versteht man eine Gruppe von Diensten, die es Objekten, beispielsweise Servern, Geschäften oder sogar Personen, ermöglicht, von anderen Objekten gefunden zu werden. Es ist nicht nur möglich, daß die zu suchenden Objekte verschiedenen Typs sind, auch die Verzeichnisinformationen selbst können sich unterscheiden. So würde man ein Telefonbuch dazu verwenden, um Telefonnummern und Adressen herauszufinden, ein E-Mail-Verzeichnis zum Suchen von EMail-Adressen. Verzeichnisdienste umfassen alle diese Informationen; in der Regel werden zusammengehörige Informationen gruppiert (so gibt es verschiedene Bände der Gelben Seiten für verschiedene Städte; der Inhalt jedes Bandes ist in verschiedene Branchen unterteilt).
Ein Transaktionsmonitordienst überwacht Transaktionen für andere Objekte. Bei einer Transaktion handelt es sich um eine Operation oder eine Gruppe von Operationen, die bis ins Letzte durchgeführt werden müssen. Dies bedeutet, daß entweder alle an der Transaktion beteiligten Objekte die Transaktion erfolgreich durchführen müssen (ihre eigenen Datensätze aktualisieren), oder alle beteiligten Objekte müssen die Transaktion abbrechen (in den Zustand vor Einleitung der Transaktion zurückkehren). Das Ergebnis ist, daß sich alle beteiligten Objekte, unabhängig davon, ob die Operation erfolgreich durchgeführt oder abgebrochen wurde, in einem konsistenten Zustand befinden. Ein Transaktionsmonitor stellt transaktionsrelevante Dienste für andere Objekte zur Verfügung.
Zusammenfassend könnte man sagen, daß Geschäftsanwendungen sich mit der Zeit von einer relativ starren, monolithischen hin zu einer extrem flexiblen, verteilten Architektur entwickelt haben. Auf dem Weg dorthin bieten die Anwendungsarchitekturen in zunehmendem Maße Stabilität aufgrund der Definitionen der Schnittstellen zwischen Komponenten und der Skalierbarkeit der Anwendungen (die teilweise durch die Fähigkeit geliefert wurde, Server-Komponenten auf verschiedenen Computern zu vervielfältigen). Darüber hinaus wurden Dienste eingeführt, die es dem Endbenutzer einer Anwendung ermöglichen, die unüberschaubare Menge von verfügbaren Diensten sinnvoll zu nutzen. Diejenigen, die seit den Tagen der Großrechner mit dem Entwurf und der Entwicklung von Geschäftsanwendungen beschäftigt sind, haben sicher schon viel Interessantes gesehen.
Welche Rolle spielt hier CORBA? Bisher war bei der Darstellung der Entwicklung von Geschäftsanwendungen von der monolithischen Großrechnerarchitektur bis hin zur stark dezentralisierten, verteilten Architektur nirgends die Rede von CORBA. Deshalb werden Sie sich wahrscheinlich jetzt fragen, welche Rolle CORBA bei all diesem spielt. Die Antwort finden Sie im weiteren Verlauf dieses Buches. Wie bereits erwähnt, basieren verteilte Systeme auf der Definition von Schnittstellen zwischen Komponenten und auf dem
Vorhandensein verschiedener Dienste (wie Verzeichnisregistrierung und -suche), die für eine Anwendung zur Verfügung stehen. CORBA stellt einen Standardmechanismus zum Definieren der Schnittstellen zwischen Komponenten sowie einige Werkzeuge zur Verfügung, die das Implementieren dieser Schnittstellen in den vom Programmierer gewählten Sprachen erleichtern. Zusätzlich definiert die Object Management Group (dies ist die Organisation, die für die Standardisierung und Verbreitung von CORBA zuständig ist) eine Fülle von Standarddiensten wie Verzeichnis- und Bezeichnungsdienste (Directory- und Naming-Services), Objektpersistenzdienste (Persistent Object Services) und Transaktionsdienste (Transaction Services). Jeder dieser Dienste ist CORBA-kompatibel definiert, so daß sie für alle CORBA-Anwendungen zur Verfügung stehen. Schließlich stellt CORBA noch die »Leitungen« zur Verfügung, die es den verschiedenen Komponenten einer Anwendung oder verschiedener Anwendungen ermöglicht, miteinander zu kommunizieren. Damit sind aber die Fähigkeiten von CORBA noch nicht erschöpft. Zwei Merkmale von CORBA, die in der Computersoftware-Branche Seltenheitswert haben, sind Plattformunabhängigkeit und Sprachenunabhängigkeit. Plattformunabhängigkeit bedeutet, daß die CORBA-Objekte auf jeder Plattform verwendet werden können, für die eine CORBA ORB-Implementierung vorhanden ist (hierzu gehören praktisch alle modernen Betriebssysteme sowie einige nicht mehr ganz so neue). Sprachenunabhängigkeit bedeutet, daß CORBA-Objekte und Clients in quasi jeder Programmiersprache implementiert werden können. Ferner ist es für CORBA-Objekte nicht relevant, in welcher Sprache andere CORBA-Objekte implementiert wurden, mit denen sie kommunizieren. Sie werden bald die Komponenten der CORBAArchitektur zu sehen bekommen, die Plattformunabhängigkeit und Sprachenunabhängigkeit möglich machen.
Die Alternativen zu CORBA Beim Entwerfen und Implementieren verteilter Anwendungen ist CORBA sicherlich nicht die einzige Möglichkeit für den Entwickler. Es gibt auch noch andere Mechanismen, mit deren Hilfe solche Anwendungen entwikkelt werden können. In Abhängigkeit von der Art der Anwendung, angefangen bei ihrer eigenen Komplexität über die Plattform(en), auf denen sie läuft, bis hin zu der bzw. den zum Implementieren verwendeten Sprache(n), gilt es für jeden Entwickler, einige Alternativen zu bedenken. In diesem Abschnitt werde ich Ihnen einige dieser Alternativen vorstellen und sie mit CORBA vergleichen. Die Socket-Programmierung In den meisten neueren Systemen erfolgt die Kommunikation zwischen den Rechnern und manchmal auch zwischen den Prozessen auf demselben Rechner über Sockets. Einfach gesagt, ein Socket ist ein Kanal, über den Anwendungen miteinander verbunden werden und kommunizieren. Das einfachste Verfahren zur Kommunikation zwischen Anwendungskomponenten ist die direkte Verwendung von
Sockets (dies wird als Socket-Programmierung bezeichnet), was bedeutet, daß der Entwickler Daten auf das Socket schreibt und/oder daraus liest. Die Anwendungsprogrammierschnittstelle (API) für die Socket-Programmierung arbeitet recht hardwarenah. Daraus resultiert, daß der Systemaufwand, der mit einer auf diese Weise kommunizierenden Anwendung verbunden ist, kaum ins Gewicht fällt. Da die API jedoch auf einer solch niedrigen Ebene angesiedelt ist, ist die Socket-Programmierung nicht besonders gut zur Bearbeitung komplexer Datentypen geeignet, besonders wenn die Anwendungskomponenten sich auf unterschiedlichen Rechnertypen befinden oder in unterschiedlichen Programmiersprachen implementiert wurden. Obwohl die direkte Socket-Programmierung sehr effektive Anwendungen ergeben kann, ist dieser Ansatz allerdings für komplexe Anwendungen in der Regel ungeeignet. Remote-Prozedurenaufruf (RPC) Eine Stufe höher auf der Leiter, oberhalb der Socket-Programmierung, befindet sich der RemoteProzedurenaufruf (Remote Procedure Call, RPC). RPC bietet eine funktionsorientierte Schnittstelle zur Kommunikation auf Socket-Ebene. Anstatt die zum und vom Socket strömenden Daten direkt zu bearbeiten, definiert der Entwickler mit RPC eine Funktion, wie in einer funktionalen Sprache wie C, und generiert Code, der dafür sorgt, daß die Funktion für die aufrufende Anwendung wie eine normale Funktion aussieht. Bei Licht betrachtet, stellt man fest, daß die Funktion tatsächlich Sockets verwendet, um mit einem Remote-Server (einem Remote-Server) zu kommunizieren, der die Funktion ausführt und das Ergebnis zurückgibt, wobei wiederum Sockets verwendet werden. Da RPC eine funktionsorientierte Schnittstelle bietet, ist dieses Verfahren oft einfacher zu verwenden als die reine Socket-Programmierung. RPC ist außerdem leistungsfähig genug, um als Basis für viele Client-Server-Anwendungen zu dienen. Obwohl es unterschiedliche, nicht miteinander kompatible Implementierungen des RPC-Protokolls gibt, steht ein Standard-RPC-Protokoll zur Verfügung, das auf den meisten Plattformen ohne Änderungen eingesetzt werden kann. Distributed Computing Environment (DCE) von OSF Die DCE (Distributed Computing Environment, verteilte Rechnerumgebung) umfaßt eine Reihe von Standards, für die die Open Software Foundation (OSF) die Pionierarbeit geleistet hat, und enthält einen Standard für RPC. Obwohl es den DCE-Standard bereits eine ganze Weile gibt und er sinnvoll ist, hat er nie breite Anerkennung erfahren und ist heute nicht viel mehr als ein Kuriosum aus vergangenen Tagen. Distributed Component Object Model (DCOM) von Microsoft Das DCOM (Distributed Component Object Model, Objektmodell für verteilte Komponenten) stellt Microsofts Einstieg in den Bereich der verteilten Rechnersysteme dar und bietet Funktionen, die denen von CORBA entsprechen. DCOM ist ein relativ robustes Objektmodell, das von den Betriebssystemen von Microsoft besonders gut unterstützt wird, weil es in Windows 95 und Windows NT integriert ist. Da es sich um eine Microsoft-Technologie handelt, ist DCOM außerhalb der Windows-Welt kaum anzutreffen. Microsoft arbeitet jedoch daran, dies abzustellen, indem es eine
Partnerschaft mit der Software AG eingegangen ist, um DCOM auch auf anderen Plattformen als nur Windows verfügbar zu machen. Bei Entstehung dieses Buchs war DCOM für das Betriebssystem Solaris von Sun verfügbar, Unterstützung wurde für Unix von Digital, MVS von IBM und andere Betriebssysteme für Ende des Jahres angekündigt. Wenn Sie dieses Buch in den Händen halten, werden einige oder alle dieser Portierungen zur Verfügung stehen. (Weitere Informationen über die Portierung von DCOM auf andere Plattformen können Sie unter http://www.softwareag.com/corporat/dcom/default.htm abrufen.) Microsoft hat bei zahlreichen Gelegenheiten deutlich gemacht, daß DCOM auf den WindowsBetriebssystemen am besten unterstützt wird. Daher ist jeder Entwickler, der plattformübergreifende Anwendungen im Auge hat, gut beraten, wenn er die Möglichkeiten von DCOM auf der bzw. den betreffenden Plattform(en) prüft, bevor er sich für den Einsatz dieser Technologie entscheidet. Für die Entwicklung reiner Windows-Anwendungen jedoch kann man sich kaum eine Grundstruktur für verteilte Rechnersysteme vorstellen, die besser in das Betriebssystem Windows integriert werden kann. Eine interessante Entwicklung bezüglich CORBA und DCOM betrifft die Verfügbarkeit von CORBADCOM-Bridges, durch die es CORBA-Objekten ermöglicht wird, mit DCOM-Objekten zu kommunizieren und umgekehrt. Aufgrund der »verschiedenen Wellenlängen« von CORBA- und DCOM-Objekten (es gibt inhärente, schwer zu überwindende Inkompatibilitäten zwischen den beiden) ist die CORBA-DCOM-Bridge keine Ideallösung, kann sich aber in Situationen, in denen sowohl DCOM- als auch CORBA-Objekte verwendet werden sollen, als nützlich erweisen. Remote Methode Invocation (RMI) im Java-Standard Die Übersicht über die Alternativen zu CORBA endet bei RMI (Remote Methode Invocation, FernMethodenaufruf) im Java-Standard, einer CORBA sehr ähnlichen Architektur mit einigen Eigenheiten. Ein Vorteil von RMI besteht darin, daß dieses Verfahren die Weitergabe eines Objekts durch seinen Wert unterstützt, eine Funktion, die (derzeit) von CORBA nicht unterstützt wird. Nachteil ist jedoch, daß es sich bei RMI um eine Java-spezifische Lösung handelt, d.h., RMI-Clients und -Server müssen in Java geschrieben sein. Für alle Java-Anwendungen, insbesondere diejenigen, die die Funktion zur Weitergabe von Objekten durch Werte nutzen, ist RMI eine gute Wahl, aber wenn die Wahrscheinlichkeit besteht, daß die Anwendung später mit Anwendungen zusammenarbeiten muß, die in anderen Sprachen geschrieben wurden, ist CORBA die bessere Alternative. Zum Glück gibt es für Java bereits komplette CORBA-Implementierungen, die sicherstellen, daß Java-Anwendungen mit der übrigen CORBA-Welt zusammenarbeiten können. Die Geschichte von CORBA Jetzt wissen Sie schon einiges über den Hintergrund von CORBA und die Gründe, warum es entwickelt wurde. Nun scheint es mir an der Zeit, kurz die Entstehungsgeschichte von CORBA darzulegen. Die Object Management Group (OMG)
Die Object Management Group (OMG) wurde 1989 mit ursprünglich acht Mitgliedern gegründet und hat heute mehr als 760 Mitglieder, deren erklärtes Ziel es ist, »ein gemeinsames architekturbezogenes Gerüst für objektorientierte Anwendungen zur Verfügung zu stellen, das auf weit verbreiteten Schnittstellenspezifikationen basiert«. Dies ist ein hohes Ziel, aber die OMG erreicht es mit der Einführung der OMA (Object Management Architecture, Objektmanagement-Architektur) , zu deren Bestandteilen CORBA gehört. Diese Standards liefern das architekturbezogene Gerüst, anhand dessen Anwendungen entwickelt werden. Sehr kurz gesagt, besteht die OMA aus der Funktion ORB (Object Request Broker, Objektanfragenvermittler), Objektdiensten (diese werden als CORBAservices bezeichnet), gemeinsamen Einrichtungen (CORBAfacilities), Domänenschnittstellen und Anwendungsobjekten. Die Funktion von CORBA innerhalb der OMA besteht darin, die ORBFunktion zu implementieren. Der größte Teil dieses Buches wird sich mit CORBA selbst befassen und CORBAservices sowie CORBAfacilities nur gelegentlich streifen.
CORBA 1.0 Nach der Gründung der OMG im Jahre 1989 wurde CORBA 1.0 im Dezember 1990 eingeführt und in Kraft gesetzt. Anfang 1991 folgte CORBA 1.1. In dieser Version waren die Interface Definition Language (IDL) und die API für Anwendungen zur Kommunikation mit einem Object Request Broker (ORB) definiert. (Diese Begriffe werden Sie ausführlich am 2. Tag kennenlernen.) Eine überarbeitete Version 1.2 erschien kurz vor CORBA 2.0. Diese Version mit ihren zusätzlichen Funktionen ließ die 1.x-Versionen schnell verblassen. Durch die 1.x-Versionen von CORBA wurde ein erster wichtiger Schritt in Richtung auf Objektinteroperabilität gemacht, denn sie ermöglichten es Objekten auf unterschiedlichen Rechnern, die mit Hilfe von unterschiedlichen Architekturen und in unterschiedlichen Sprachen geschrieben waren, miteinander zu kommunizieren. CORBA 2.0 und IIOP CORBA 1.x stellte den ersten wichtigen Schritt in Richtung Objektkompatibilität dar, aber es handelte sich nicht um eine vollständige Spezifikation. Obwohl darin Standards für die IDL und den Zugriff auf einen ORB über eine Anwendung enthalten waren, bestand die wichtigste Einschränkung darin, daß kein Standardprotokoll definiert war, über das die ORBs miteinander kommunizieren konnten. Ergebnis: Ein CORBA-ORB eines Anbieters konnte nicht mit dem CORBA-ORB eines anderen Anbieters kommunizieren. Diese Einschränkung beeinträchtigte die Kompatibilität zwischen den verteilten Objekten in erheblichem Maße. Dann kam der Auftritt von CORBA 2.0. Nach der Inkraftsetzung im Dezember 1994 war die erste Errungenschaft von CORBA 2.0 die Definition eines Standardprotokolls, über das die ORBs der verschiedenen CORBA-Anbieter miteinander kommunizieren konnten. Dieses Protokoll heißt Internet Inter-ORB Protocol (IIOP) und muß von allen Anbietern implementiert werden, die ihre Produkte als CORBA 2.0-kompatibel bezeichnen wollen. Die wichtigste Funktion des IIOP besteht darin, eine echte Interoperabilität zwischen den Produkten der zahlreichen Anbieter herzustellen, wodurch die CORBA-Anwendungen anbieterunabhängiger werden konnten. Da es sich beim IIOP um das Internet Inter-ORB Protocol handelt, ist dies nur auf Netzwerke anwendbar, die auf TCP/IP basieren; hierzu gehören das Internet sowie die meisten Intranets.
Der CORBA-Standard wurde auch über Version 2.0 hinaus weiterentwikkelt. Im September 1997 wurde die Version 2.1 fertig, kurz darauf folgte 2.2. Version 2.3 wird für Anfang 1998 erwartet. (Die OMG weiß sich selbst zu beschäftigen!) Mit diesen überarbeiteten Versionen wurden bzw. werden evolutionäre (aber keineswegs revolutionäre) Weiterentwicklungen der CORBA-Architektur eingeführt.
Die CORBA-Architektur im Überblick Jetzt haben Sie Einzelheiten zur Geschichte und zu den Gründen für die Entwicklung von CORBA gelernt. Nun werden wir uns mit der Architektur von CORBA beschäftigen. Dieses Thema wird am 2. Tag noch ausführlicher behandelt, aber ich möchte Ihnen am 1. Tag eine ganz allgemeine Zusammenfassung über den Aufbau der Architektur von CORBA geben. Zunächst sei gesagt, daß es sich bei CORBA um eine objektorientierte Architektur handelt . CORBAObjekte weisen viele Merkmale auf, die auch in anderen objektorientierten Systemen zu finden sind, hierzu gehören Vererbung und Polymorphie. Was CORBA noch interessanter macht, ist die Tatsache, daß diese Merkmale auch zur Verfügung stehen, wenn eine nicht objektorientierte Sprache wie C oder Cobol verwendet wird. Allerdings wird CORBA besonders gut durchobjektorientierte Sprachen wie C++ und Java abgebildet. Unter Schnittstellenvererbung versteht man ein Konzept, das Entwicklern bekannt sein müßte, die objektorientiert in C oder Java arbeiten. Den Gegensatz dazu bildet die Implementierungsvererbung , bei der eine Implementierungseinheit (in der Regel eine Klasse) von einer anderen abgeleitet werden kann. Im Vergleich dazu kann bei der Schnittstellenvererbung eine Schnittstelle von einer anderen abgeleitet werden. Obwohl Schnittstellen durch Vererbung miteinander in Beziehung gesetzt werden können, braucht dies bei den Implementierungen dieser Schnittstellen nicht der Fall zu sein.
Der Object Request Broker (ORB) Einen grundlegenden Bestandteil der Common Object Request Broker Architecture bildet der Object Request Broker oder kurz ORB. (Daß das Akronym ORB in CORBA enthalten ist, konnte gar kein Zufall sein.) Unter einem ORB versteht man eine Software-Komponente, die die Kommunikation zwischen Objekten erleichtern soll. Hierzu werden eine Reihe von Leistungsmerkmalen bereitgestellt, eine davon besteht darin, ein Remote-Objekt aufzufinden, sofern eine Objektreferenz vorhanden ist. Ein anderer vom ORB zur Verfügung gestellter Dienst besteht im Übertragen des Formats von Parametern und Rückgabewerten (dies wird auch als »Marshaling« bezeichnet), die bei RemoteMethodenaufrufen gesendet bzw. empfangen werden. (Es ist klar, daß Sie diese Erläuterung jetzt noch nicht verstehen, ich komme aber am 2. Tag noch eingehend darauf zu sprechen. Jetzt brauchen Sie sich bloß zu merken, daß die Object Management Architecture (OMA ) die ORB-Funktionalität vorsieht und daß CORBA der Standard für die Implementierung dieser ORB-Funktionalität ist. Sie
werden bald sehen, daß die Verwendung von ORBs den verteilten CORBA-Objekten Plattformunabhängigkeit beschert. Die Interface Definition Language (IDL) Ein weiterer zentraler Bestandteil der Architektur von CORBA ist die darin verwendete Interface Definition Language (IDL). Die IDL dient zur Definition von Schnittstellen zwischen CORBAObjekten und ist das Instrument, mit dem die Sprachenunabhängigkeit von CORBA sichergestellt wird. Da die mit der IDL beschriebenen Schnittstellen auf jede beliebige Programmiersprache abgebildet werden können, sind die mit CORBA entwickelten Anwendungen und Komponenten unabhängig von der bzw. den Sprache(n), die zur Implementierung verwendet wurde(n). Anders gesagt, ein in C++ geschriebener Client kann mit einem in Java geschriebenen Server kommunizieren, dieser wiederum kann mit einem anderen Server kommunizieren, der in Cobol geschrieben wurde usw. Ein wichtiger Aspekt bei der IDL ist die Tatsache, daß es sich nicht um eine Implementierungssprache handelt. Dies bedeutet, daß Sie in dieser Sprache keine Anwendungen schreiben können. Die IDL hat nur den Zweck, Schnittstellen zu definieren, die Implementierungen für diese Schnittstellen erfolgen über eine andere Sprache. Am 3. Tag werden Sie die IDL näher kennenlernen und dabei über dieses und andere Themen Näheres erfahren. Das Kommunikationsmodell von CORBA In CORBA werden »Objektreferenzen« verwendet (diese werden im CORBA/IIOP-Sprachgebrauch als IORs (Interoperable Object References, Interoperable Objektreferenzen) bezeichnet), um die Kommunikation zwischen den Objekten zu erleichtern. Wenn eine Komponente einer Anwendung auf ein CORBA-Objekt zugreifen möchte, erhält es zunächst eine IOR für das betreffende Objekt. Mit Hilfe der IOR kann die Komponente (die als Client des betreffenden Objekts bezeichnet wird) Methoden des Objekts aufrufen (dieses wird hier als Server bezeichnet). In CORBA versteht man unter einem Client einfach eine Anwendung, die die Dienste eines CORBA-Objekts nutzt, d.h. eine Anwendung, die eine oder mehrere Methoden eines anderen Objekts aufruft. Ein Server ist somit eine Anwendung, die CORBA-Objekte erstellt und die Dienste, die von diesen Objekten zur Verfügung gestellt werden, für andere Anwendungen verfügbar macht. Am 2. Tag folgt eine eingehendere Behandlung der CORBA-Clients und -Server.
Wie bereits erwähnt, verwenden CORBA-ORBs für die Kommunikation in der Regel das Internet Inter-ORB Protocol (IIOP). Es gibt auch andere Protokolle für die Kommunikation zwischen ORBs, aber das IIOP ist dabei, in kurzer Zeit zum meistverwendeten Protokoll zu werden. Dies liegt vor allem daran, daß es Standard ist, und zum anderen an der Beliebtheit von TCP/IP (dem vom Internet verwendeten Netzwerkprotokoll), der Schicht, über der sich das IIOP befindet. CORBA ist jedoch unabhängig von Netzwerkprotokollen und könnte (zumindest theoretisch) über jedes beliebige
Netzwerkprotokoll laufen. Es gibt beispielsweise auch Implementierungen von CORBA, die über DCE und nicht über TCP/IP laufen, und es besteht ein Interesse daran, CORBA über ATM und SS7 laufen zu lassen. Das Objektmodell von CORBA In CORBA erfolgt die gesamte Kommunikation zwischen Objekten über Objektreferenzen (hier sei nochmals erwähnt, daß diese auch als IORs (Interoperable Object References, Interoperable Objektreferenzen) bezeichnet werden, wenn Sie das IIOP verwenden). Die Sicht auf diese Objekte ist nur dadurch möglich, daß Referenzen an diese Objekte weitergegeben werden (Variablenparameter, Pass By Reference), die Weitergabe der Objekte kann nicht durch Werte (Wertparameter, Pass By Value) erfolgen (zumindest ist dies in der aktuellen Spezifikation von CORBA der Fall). Anders gesagt, Remote-Objekte bleiben in CORBA entfernt (Remote), derzeit gibt es keine Möglichkeit für ein Objekt, sich selbst an einen anderen Ort zu verschieben oder zu kopieren. (Dieses Thema sowie andere Einschränkungen und Entwurfsthemen von CORBA werden am 10. Tag behandelt.) Ein weiterer Aspekt des Objektmodells von CORBA ist der BOA (Basic Object Adapter, grundlegender Objektadapter); auch diesen Begriff werden Sie am 2. Tag kennenlernen. Ein BOA stellt allgemeine Basisdienste für alle CORBA-Objekte zur Verfügung. CORBA-Clients und -Server Wie auch bei reinen Client-Server-Architekturen werden bei CORBA die Begriffe Client und Server beibehalten. In CORBA kann eine Komponente sowohl als Client als auch als Server fungieren. Im Grunde wird eine Komponente als Server betrachtet, wenn sie CORBA-Objekte enthält, deren Dienste anderen Objekten zur Verfügung stehen. Analog dazu wird eine Komponente als Client betrachtet, wenn sie auf Dienste eines anderen CORBA-Objekts zugreift. Natürlich kann eine Komponente gleichzeitig sowohl verschiedene Dienste zur Verfügung stellen als auch solche nutzen, und daher kann sie, je nach Szenario, als Client oder Server betrachtet werden. Stubs und Skeletons Beim Implementieren von CORBA-Anwendungskomponenten werden Ihnen auch Client-Stubs und Server-Skeletons begegnen. Unter einem Client-Stub versteht man einen kleinen Quelltextteil, der es einem Client ermöglicht, auf eine Server-Komponente zuzugreifen. Dieser Quelltext wird zusammen mit dem Client-Teil der Anwendung kompiliert. Analog versteht man unter Server-Skeletons Quelltextteile, die beim Implementieren eines Servers quasi »ausgefüllt werden«. Sie brauchen die Client-Stubs und Server-Skeletons nicht selbst zu schreiben, denn dieser Quelltext wird beim Kompilieren von IDL-Schnittstellendefinitionen generiert. Auch dies werden Sie bald kennenlernen. Erweiterte Funktionalität: CORBAservices und CORBAfacilities Neben den CORBA-Basisfunktionen, die die Kommunikation der Objekte miteinander ermöglichen, sind auch noch Funktionen wichtig, die von der OMA, zu der CORBA gehört, zusätzlich zur Verfügung gestellt werden und als CORBAservices und CORBAfacilities bekannt sind. Wie Sie noch
feststellen werden, bieten CORBAservices und CORBAfacilities sowohl horizontale (in der Regel für alle Branchen nützliche) als auch vertikale (für spezielle Branchen konzipierte) Dienste und Einrichtungen. Diese Leistungsmerkmale werden am 12. Tag noch eingehend behandelt, danach werden Sie Gelegenheit haben, einen Teil dieser Funktionalität in einer CORBA-Anwendung einzusetzen.
Zusammenfassung Heute haben Sie einen sehr komprimierten Überblick über die Architektur von CORBA erhalten, einen Abriß über die Geschichte der Entwicklung von Geschäftsanwendungen und der Rolle, die CORBA bei all diesem spielt. Sie wissen nun, was Sie von diesem Buch erwarten können: Sie werden nicht über Nacht zum CORBA-Experten, erhalten aber wertvolle Kenntnisse zum Entwurf und zur Entwicklung von CORBA-gestützten Anwendungen. In den nächsten Tagen werden Sie die Architektur von CORBA noch viel eingehender kennenlernen, Sie werden erfahren, was Sie schon immer über IDL wissen wollten, und sind damit schon auf dem Weg zum Entwikkeln von CORBA-Anwendungen.
Fragen & Antworten Frage: Mir ist immer noch nicht richtig klar, warum ich CORBA und nicht eine der anderen Methoden zur Kommunikation zwischen Prozessen verwenden soll. Antwort: Es gibt einige Bereiche, in denen sich CORBA richtig hervortut. Bei Anwendungen, die verschiedene, in unterschiedlichen Sprachen geschriebene Komponenten enthalten und/oder auf verschiedenen Plattformen laufen sollen, ist die Verwendung von CORBA sehr sinnvoll. CORBA übernimmt einige potentiell lästige Details für Sie, so beispielsweise das automatische Konvertieren von Zahlenformaten zwischen verschiedenen Rechnern (dies erfolgt durch Formatübertragung, also dem Marshaling-Vorgang). Zusätzlich bietet CORBA eine leicht verständliche Abstraktion für verteilte Anwendungen, in der objektorientierter Entwurf, ein Exception-Modell und andere nützliche Konzepte enthalten sind. Die Stärken von CORBA kommen jedoch erst bei Anwendungen gut zur Geltung, die in einem gesamten Unternehmen eingesetzt werden. Die vielen Robustheitsmerkmale von CORBA sowie die über die CORBAservices und CORBAfacilities der OMA zur Verfügung gestellten Funktionen und insbesondere die Skalierbarkeit von CORBA machen diesen Standard für Unternehmensanwendungen zum idealen Instrument. Frage: Was versteht man unter IDL, und wofür kann die Sprache eingesetzt werden?
Antwort: Die Sprache IDL (Interface Definition Language, Schnittstellendefinitionssprache) wird in den nächsten beiden Tagen noch eingehender behandelt. Jetzt sei nur so viel verraten: Der Wert von IDL liegt darin, daß sie zum Abstrahieren unterschiedlicher Sprachen, Hardware und Betriebssystemarchitekturen dient. So wird beispielsweise der IDL-Datentyp long automatisch in den Zahlentyp für die Architektur umgesetzt, unter der die Anwendung ausgeführt wird. Außerdem kann die IDL, weil sie sprachenunabhängig ist, zur Definition der Schnittstellen von Objekten dienen, die in einer beliebigen Sprache implementiert sind.
Workshop Der folgende Abschnitt soll Ihnen dabei helfen, Ihr Verständnis des heute vorgestellten Stoffs zu prüfen und das Gelernte in die Praxis umzusetzen. Die Antworten auf die Quizfragen finden Sie in Anhang A. An den meisten Tagen finden Sie hier außerdem noch einige Übungen, die jedoch heute entfallen, weil an diesem Tag noch keine »verwertbaren Kenntnisse« vorgestellt wurden. Quiz 1. 2. 3. 4.
Wofür steht IIOP und was bedeutet es? Welche Beziehungen bestehen zwischen CORBA, OMA und OMG? Was versteht man unter einem Client-Stub? Was ist eine Objektreferenz? Und was ist eine IOR?
Ein Imprint des Markt&Technik Buch- und Software-Verlag GmbH. Elektronische Fassung des Titels: Corba in 14 Tagen, ISBN: 3-8272-2031-9
Tag 2 Die Architektur von CORBA Überblick Der Object Request Broker (ORB) Die Interface Definition Language (IDL) Das Kommunikationsmodell von CORBA Das Objektmodell von CORBA CORBA-Clients und -Server Stubs und Skeletons Erweiterte Funktionalität: CORBAservices und CORBAfacilities Zusammenfassung Fragen & Antworten Workshop
Überblick Am ersten Tag haben Sie einiges über die Geschichte von CORBA gelernt und erfahren, welchen Platz die Architektur von CORBA innerhalb der Welt der Client-Server-Anwendungsentwicklung einnimmt. Sie haben außerdem eine kurze Übersicht über die Architektur von CORBA erhalten. Am Ende dieses Tages werden Sie ein tiefergehendes Verständnis der Architektur von CORBA und ihrer Komponenten gewonnen haben. In diesem Kapitel werden die folgenden Hauptthemen behandelt: ■ ■ ■
Der Object Request Broker (ORB), einer der Eckpfeiler der Architektur von CORBA Die Interface Definition Language (IDL), der zweite Eckpfeiler der Architektur von CORBA Das Kommunikationsmodell von CORBA und der Platz, den CORBA innerhalb der Netzwerkarchitektur
■ ■ ■ ■
einnimmt Das Objektmodell von CORBA einschließlich Objektreferenzen und Basic Object Adapters (BOAs) Die Definition der Clients und Server und ihre Rolle in der Architektur von CORBA Die Verwendung von Client-Stubs und Server-Skeletons beim Erstellen von CORBA-Anwendungen Übersicht über die CORBAservices und CORBAfacilities, über die zusätzliche Funktionalität für CORBAAnwendungen zur Verfügung gestellt wird
Der Object Request Broker (ORB) Wie man sich unschwer vorstellen kann, ist der Object Request Broker (ORB) ein grundlegender Bestandteil der Common Object Request Broker-Architektur. Dem ORB liegt folgendes Konzept zugrunde: Wenn eine Anwendungskomponente einen Dienst nutzen möchte, der von einer anderen Komponente zur Verfügung gestellt wird, muß sie zunächst eine Objektreferenz für das Objekt erlangen, welches den Dienst zur Verfügung stellt. (Wie diese Objektreferenz erlangt wird, ist ein eigenes Thema, das später an die Reihe kommt; momentan wollen wir aber den ORB-Mechanismus untersuchen und gehen einfach einmal davon aus, daß die Objektreferenz bereits zur Verfügung steht.) Nachdem die Objektreferenz erlangt wurde, kann die Komponente Methoden des Objekts aufrufen und damit auf die vom Objekt zur Verfügung gestellten Dienste zugreifen. (Der Entwickler der ClientKomponente weiß zum Zeitpunkt des Kompilierens, welche Methoden eines bestimmten Server-Objekts zur Verfügung stehen.) Die Hauptaufgabe des ORB ist die Auflösung der Anfragen von Objektreferenzen, so daß die Anwendungskomponenten die Konnektivität miteinander herstellen können. (Diese ORB-Konzepte sind in Abbildung 2.1 gezeigt.) Sie werden sehen, daß der ORB noch weitere Aufgaben erfüllt.
Abbildung 2.1: ORB-Auflösung von Objektanfragen Die Formatübertragung (Marshaling)
Nachdem eine Anwendungskomponente eine Referenz auf ein Objekt erhalten hat, dessen Dienste sie nutzen möchte, kann die Komponente Methoden dieses Objekts aufrufen. In der Regel übernehmen diese Methoden Parameter als Eingabe und geben andere Parameter als Ausgabe zurück. Eine weitere Aufgabe des ORB besteht im Empfang der Eingabeparameter von der Komponente, welche die Methode aufruft, und in der Formatübertragung dieser Parameter (dieser Vorgang wird auch als Marshaling bezeichnet). Darunter versteht man eine Umsetzung der Parameter in ein Format, das über das Netzwerk zum Remote-Objekt übertragen werden kann. (Manchmal wird dies auch als »Übertragungsformat« bezeichnet.) Der ORB nimmt auch die umgekehrte Formatübertragung (Unmarshaling) der zurückgegebenen Parameter vor; diese werden also aus dem Übertragungsformat in ein Format umgesetzt, das die aufrufende Komponente versteht. Dieser Prozeß ist in Abbildung 2.2 dargestellt. Formatübertragung ist die Umsetzung der Eingabeparameter in ein Format, das über ein Netzwerk übertragen werden kann.
Die umgekehrte Formatübertragung ist die Rückumsetzung, bei der die Daten aus einem Netzwerk in Ausgabeparameter umgesetzt werden.
Das Übertragungsformat ist das Format, in dem die Daten nach bzw. vor der (umgekehrten) Formatübertragung über das Netzwerk übertragen werden.
Abbildung 2.2: Die Formatübertragung von Parametern und Rückgabewerten Die gesamte Formatübertragung erfolgt ohne jeglichen Eingriff des Programmierers. Die Client-Anwendung ruft einfach die gewünschte Remote-Methode auf, die für den Client so aussieht, als wäre sie eine lokale Methode, und es wird ein Ergebnis zurückgegeben (oder eine Exception ausgelöst), ebenfalls gerade so, als handle es sich um eine lokale Methode. Der gesamte Prozeß, also die Formatübertragung der Eingabeparameter, das Auslösen des Methodenaufrufs auf dem Server und die umgekehrte Formatübertragung der Rückgabeparameter werden automatisch und transparent durch den ORB durchgeführt. Plattformunabhängigkeit Ein Ergebnis der Formatübertragung/umgekehrten Formatübertragung besteht darin, daß die Kommunikation zwischen den Komponenten plattformunabhängig stattfindet, weil die Parameter für die Übertragung in ein plattformunabhängiges Format und beim Empfang wieder in ein plattformspezifisches Format konvertiert werden (das Übertragungsformat ist Bestandteil der CORBA-Spezifikation). Dies bedeutet, daß ein Client, der beispielsweise auf einem Macintosh-System läuft, Methoden auf einem Server aufrufen kann, der auf einem UnixSystem läuft. Zusätzlich zur Unabhängigkeit vom verwendeten Betriebssystem werden auch HardwareUnterschiede (wie die Reihenfolge der Prozessor-Bytes oder die Art der Adressierung) irrelevant, weil der ORB automatisch die notwendigen Umwandlungen vornimmt. Im wesentlichen fängt der ORB die PlattformUnterschiede ab, unabhängig davon, ob es sich um das Betriebssystem, die Art der Adressierung, die Wortlänge usw. handelt. Beachten Sie, daß die Formatübertragung und die umgekehrte Formatübertragung der Parameter vollständig vom ORB übernommen werden, wobei dieser Vorgang sowohl für den Client als auch für den Server vollständig transparent ist. Da der Prozeß vollständig vom ORB durchgeführt wird, braucht sich der Entwickler nicht mit Einzelheiten bezüglich des Mechanismus für die Formatübertragung und die umgekehrte Formatübertragung zu beschäftigen. Der ORB: Zusammenfassung Da das Konzept des ORB eine wesentliche Grundlage für das Verständnis der Architektur von CORBA darstellt, ist es wichtig, daß Sie dieses verstanden haben. Daher fasse ich den Zweck des ORB noch einmal zusammen. Er hat folgende Aufgaben: ■
■
■
■
■
Wenn eine Objektreferenz von einem Client vorliegt, findet der ORB die zugehörige Objektimplementierung (den Server) für den Client. (Denken Sie daran, daß es Aufgabe des Clients ist, zunächst eine Objektreferenz zu erlangen, den hierzu benötigten Prozeß werden Sie später kennenlernen.) Wenn der Server gefunden wurde, stellt der ORB sicher, daß der Server bereit ist, die Anfrage zu empfangen. Der ORB auf der Client-Seite akzeptiert die Parameter der aufgerufenen Methode und führt eine Formatübertragung der Parameter an das Netzwerk durch. Der ORB auf der Server-Seite führt eine umgekehrte Formatübertragung der Parameter aus dem Netzwerk durch und liefert diese an den Server. Für die Rückgabeparameter, sofern vorhanden, wird auf gleiche Weise eine Formatübertragung/umgekehrte Formatübertragung durchgeführt.
Der Hauptvorteil des ORB ist die plattformunabhängige Verarbeitung der Daten. Die Parameter werden, während die Formatübertragung/umgekehrte Formatübertragung durchgeführt wird, für die Übertragung zwischen unterschiedlichen Computerformaten konvertiert.
Die Interface Definition Language (IDL) Wenn man den Object Request Broker als einen der Eckpfeiler der Architektur von CORBA bezeichnen kann, so ist die IDL (Interface Definition Language, Schnittstellendefinitionssprache) der zweite. Wie schon ihr Name sagt, dient die IDL zur Definition von Schnittstellen zwischen einzelnen Anwendungskomponenten. Beachten Sie, daß es sich bei der IDL nicht um eine prozedurenorientierte (prozedurale) Sprache handelt; sie dient einzig und allein zur Definition von Schnittstellen und nicht von Implementierungen. C++-Programmierer können sich IDLDefinitionen analog zu Header-Dateien für Klassen vorstellen, denn eine solche Header-Datei enthält in der Regel keine Implementierung einer Klasse, sondern beschreibt die Schnittstelle der betreffenden Klasse. JavaProgrammierer können sich IDL-Definitionen wie die Definitionen von Java-Schnittstellen vorstellen, auch hier wird nur die Schnittstelle beschrieben, und es wird keine Implementierung zur Verfügung gestellt. Die Interface Definition Language (IDL) ist eine Standardsprache, mit der die von CORBA-Objekten verwendeten Schnittstellen definiert werden. Sie wird am 3. Tag ausführlich beschrieben.
Die IDL-Spezifikation ist dafür verantwortlich, daß die Daten zwischen Anwendungen in unterschiedlichen Sprachen korrekt ausgetauscht werden. Wenn es sich beispielsweise beim IDL-Typ long um eine vorzeichenbehaftete 32 Bit große Ganzzahl handelt, kann diese in den C++-Typ long (je nach Plattform) oder in den Java-Typ int umgewandelt werden. Es obliegt der IDL-Spezifikation und den IDL-Compilern, die diese implementieren, solche Datentypen auf sprachunabhängige Weise zu definieren. Die IDL wird im nächsten Kapitel ausführlich behandelt. Anschließend werden Sie mit der IDL Schnittstellen (was sonst) für die Beispiele definieren, die im Verlauf dieses Buchs verwendet werden. Sprachenunabhängigkeit Die IDL ist Teil der Standard-CORBA-Spezifikation und unabhängig von jeglicher Programmiersprache. Sie stellt die Sprachenunabhängigkeit durch Sprachabbildung (Language Mapping) sicher. Die OMG hat eine Anzahl von Standard-Sprachabbildungen für viele verbreitete Sprachen wie C, C++, Cobol, Java und Smalltalk definiert. Abbildungen für andere Sprachen sind ebenfalls vorhanden, aber entweder sind diese Abbildungen nicht standardgemäß, oder sie werden derzeit gerade von der OMG standardisiert. Unter Sprachabbildung (Language Mapping) versteht man eine Spezifikation, mit deren Hilfe IDL-Konstrukte auf die entsprechenden Konstrukte einer Programmiersprache abgebildet werden. So wird beispielsweise bei der Sprachabbildung für C++ das IDL-Konstrukt interface auf das C++-Konstrukt class »gemappt« (abgebildet).
Sprachenunabhängigkeit ist ein äußerst wichtiges Merkmal der Architektur von CORBA. Da beim Arbeiten mit CORBA keine bestimmte Sprache verwendet werden muß, haben die Anwendungsentwickler die Freiheit, jeweils diejenige Sprache zu wählen, die für die betreffende Anwendung am besten geeignet ist. Diese Freiheit noch einen Schritt weiter zu führen bedeutet, daß die Entwickler auch mehrere unterschiedliche Sprachen für die einzelnen Komponenten ihrer Anwendung wählen können. So ist es beispielsweise möglich, die Client-Komponenten einer Anwendung in Java zu implementieren, wodurch sichergestellt wird, daß die Clients praktisch auf jedem Rechner ausgeführt werden können. Aus Leistungsgründen könnten dann die Server-Komponenten dieser Anwendung in C++ implementiert werden. CORBA ermöglicht die Kommunikation zwischen diesen doch recht unterschiedlichen
Komponenten.
Das Kommunikationsmodell von CORBA Damit Sie CORBA richtig verstehen können, müssen Sie zunächst die Rolle dieser Architektur in einem Netzwerk von Computersystemen verstehen. In der Regel besteht ein solches Netzwerk aus Systemen, die physikalisch miteinander verbunden sind (obwohl uns die Einführung der drahtlosen Netzwerktechnologie dazu zwingen könnte, darüber nachzudenken, ob »physikalisch miteinander verbunden« noch der passende Ausdruck ist). Diese physikalische Ebene stellt das Medium zur Verfügung, über das die Kommunikation abgewickelt wird, unabhängig davon, ob es sich dabei um eine Telefonleitung, ein Glasfaserkabel, eine Satellitenverbindung oder eine Kombination von Netzwerktechnologien handelt. Oberhalb der physikalischen Schicht liegt die Transportschicht, zu der die Protokolle gehören, die für den Transport von Datenpaketen von einem Punkt zum anderen verantwortlich sind. Im Zeitalter des Internet ist TCP/IP (Transmission Control Protocol/Internet Protocol) das wohl am häufigsten verwendete Protokoll. Die meisten Internet-gestützten Anwendungen verwenden TCP/IP zur Kommunikation miteinander, einschließlich der Anwendungen, die auf FTP (File Transfer Protocol), Telnet (einem Protokoll für die Host-Kommunikation) und HTTP (Hypertext Transport Protocol), der Basis für das World Wide Web, basieren. Protokolle zur Kommunikation zwischen ORBs Welche Rolle spielt nun CORBA innerhalb dieses Netzwerkmodells? Es läßt sich feststellen, daß die CORBASpezifikation hinsichtlich der Netzwerkprotokolle neutral ist. Der CORBA-Standard legt das GIOP (General InterORB Protocol, allgemeines Protokoll für die Kommunikation zwischen ORBs) fest, das auf einer hohen Ebene einen Standard für die Kommunikation zwischen verschiedenen CORBA-ORBs und -Komponenten definiert. Wie sein Name vermuten läßt, handelt es sich bei GIOP nur um ein allgemeines Protokoll, und im CORBA-Standard sind auch noch zusätzliche Protokolle definiert, die GIOP dazu bringen, ein bestimmtes Transportprotokoll zu verwenden. So gibt es GIOP-Protokolle für TCP/IP und für DCE (das Distributed Computing EnvironmentProtokoll der Open Software Foundation). Zusätzlich können Hersteller herstellerspezifische Protokolle für die Kommunikation zwischen CORBA-Komponenten definieren und verwenden (dies tun sie auch). Das GIOP (General Inter-ORB Protocol, allgemeines Protokoll für die Kommunikation zwischen ORBs) ist ein Standardprotokoll auf einer hohen Ebene, das zur Kommunikation zwischen ORBs dient. Da es sich bei GIOP um ein allgemeines Protokoll handelt, wird es nicht direkt verwendet, sondern durch ein direkt zu verwendendes spezielles Protokoll ergänzt.
Der Hauptschwerpunkt in diesem Buch wird auf der Behandlung und Verwendung von CORBA mit dem GIOPProtokoll für TCP/IP-Netzwerke liegen, das als das IIOP (Internet Inter-ORB Protocol, Internet-Protokoll für die Kommunikation zwischen ORBs) bezeichnet wird. Gemäß dem Stand von Version 2.0 der CORBA-Spezifikation müssen Hersteller das IIOP-Protokoll implementieren, damit ihre Anwendungen als CORBA-kompatibel bezeichnet werden können (obwohl sie zusätzlich zum IIOP ihre eigenen Protokolle anbieten können). Diese Anforderung hilft, die Kompatibilität zwischen CORBA-Produkten verschiedener Hersteller sicherzustellen, weil jedes mit CORBA 2.0 kompatible Produkt dieselbe Sprache »sprechen« muß. Einige Hersteller sind so weit gegangen, daß sie das IIOP als das native Protokoll ihres Produkts (dies ist das standardmäßig verwendete Protokoll) übernommen haben und kein herstellerspezifisches Protokoll verwenden. Ein ORB kann allerdings eine beliebige Anzahl von Protokollen unterstützen, solange das IIOP unterstützt wird (bei der Kommunikation können
die ORBs aushandeln, welches Protokoll verwendet werden soll). Ferner fügen eine Reihe von Herstellern IIOPkompatible ORBs zu ihren Produkten, von Datenbank-Servern über Werkzeuge zur Anwendungsentwicklung bis hin zu Web-Browsern, hinzu. Wie Sie sehen, ist das IIOP also ein wichtiger Faktor bei der Kompatibilität von CORBA-Anwendungen. Das IIOP (Internet Inter-ORB Protocol, Internet-Protokoll für die Kommunikation zwischen ORBs) ist eine Spezialversion des GIOP. Das IIOP ist das Standardprotokoll für die Kommunikation zwischen ORBs in TCP/IP-Netzwerken. Damit ein ORB als kompatibel zu CORBA 2.0 betrachtet werden kann, muß er das IIOP unterstützen (kann aber auch noch weitere Protokolle unterstützen).
CORBA und das Netzwerkmodell Bei der ganzen Diskussion um die Protokolle für die Kommunikation zwischen ORBs darf natürlich nicht unberücksichtigt bleiben, wo CORBA innerhalb des übrigen Netzwerkmodells ins Spiel kommt. In Abbildung 2.3 sehen Sie die Netzwerkarchitektur einer typischen CORBA-Anwendung. Im wesentlichen werden CORBAAnwendungen auf von GIOP abgeleiteten Protokollen wie dem IIOP aufgesetzt. Diese Protokolle wiederum liegen oberhalb von TCP/IP, DCE oder dem sonst im Netzwerk verwendeten, zugrunde liegenden Transportprotokoll. CORBA-Anwendungen sind nicht darauf beschränkt, nur eines dieser Protokolle zu verwenden. Die Anwendungsarchitektur kann so konzipiert sein, daß eine Bridge beispielsweise zur Verbindung von DCEgestützten Anwendungskomponenten mit IIOP-gestützten Komponenten verwendet wird. Sie sehen, daß die Architektur von CORBA keine Netzwerkprotokolle verdrängt, sondern eine zusätzliche Schicht hinzufügt, nämlich die Protokollschicht für die Kommunikation zwischen ORBs, bei der die darunterliegende Transportschicht als Basis verwendet wird. Auch dies ist ein wichtiger Faktor bei der Interoperabilität zwischen CORBAAnwendungen, da CORBA nicht die Verwendung eines bestimmten Netzwerktransportprotokolls vorschreibt.
Abbildung 2.3: Die Architektur einer verteilten CORBA-Anwendung
Das Objektmodell von CORBA Jede objektorientierte Architektur weist ein Objektmodell auf, in dem beschrieben ist, wie die Objekte im System dargestellt werden. Natürlich gibt es auch für CORBA ein solches Objektmodell, denn es handelt sich ja um eine objektorientierte Architektur.
Da CORBA jedoch eine verteilte Architektur ist, unterscheidet sich das darin definierte Objektmodell wahrscheinlich in einigen Punkten von dem, was die meisten Leser kennen werden (beispielsweise das Objektmodell von C++ oder von Java). Drei der Hauptunterschiede zwischen dem Objektmodell von CORBA und den herkömmlichen Modellen liegen in der »halbtransparenten« Unterstützung der Objektverteilung, der Behandlung von Objektreferenzen und der Verwendung von Objektadaptern, besonders BOAs (Basic Object Adapters, grundlegende Objektadapter). Sie werden diese Konzepte im Folgenden näher kennenlernen. Objektverteilung Für einen CORBA-Client sieht ein Remote-Methodenaufruf genauso aus wie ein lokaler Methodenaufruf. Dies liegt an der Verwendung der Client-Stubs (die ich weiter unten in diesem Kapitel noch eingehend erläutern werde). Daher ist die Tatsache, daß die CORBA-Objekte verteilt sind, für die Benutzer dieser Objekte transparent; die Clients nehmen nicht wahr, daß sie mit Objekten zu tun haben, die im Netzwerk verteilt sind. In der Tat stimmt die vorstehende Aussage fast immer. Da die Verteilung von Objekten eine höhere Fehlerwahrscheinlichkeit (verursacht durch Netzwerkausfälle, Server-Abstürze usw.) mit sich bringt, muß CORBA Ausweichmöglichkeiten bieten, die in solchen Fällen greifen. Dies erfolgt durch Bereitstellung von SystemExceptions, die durch jede Remote-Methode ausgelöst werden können. Exceptions werden in den späteren Kapiteln noch ausführlich erläutert: Am 3. Tag wird gezeigt, wie Exceptions in der IDL deklariert werden, am 7. Tag werden Sie eine Exception-Behandlungsfunktion zu einer Beispielanwendung hinzufügen. Zum jetzigen Zeitpunkt brauchen Sie nur zu wissen, daß alle Operationen in allen CORBA-Objekten implizit eine CORBASystem-Exception auslösen können, die einen Netzwerkfehler, die Nichtverfügbarkeit des Servers oder eine ähnliche Situation signalisiert. So ist mit Ausnahme dieser zusätzlichen, über CORBA-Objektmethoden ausgelösten Exception jede Remote-Methode ansonsten identisch mit ihrem lokalen Gegenstück. Objektreferenzen In einer verteilten Anwendung gibt es zwei Möglichkeiten für eine Anwendungskomponente, um auf ein Objekt in einem anderen Prozeß zuzugreifen. Eines davon wird als »Übergabe durch Referenz« (oder Referenzübergabe) bezeichnet und in Abbildung 2.4 dargestellt. Bei dieser Methode gibt der erste Prozeß (Prozeß A) eine Objektreferenz an den zweiten Prozeß (Prozeß B) weiter. Wenn Prozeß B eine Methode dieses Objekts aufruft, wird diese von Prozeß A ausgeführt, weil dieser Prozeß Eigentümer des Objekts ist. (Das Objekt ist im Speicherund Prozeßraum von Prozeß A enthalten.) Prozeß B hat (über die Objektreferenz) nur eine Sicht auf das Objekt und kann so nur anfordern, daß Prozeß A Methoden für Prozeß B ausführt. Übergabe eines Objekts durch Referenz bedeutet, daß ein Prozeß einem anderen Prozeß die Sicht auf eines seiner Objekte gewährt, während er Eigentümer des betreffenden Objekts bleibt. Wenn ein Objekt durch Referenz weitergegeben wird, bleibt das Objekt selbst »an Ort und Stelle«, während nur eine Objektreferenz weitergegeben wird. Operationen am Objekt, die über die Objektreferenz erfolgen, werden vom Objekt selbst verarbeitet.
Abbildung 2.4: Die Objektübergabe durch Referenz Die zweite Methode zur Übergabe eines Objekts von einer Anwendungskomponente zu einer anderen heißt Übergabe durch Werte. Diese Methode ist in Abbildung 2.5 zu sehen. Bei dieser Methode wird der tatsächliche Status des Objekts (z.B. die Werte für die Elementvariablen) an die anfordernde Komponente weitergegeben (dies erfolgt in der Regel durch einen Prozeß, der als »Serialisierung« bezeichnet wird). Wenn die Methoden des Objekts durch Prozeß B aufgerufen werden, werden diese von Prozeß B und nicht von Prozeß A ausgeführt, bei dem sich das ursprüngliche Objekt befindet. Da hier eine Übergabe durch den Wert erfolgt, ändert sich der Status des ursprünglichen Objekts nicht, nur die Kopie (deren Eigentümer nun Prozeß B ist) wird geändert. Im allgemeinen obliegt es dem Entwickler, den Quelltext so zu schreiben, daß die Objekte serialisiert und entserialisiert werden (obwohl diese Funktionalität in manche Sprachen, wie z.B. Java, integriert ist).
Wenn für ein Objekt eine Übergabe durch den Wert erfolgt, wird der Status des Objekts kopiert und an den Zielprozeß weitergegeben, wo eine neue Kopie des Objekts als Instanz angelegt wird. Operationen an der Kopie des Objekts werden vom kopierten Objekt durchgeführt und nicht vom ursprünglichen Objekt.
Serialisierung bezeichnet die Codierung des Status eines Objekts, beispielsweise einer Datei auf einer Festplatte oder einer Netzwerkverbindung, in einen Stream. Wenn ein Objekt serialisiert wurde, kann es in einen solchen Stream geschrieben und anschließend gelesen und entserialisiert werden, wobei die serialisierten Daten, die den Objektstatus enthalten, in eine Objektinstanz zurückkonvertiert werden.
Abbildung 2.5: Übergabe eines Objekts durch Werte Ein wichtiger Aspekt des Objektmodells von CORBA besteht darin, daß alle Objekte durch Referenz weitergegeben werden. (Während dieses Buch entsteht, hat die OMG eine Aufforderung zur Einreichung von Vorschlägen (RFP, Request for Proposals) eingereicht, um CORBA die Funktionalität zur Übergabe von Objekten durch Werte hinzuzufügen. Daher kann man davon ausgehen, daß diese Funktionalität in naher Zukunft zum CORBA-Standard hinzugefügt werden wird.) Damit die Übergabe von Objekten durch Werte in einer verteilten Anwendung erleichtert wird, muß zusätzlich zur Übergabe des Objektstatus über das Netzwerk auch sichergestellt werden, daß die Komponente, die das Objekt empfängt, Implementierungen für die von dem Objekt unterstützten Methoden aufweist. (Dies ist nicht notwendig, wenn Objekte durch Referenz weitergegeben werden, weil dabei, wie bereits erwähnt, die Methodenaufrufe von der Komponente ausgeführt werden, die Eigentümer des tatsächlichen Objekts ist.) Wenn die Funktionalität zur Übergabe durch Werte für CORBA definiert wird, werden diese Aspekte zur Sprache kommen. Sie als Leser
sollten sich über die Ankündigungen der OMG in Hinblick auf Aktualisierungen im Rahmen dieser Entwicklung auf dem laufenden halten. (Die OMG-Web-Seite, auf der viele Informationen und Spezifikationen zu CORBA zur Verfügung gestellt werden, finden Sie unter http://www.omg.org/. Es gibt einige Aspekte, die bei der Übergabe von Objekten nur durch Referenz zu berücksichtigen sind. Wie Sie jetzt wissen, werden für den Fall, daß Objekte nur durch Referenz weitergegeben werden können, für ein Objekt aufgerufene Methoden immer von der Komponente ausgeführt, die Eigentümer des Objekts ist (d.h., die das Objekt erstellt hat). Ein Objekt kann nicht von einer Anwendungskomponente zu einer anderen importiert werden. (Es ist allerdings möglich, Methoden zu erstellen, mit denen dieses Verhalten simuliert wird, es wird nur derzeit von der CORBA-Architektur selbst nicht zur Verfügung gestellt.) Dies bedeutet auch, daß alle Methodenaufrufe RemoteMethodenaufrufe sind (es sei denn, sowohl das aufrufende Objekt als auch das aufgerufene Objekt gehören derselben Anwendungskomponente). Wenn eine Komponente eine längere Reihe von Methodenaufrufen eines Remote-Objekts durchführt, kommt es offensichtlich zu einem ziemlich großen Systemaufwand für die Kommunikation zwischen den beiden Komponenten. Aus diesem Grund kann es unter Umständen effektiver sein, ein Objekt durch seinen Wert weiterzugeben, so daß die Komponente, die dieses Objekt verwendet, es lokal bearbeiten kann. Am 10. Tag werden Sie diese Problematik im Detail kennenlernen, aber in der Zwischenzeit sollten Sie wissen, daß das Fehlen einer Semantik für die Übergabe durch Werte in CORBA diese Problematik hervorruft. Basic Object Adapter (BOAs) Der CORBA-Standard beschreibt eine Anzahl sogenannter Objektadapter, deren Hauptzweck es ist, die Schnittstelle zwischen einer Objektimplementierung und dem zugehörigen ORB zur Verfügung zu stellen. Die OMG empfiehlt, neue Typen von Objektadaptern nur dann zu erstellen, wenn dies notwendig ist, und stellt drei Gebrauchsmuster für Objektadapter zur Verfügung: den BOA (Basic Object Adapter, grundlegender Objektadapter), mit dem wir uns jetzt beschäftigen werden, den Bibliotheksobjektadapter (Library Object Adapter) und den objektorientierten Datenbankadapter (Object-Oriented Database Adapter). Die beiden letzteren dienen zum Zugriff auf Objekte im dauerhaften Speicher. (In der CORBA-Spezifikation sind diese Objektadapter ausführlich beschrieben.) Wie bereits gesagt, werden wir uns allerdings nur mit dem Basic Object Adapter beschäftigen, der bei weitem am häufigsten verwendet wird. Der BOA stellt für CORBA eine allgemeine Gruppe von Methoden für den Zugriff auf ORB-Funktionen zur Verfügung. Diese Funktionen reichen von der Benutzeridentifikationsüberprüfung über die Objektaktivierung bis hin zur Objektpersistenz. Der BOA ist in der Tat die Schnittstelle des CORBA-Objekts zum ORB. Gemäß der CORBA-Spezifikation muß der BOA in jeder ORB-Implementierung vorhanden sein, und dies schien bei den meisten (wenn nicht allen) verfügbaren CORBAProdukten der Fall zu sein. Strategien zur Server-Aktivierung Ein besonders wichtiges (und nützliches) Merkmal des BOA sind seine Funktionen zum Aktivieren und Deaktivieren von Objekten. Der BOA unterstützt vier verschiedene Aktivierungsstrategien, die angeben, wie die Anwendungskomponenten initialisiert werden sollen. Es handelt sich um die folgenden: ■
■ ■
■
Die Strategie der gemeinsam verwendeten Server, bei der ein einziger Server (in diesem Zusammenhang ist damit ein auf einem Rechner laufender Prozeß gemeint) von mehreren Objekten gemeinsam verwendet wird. Die Strategie der nicht gemeinsam verwendeten Server, bei der ein Server immer nur ein Objekt enthält. Die Strategie der Server-pro-Methode, bei der ein Server automatisch gestartet wird, wenn eine Objektmethode aufgerufen wird. Beim Zurückgeben dieser Methode wird der Server automatisch beendet. Die Strategie der permanenten Server, bei der der Server manuell gestartet wird (durch einen Benutzer, einen Stapelverarbeitungsauftrag, einen System-Daemon oder einen anderen externen Agent).
Eine Strategie zur Server-Aktivierung gibt an, wie der Zugriff auf den betreffenden Server erfolgen soll, ob z.B. ein einziger Server von allen Clients verwendet werden soll oder für jeden Client eine eigene Instanz des Servers gestartet werden soll usw.
Diese Vielzahl von Aktivierungsstrategien ermöglicht es dem Anwendungsentwickler, die Art von Verhalten auszuwählen, die für den betreffenden Server am sinnvollsten ist. So ist es beispielsweise für einen Server, der eine gewisse Zeit zur Initialisierung benötigt, am besten, wenn er als permanenter Server gestartet wird, weil sonst die zum Starten notwendige Initialisierungszeit die Antwortzeit für den Server beeinträchtigen würde. Andererseits könnte ein Server, der auf Anforderung schnell initialisiert wird, mit der Strategie des Server-pro-Methode definiert werden. Ich möchte hier noch ausführen, daß der Ausdruck »permanenter Server« nichts mit dem normalerweise verwendeten Begriff »permanent« zu tun hat, der sich auf die Fähigkeit eines Objekts bezieht, seinen aktuellen Status in einer Art von nichtflüchtiger Speichereinrichtung wie z.B. einer auf Festplatte in einzelnen Dateien gespeicherten Datenbank zu speichern. Ein permanenter Server speichert nicht notwendigerweise seinen aktuellen Status in einem permanenten Speicher (obwohl dies möglich wäre). In diesem Fall bezieht sich der Begriff nur darauf, daß der Server permanent läuft.
CORBA-Clients und -Server In einer herkömmlichen Client-Server-Anwendung stellt der Server die Komponente(n) dar, die anderen Anwendungskomponenten Dienste zur Verfügung stellt. Der Client ist die Komponente, die von einem oder mehreren Servern zur Verfügung gestellte Dienste nutzt. Die Architektur einer CORBA-Anwendung unterscheidet sich hiervon nicht. In der Regel stellen bestimmte Komponenten einer Anwendung Dienste zur Verfügung, die von anderen Komponenten der Anwendung genutzt werden. Es ist daher nicht überraschend, daß die Begriffe »Client« und »Server« diese Komponenten einer CORBA-Anwendung bezeichnen. Wenn man allerdings einen einzelnen Aufruf einer Remote-Methode betrachtet, so können die Rollen von Client und Server vorübergehend vertauscht werden, weil jedes CORBA-Objekt gleichzeitig an mehreren Interaktionen beteiligt sein kann. In einer CORBA-Anwendung wird jede Komponente, die eine Implementierung für ein Objekt zur Verfügung stellt, als Server bezeichnet, zumindest, soweit dieses Objekt betroffen ist. Wenn eine Komponente ein Objekt erstellt und anderen Komponenten die Sicht auf dieses Objekt ermöglicht (also anderen Komponenten die Möglichkeit gibt, Referenzen auf dieses Objekt zu erlangen), fungiert diese Komponente als Server für das Objekt, und jede Anfrage, die von anderen Komponenten für dieses Objekt vorgenommen wird, wird von der Komponente verarbeitet, die das betreffende Objekt erstellt hat. Wenn eine Komponente als CORBA-Server bezeichnet wird, so bedeutet dies, daß sie (als Server) für andere Komponenten (Clients) Methoden eines bestimmten Objekts ausführt. Die Komponente ist gleichzeitig Client und Server Häufig kann eine Anwendungskomponente anderen Anwendungskomponenten Dienste zur Verfügung stellen, während sie selbst auf Dienste anderer Komponenten zugreift. In diesem Fall fungiert die Komponente als Client
einer Komponente und als Server für die übrigen Komponenten (siehe Abbildung 2.6). Es kann sogar vorkommen, daß zwei Komponenten gegenseitig gleichzeitig als Client und Server fungieren. Um diese Situation zu verstehen, sehen Sie sich die folgende Konstellation an (Abbildung 2.7): Die erste Komponente (Komponente A) erhält eine Referenz auf ein Objekt, das von einer zweiten Komponente (Komponente B) erstellt wurde, und ruft eine Methode für das Objekt auf. Hier fungiert Komponente A als Client und Komponente B als Server. Jetzt gehen wir einmal davon aus, daß Komponente A als Parameter für die aufgerufene Methode eine Referenz auf ein Objekt weitergibt, das sie erstellt hat (und damit eine Implementierung für das Objekt zur Verfügung stellt). Gehen wir weiter davon aus, daß Komponente B nun eine Methode für das Objekt aufruft. Für diesen betreffenden Methodenaufruf fungiert Komponente A als Server, während Komponente B der Client ist. Die beiden Komponenten haben ihre grundsätzliche Rolle in der Anwendung nicht geändert, aber sie haben vorübergehend ihre Rollen als Client und als Server vertauscht. Diesem Beispiel können Sie entnehmen, daß in einer CORBAAnwendung die Verwendung der Begriffe »Client« und »Server« vom Kontext der aufgerufenen Methode abhängt sowie von der Komponente, in der sich das Objekt für die Methode befindet.
Abbildung 2.6: Die Komponente als Client und als Server Bei der Benennung von Clients und Servern wäre noch ein letzter Punkt zu berücksichtigen: Obwohl eine Anwendungskomponente sowohl als Client als auch als Server fungieren kann, wird sie dennoch als Client oder Server bezeichnet, nicht als beides. Im obigen Beispiel kann man davon ausgehen, daß Komponente A die meiste Zeit Methoden für Objekte aufruft, die Komponente B gehören. Wie im Beispiel gezeigt, können einige (oder sogar alle) Methodenaufrufe Objektreferenzen an Komponente B weitergeben, die dann durch diese Objektreferenzen wiederum Aufrufe an Komponente A durchführen kann. Obwohl Komponente A für diese Methodenaufrufe als Server fungiert, könnte man diese Komponente mit Fug und Recht als den Client und Komponente B als Server bezeichnen, weil es die Hauptfunktion der Komponente A ist, Dienste zu nutzen, die von Komponente B zur Verfügung gestellt werden, und sie selbst nur Objekte zur Verfügung stellt, die in Methoden von Komponente B als Argumente verwendet werden. Methoden, die auf diese Weise aufgerufen werden, werden als Client-Callback-Methoden (Rückrufmethoden) oder einfach als Callbacks (Rückrufe) bezeichnet. Callbacks sind besonders wichtig, da in CORBA derzeit noch keine Objekte durch Werte weitergegeben werden können. Wenn diese Funktion einmal zur Verfügung steht, werden wahrscheinlich viele Callbacks unnötig. Client-Callback-Methoden oder einfach Callback (Rückruf) ist eine allgemeine Bezeichnung für eine Methode, die von einem Client implementiert und von einem Server aufgerufen wird. Durch einen Rückruf wird ein Client zu einer Art beschränktem Server.
Abbildung 2.7: Client-Callback-Methode
Stubs und Skeletons Nachdem ein Entwickler die Definitionen für die Komponentenschnittstelle mit Hilfe der IDL erzeugt hat, werden die entstandenen IDL-Dateien mit einem IDL-Compiler weiterverarbeitet, der dabei Client-Stubs und ServerSkeletons erzeugt. Diese fungieren als eine Art von »Kitt«, mit dem die sprachenunabhängigen IDLSchnittstellenspezifikationen mit sprachspezifischem Implementierungsquelltext verbunden werden. Client-Stubs für jede Schnittstelle werden zur Einbindung von Clients zur Verfügung gestellt, die diese Schnittstellen nutzen. Der Client-Stub für eine bestimmte Schnittstelle stellt eine Pseudoimplementierung für jede Methode in der Schnittstelle zur Verfügung. Anstatt die Server-Funktionen auszuführen, kommunizieren die Methoden des Client-Stub einfach mit dem ORB, damit für die benötigten Parameter eine Formatübertragung bzw. eine umgekehrte Formatübertragung durchgeführt wird. Ein Client-Stub, der vom IDL-Compiler generiert wird, ist ein kleiner Quelltextabschnitt, der einem Client eine CORBA-Server-Schnittstelle zur Verfügung stellt.
Ein Server-Skeleton wird ebenso vom IDL-Compiler generiert und ist ein Quelltextabschnitt, der das Gerüst darstellt, auf dem der ServerImplementierungsquelltext für eine bestimmte Schnittstelle erzeugt wird.
Auf der anderen Seite stehen die Server-Skeletons, die das Gerüst bilden, auf dem der Server erzeugt wird. Für jede Methode einer Schnittstelle generiert der IDL-Compiler eine leere Methode im Server-Skeleton. Der Entwickler stellt dann für jede dieser Methoden die Implementierung zur Verfügung. In Abbildung 2.8 wird gezeigt, welche Position Client-Stubs und Server-Skeletons in der CORBA-Architektur einnehmen.
Abbildung 2.8: Client-Stubs und Server-Skeletons Das Entwickeln eines CORBA-Clients und -Servers wird am 4. Tag eingehend behandelt. Sie werden dann erfahren, wie der IDL-Compiler verwendet wird, wie mit Hilfe der vom IDL-Compiler generierten Client-Stubs ein CORBA-Client entwickelt wird, und wie ein CORBA-Server entwickelt wird, wobei von den ebenfalls vom IDLCompiler generierten Server-Skeletons ausgegangen wird. Schließlich zeige ich Ihnen, daß CORBA-Clients auch ohne die Verwendung von Client-Stubs entwickelt werden können, wobei die DII (Dynamic Invocation Interface, dynamische Aufrufschnittstelle) verwendet wird. Solche Clients sind nicht statisch mit der Server-Schnittstelle verbunden, sondern können Server-Schnittstellen dynamisch finden und Dienste nutzen, an die beim Entwickeln der Clients noch niemand gedacht hat. (Die Verwendung der DII erhöht allerdings die Komplexität einer ClientAnwendung beträchtlich und sollte daher am besten nicht für zentrale Teile der Anwendung eingesetzt werden.) Die DII ist etwas für Fortgeschrittene, daher werden Sie ihr erst am 11. Tag wieder begegnen.
Erweiterte Funktionalität: CORBAservices und CORBAfacilities Sicherlich ist es möglich, allein mit den Grundlagen von CORBA vieles zu erreichen: Sie können mit Hilfe von IDL Komponentenschnittstellen erzeugen, diese Schnittstellen dann implementieren und Clients entwickeln, die die verfügbaren Dienste nutzen. Die OMA (zur Erinnerung: dies ist die von der Object Management Group konzipierte Globalarchitektur, zu der auch CORBA gehört) stellt mit den CORBAservices und CORBAfacilities viel mehr als nur die grundlegende ORB-Funktionalität zur Verfügung. Zu dieser Funktionalität gehören Dienste für Ereignisverwaltung, Lizenzverwaltung, Objektpersistenz, Bezeichnung, Sicherheit, Transaktionen, Benutzeroberflächenverwaltung, Datenaustausch und noch vieles andere. Die Schnittstellen für diese Funktionen wurden von der OMG standardisiert, was bedeutet, daß ihre Verwendung über Plattformen und Produkte hinweg konsistent erfolgt (oder erfolgen wird). Was noch wichtiger ist: Diese Schnittstellen für CORBAservices und CORBAfacilities wurden in der IDL definiert, was bedeutet, daß diese Dienste und Einrichtungen von Anwendungen genauso verwendet werden können wie andere CORBA-Objekte.
Mit den CORBAservices und CORBAfacilities und deren heutiger und zukünftiger Verwendung werden Sie sich am 12. Tag beschäftigen. Für jetzt genügt es zu wissen, daß ein Unterschied zwischen den von der OMG definierten Diensten und Funktionen und den Diensten und Funktionen besteht, die in den verschiedenen CORBAProdukten verfügbar sind. Bevor Sie einen bestimmten Dienst oder eine Funktion in den Entwurf einer Anwendung einbauen, sollten Sie sicherstellen, daß auch tatsächlich ein Produkt vorhanden ist, mit dem diese Funktion implementiert werden kann. Außerdem ist wichtig, daß in einem Produkt, das als CORBA 2.0-kompatibel bezeichnet werden darf, weder CORBAservices noch CORBAfacilities implementiert sein müssen, nur die Kernfunktionalität von CORBA ist zwingend erforderlich.
Zusammenfassung In diesem Kapitel haben Sie die beiden Eckpfeiler der Architektur von CORBA kennengelernt: den ORB, der die Kommunikation der CORBA-Objekte untereinander sicherstellt, und die IDL, mit der die Schnittstellen der Anwendungskomponenten definiert werden, von denen ausgehend CORBA-Anwendungen entwickelt werden. Sie haben mit dem Objektmodell von CORBA Bekanntschaft gemacht und einiges über die Protokolle zur Kommunikation zwischen ORBs (insbesondere IIOP), über die Verwendung von Objektreferenzen in CORBA und über den BOA gelernt. Sie wissen nun, wie die Begriffe Client und Server in CORBA zu verstehen sind und haben erfahren, daß eine einzige Anwendungskomponente gleichzeitig als Client und als Server fungieren kann. Sie haben außerdem gesehen, wie über IDL-Definitionen Client-Stubs und Server-Skeletons erzeugt werden, die wiederum zur Implementierung von CORBA-Clients und -Servern dienen. Schließlich haben Sie eine Einführung in die CORBAservices und CORBAfacilities erhalten, über die zusätzliche Funktionalität für CORBAAnwendungen zur Verfügung gestellt wird. Nachdem Sie sich nun die Gesamtarchitektur von CORBA erarbeitet haben, werden Sie mit den Grundlagen der IDL fortfahren, wobei Sie zunächst die einfachen Datentypen und dann die komplexeren IDL-Konstrukte kennenlernen werden. Diese IDL-Kenntnisse sind für das Entwerfen und Implementieren von CORBAAnwendungen unerläßlich.
Fragen & Antworten Frage: Warum würde der Bedarf an Callbacks geringer werden, wenn die Funktion zur Übergabe von Objekten durch Werte in CORBA vorhanden wäre? Antwort: In vielen Fällen muß ein Client nur ein einfaches Objekt an die Server-Methode übergeben. Da das Übergeben von Objekten durch Werte nicht möglich ist, muß der Server einen Callback an den Client durchführen, um das Objekt manipulieren zu können (auch wenn er nur die Statusinformationen des Objekts lesen möchte). Wenn ein Objekt durch seinen Wert weitergegeben werden kann, kann der Server eine lokale Kopie des Objekts bearbeiten, wodurch kein Client-Callback mehr notwendig ist. (Natürlich wird es auch Fälle geben, in denen der Client Eigentümer des Objekts bleiben will und der Server einen Callback durchführen muß. In solchen Fällen würde das Callback-Paradigma aufrechterhalten.) Frage: Client-Stubs weisen für meine Anwendung wahrscheinlich zu viele Einschränkungen auf. Muß ich die DII verwenden?
Antwort: Bei der überwältigenden Mehrzahl von Anwendungen, bei denen der Entwickler meint, die Verwendung der DII sei notwendig, sollte er sich dies gut überlegen. Aufgrund der Komplexität und des Systemaufwands, der für die DII erforderlich ist, ist es fast immer besser, ihre Verwendung zu vermeiden. (Nähere Informationen zu den sinnvollen Anwendungsbereichen für die DII finden Sie in Kapitel 11.) Frage: Warum sind die Sprachabbildungen (Language Mappings) ein notwendiger Bestandteil von CORBA? Antwort: Da CORBA-Objektschnittstellen in der IDL definiert werden und diese Sprache unabhängig von allen Implementierungssprachen ist, ist es notwendig, eine Methodik zu definieren, um IDL-Datentypen in die Datentypen der gewählten Implementierungssprache zu konvertieren. Die Sprachabbildung für eine bestimmte Implementierungssprache definiert diese Methodik. Ferner sind die Sprachabbildungen für viele häufig verwendete Sprachen standardisiert, so daß eine Anwendung, die zur Verwendung eines bestimmten CORBAProdukts geschrieben wurde, auch mit einem anderen CORBA-Produkt funktionieren kann, wobei keine oder nur wenige Änderungen erforderlich sind (solange die Anwendung nur die Merkmale der StandardSprachabbildung nutzt).
Workshop Der folgende Abschnitt soll Ihnen dabei helfen, Ihr Verständnis des heute vorgestellten Stoffs zu prüfen und das Gelernte in die Praxis umzusetzen. Die Antworten auf die Quizfragen finden Sie in Anhang A. An den meisten Tagen finden Sie hier außerdem noch einige Übungen, die jedoch heute entfallen, weil an diesem Tag noch keine »verwertbaren Kenntnisse« vorgestellt wurden. Quiz 1. Weiter vorn in diesem Kapitel wurde angesprochen, daß die Fähigkeit, Objekte durch Werte weiterzugeben, sobald sie in CORBA verfügbar sein wird, viele Callbacks überflüssig machen wird. Warum ist dies der Fall? 2. Ein Entwickler einer CORBA-Anwendung möchte zwei Server-Komponenten in die Anwendung integrieren. Die erste Komponente weist eine einzige Methode auf, die einfach die Uhrzeit zurückgibt. Die zweite Komponente führt nach der Initialisierung eine langwierige Berechnung in einer umfangreichen Datenbanktabelle durch, sie enthält eine einzige Methode, die den zuvor berechneten Wert zurückgibt. Welche Strategien für die Server-Aktivierung müßten für die beiden Komponenten verwendet werden und warum? 3. Können Sie sich Nachteile bei der Verwendung von Client-Stubs in einer CORBA-Client-Anwendung vorstellen? (Tip: Welche potentiell nützlichen Funktionen stellt die DII zur Verfügung?) 4. Warum sind Sprachabbildungen ein notwendiger Bestandteil von CORBA?
Ein Imprint des Markt&Technik Buch- und Software-Verlag GmbH. Elektronische Fassung des Titels: Corba in 14 Tagen, ISBN: 3-8272-2031-9
Tag 3 Beherrschen der Sprache Interface Definition Language (IDL) Überblick Grundregeln von IDL Module Einfache Typen Weitere IDL-Konstrukte Container-Typen Der Typ exception Der Typ any Der Pseudotyp TypeCode Zusammenfassung
Fragen & Antworten Workshop
Überblick Sie haben im vorherigen Kapitel die Details der Architektur von CORBA kennengelernt sowie einen Überblick über die verschiedenen CORBA-Anwendungsbestandteile und deren Verwendungszweck erhalten. Ein Hauptbestandteil der CORBA-Architektur ist, wie Sie sehen konnten, die SchnittstellenDefinitionssprache »Interface Definition Language« (IDL). IDL wird zur Beschreibung der Schnittstellen (Interfaces) zwischen CORBA-Objekten verwendet. Sie haben außerdem erfahren, daß IDL bezüglich der Implementierungssprache neutral (implementationsunabhängig) ist; mit anderen Worten können IDL-Schnittstellen in jeder Sprache implementiert werden, für die eine Sprachabbildung (Language Mapping) verfügbar ist, wie zum Beispiel für Java, C, C++ und für zahlreiche andere. Heute werden Sie die verschiedenen Konstrukte in IDL erkunden und deren Verwendung lernen. Sie werden mit einfachen Datentypen, wie Booleschen Typen, Gleitkommatypen, Integer-Typen sowie Zeichen und Zeichenketten beginnen. Sie werden feststellen, daß diese Typen den Datentypen vieler anderer Programmiersprachen sehr ähnlich sind. Anschließend werden zusammengesetzte Typen besprochen (dazu gehört der Aufzählungstyp enum, der Aufzählungstyp struct, der Typ union und der Typ interface), bei denen es sich einfach um Typen handelt, die aus anderen Typen zusammengesetzt werden. Abschließend lernen Sie die erweiterten Typen kennen, wie zum Beispiel Container-Typen (Sequenzen und Arrays), Exceptions und andere. Gegen Ende dieses Kapitels werden Sie über einen praktisch vollständigen Überblick zur IDL verfügen.
Grundregeln von IDL Bevor wir mit den Datentypen und anderen Konstrukten von IDL beginnen, sehen wir uns einige Grundregeln der IDL-Syntax und andere Gesichtspunkte der Sprache IDL an. IDL verfügt insbesondere über Regeln bezüglich der Groß-/Kleinschreibung, Definitionssyntax, Kommentarsyntax und der Verwendung des C-Präprozessors. Groß-/Kleinschreibung In IDL wird bei der Unterscheidung zwischen den einzelnen Bezeichnern (wie zum Beispiel bei den Namen von Schnittstellen und Operationen) die Schreibweise (Groß-/Kleinschreibung) unterschieden. So können Sie sich nicht auf eine Schnittstelle mit der Bezeichnung meinObjekt beziehen, indem Sie meinOBJEKT angeben. Neben der Tatsache, daß bei Bezeichnern die Schreibweise unterschieden wird, erlegt IDL eine weitere Einschränkung auf: Die Namen von Bezeichnern im gleichen Gültigkeitsbereich (beispielsweise zwei Schnittstellen im gleichen Modul oder zwei Operationen in der gleichen Schnittstelle) dürfen sich nicht nur in der Schreibweise unterscheiden. So wäre es zum
Beispiel nicht erlaubt, in einer Schnittstelle meinObjekt gleichzeitig eine Operation eineOperation und eine andere eineOPERATION zu benennen. Module, Schnittstellen und Operationen wurden bislang noch nicht besprochen. Im weiteren Verlauf dieses Kapitels erfahren Sie weitere Details zu diesen Konstrukten. Was bei OMG als Operation bezeichnet wird, könnte Ihnen bereits unter Begriffen wie Methode, Elementfunktion oder gar als Botschaft bekannt sein. Unter welcher Bezeichnung Sie sie auch kennen, eine Operation definiert ein spezielles Verhalten einer Schnittstelle, einschließlich der dazugehörigen Eingabe- und Ausgabeparameter. Die Begriffe Operation und Methode werden in diesem Buch austauschbar verwendet, da sie sich auf genau das gleiche Konzept beziehen.
Definitionssyntax von IDL Alle Definitionen werden in IDL durch ein Semikolon (;) abgeschlossen, ähnlich wie in C, C++ und Java. Für Definitionen, die andere Definitionen beinhalten (wie zum Beispiel Module und Schnittstellen) werden, wiederum wie C, C++ und Java, geschweifte Klammern ({}) verwendet. Wenn eine abschließende geschweifte Klammer am Ende einer Definition erscheint, folgt ihr auch ein Semikolon. Ein Beispiel für diese Syntax finden Sie im Listing 3.2, im Abschnitt »Module« dieses Kapitels. Kommentare in IDL Kommentare werden in IDL gemäß den gleichen Konventionen wie in Java und C++ eingefügt. Es sind sowohl Kommentare im C- als auch im C++-Stil erlaubt, wie in Listing 3.1 gezeigt wird. (Beachten Sie, daß im zweiten Kommentar des Listings eingebettete Kommentarzeichen enthalten sind; dies dient jedoch nur der besseren Veranschaulichung und ist in IDL nicht erlaubt.) Listing 3.1: Kommentare in IDL 1: // Dies ist ein Kommentar im C++-Stil. Alles, was dem Zeichen "//" 2: // folgt, bis hin zum Zeilenende, wird als Teil des Kommentars 3: // betrachtet. 4: /* Dies ist ein Kommentar im C-Stil. Alles, was sich zwischen 5: dem ersten "/*" und dem darauf folgenden "*/" befindet, wird als 6: Teil des Kommentars betrachtet. */ Verwenden des C-Präprozessors IDL geht davon aus, daß für die Verarbeitung von Konstrukten, wie zum Beispiel Makro-Definitionen und bedingte Kompilierung, ein C-Präprozessor vorhanden ist. Falls Ihr IDL-Quelltext diese
Merkmale nicht verwendet, können Sie auch ohne einen C-Präprozessor auskommen. Sie sollten jedoch zur Kenntnis nehmen, daß IDL die Merkmale des C-Präprozessors bei Bedarf verwenden kann. Beim C-Präprozessor, welcher in C- und C++-Compilern sowie einigen Betriebssystemen enthalten ist, handelt es sich um ein Tool, das für die Verwendung dieser Sprachen grundlegend wichtig ist. (Die Sprache Java verwendet keinen Präprozessor.) Bevor ein C- oder C++-Compiler Ihren Quelltext kompiliert, führt er den Präprozessor mit diesem Quelltext aus. Der Präprozessor löst Makros auf, verarbeitet Direktiven wie #ifdef...#endif und #include und führt Ersetzungen von #define-Symbolen durch. Für weitere Informationen zum CPräprozessor lesen Sie ein entsprechendes C- oder C++-Dokument, oder versuchen Sie, falls Sie Zugriff auf ein Unix-System haben, den Befehl man cpp.
Module Das erste Sprachkonstrukt von IDL, das wir untersuchen werden, ist das Modul. Das Konstrukt module wird verwendet, um IDL-Definitionen, die einem gemeinsamen Zweck dienen, zusammen zu gruppieren. Die Verwendung des Konstrukts module ist einfach: In einer Deklaration wird der Name des Moduls festgelegt, und seine Elemente werden in geschweiften Klammern eingebunden. In Listing 3.2 finden Sie ein Beispiel hierzu. Die Gruppierung von ähnlichen Schnittstellen, Konstantenwerten usw. wird in der Regel als Partitionierung (Partitioning) bezeichnet. Dies ist ein üblicher Vorgang beim Entwurf von Systemen (vor allem beim Entwurf komplexer Systeme). Für Partitionen sind auch die Begriffe Modul (was keine Überraschung darstellen sollte) oder Package gebräuchlich (tatsächlich ähnelt das Modul-Konzept von IDL sehr dem Package-Konzept von Java - oder umgekehrt, da IDL zuerst da war).
Listing 3.2: Beispiel für ein Modul 1: 2: 3: 4: 5: 6: 7: 8:
module Bank { interface Kunde { ... }; interface Konto { ... } ...
9: }; Das Beispiel im Listing 3.2 definiert ein Modul mit der Bezeichnung Bank, welches die zwei Schnittstellen Kunde und Konto enthält (das Auslassungszeichen in Form von drei Punkten wird verwendet, um anzuzeigen, daß die tatsächlichen Definitionen weggelassen wurden). Das Beispiel greift der Besprechung etwas vor, indem es bereits hier das Konstrukt interface verwendet; Schnittstellen werden später in diesem Kapitel besprochen. Kopplung und Bindung Nun, da Sie in der Lage sind, Schnittstellen zusammen zu gruppieren, stellt sich die Frage, wie Sie entscheiden, welche Schnittstellen zusammen zu gruppieren sind. Dies ist im Grunde eine Frage des Systementwurfs und würde am besten in einem eigenen Dokument zu diesem Thema beantwortet. (Es sind zahlreiche ausgezeichnete Bücher zum Thema objektorientierte Analyse und Entwurf erhältlich.) Eine allgemeine Richtlinie ist jedoch, daß ein guter Entwurf grundsätzlich zwei Attribute aufweist: geringe Kopplung (loose coupling) und enge Bindung (tight cohesion). Ersteres bedeutet, daß Komponenten in separaten Modulen nicht fest miteinander integriert sind; eine Anwendung, die Komponenten aus einem Modul verwendet, muß in der Regel nichts über Komponenten in einem anderen Modul wissen. (Es gibt sicherlich des öfteren eine Überlappung zwischen Modulen. Dies kann verschiedene Gründe haben, wie zum Beispiel die Notwendigkeit, Daten zwischen Modulen gemeinsam zu nutzen, oder um eine einheitliche Funktionalität zwischen Modulen zu erleichtern.) Liegt keine oder nur eine geringe Abhängigkeit zwischen Komponenten vor, so werden sie als gering gekoppelt bezeichnet.
Andererseits ist es in einem einzelnen Modul für einen Entwurf vorteilhaft, eine enge Bindung zu erreichen. Dies bedeutet, daß Schnittstellen innerhalb des Moduls fest miteinander integriert sind. Beispielsweise kann ein Modul mit der Bezeichnung InternerMotor Schnittstellen wie Zylinderkopf, Keilriemen, Kurbelwelle, Kolben und viele andere enthalten. Es ist schwierig, den Zweck einer dieser Komponenten zu beschreiben, ohne sich auf die anderen zu beziehen; daher könnte man von einer festen Bindung sprechen. Apropos Vergleich: Sie werden wenige Gemeinsamkeiten zwischen der Komponente InternerMotor und beispielsweise der Komponente AudioSystem finden; Komponenten von InternerMotor, wie beispielsweise OelFilter und Zuendkerze haben eine geringe Kopplung mit Audiosystem-Komponenten wie zum Beispiel CDGeraet und Tiefpasslautsprecher. Die Abbildung 3.1 veranschaulicht das Konzept von Kopplung und Bindung; beachten Sie, daß es zahlreiche Beziehungen zwischen den Komponenten innerhalb des gleichen Moduls gibt, während es jedoch wenige Beziehungen zwischen den Komponenten aus separaten Modulen gibt. Die Abbildung veranschaulicht auch den Vorteil von geringer Kopplung zwischen Modulen: Stellen Sie sich vor, Sie müßten auch die Zündkerzen und die Steuerkette austauschen, wenn Sie einen neuen CD-Player in Ihr
Fahrzeug einbauen würden! Die geringe Kopplung von Komponenten reduziert die Wahrscheinlichkeit, daß Änderungen an einer Komponente auch Änderungen an einer anderen Komponente erforderlich machen.
Abbildung 3.1: Kopplung und Bindung
Einfache Typen Wie die meisten Programmiersprachen verfügt auch IDL über eine Vielfalt einfacher Typen (die zu zusammengesetzten Typen verknüpft werden können). Diese Typen speichern einfache Werte, wie ganzzahlige Werte, Gleitkommazahlen, Zeichenketten und so weiter. void Der Typ void in IDL entspricht dem Typ void in C, C++ und Java. Es macht zwar wenig Sinn, eine Variable vom Typ void zu verwenden, jedoch ist der Typ für Methoden nützlich, die keinen Wert
zurückgeben. boolean Der IDL-Typ boolean speichert, wie sein Name schon andeutet, einen booleschen Wert. IDL definiert die zwei booleschen Konstanten true (wahr) und false (falsch), deren Bedeutung nicht weiter erläutert werden muß. Je nachdem, welche Programmiersprache verwendet wird, kann der IDL-Typ boolean auf einem ganzzahligen Typ basieren (wie zum Beispiel auf dem Typ short von C und C++) oder auf den spracheigenen booleschen Typen der Programmiersprache (wie es bei Java der Fall ist). boolean EinBoolescherWert; char und wchar Der IDL-Typ char speichert, dem Typ char in C, C++ und Java entsprechend, ein einzelnes Zeichen. Wie erwartet, bildet er den Typ char dieser Sprachen direkt ab. Bei dem Datentyp char handelt es sich um einen 8-Bit-Wert. char EinZeichen; In der Version 2.1 wurde CORBA der Typ wchar hinzugefügt, der eine implementationsabhängige Größe hat (diese beträgt normalerweise 16 Bit, jedoch sollten Sie hierzu in Ihrer ORB-Dokumentation nachschlagen, um die genaue Größe zu ermitteln). Gleitkommatypen IDL definiert zur Darstellung von Gleitkommawerten mehrere Typen; diese dürften den meisten Entwicklern bekannt sein. float In IDL stellt der Typ float einen IEEE-Gleitkommawert mit einfacher Genauigkeit dar. Auch hier liegt eine Entsprechung zu dem gleichnamigen Typ in C, C++ und Java vor. float EinGleitkommaWert; double und long double Der Typ double stellt einen IEEE-Gleitkommawert mit doppelter Genauigkeit dar. Wie zu erwarten, entspricht er dem bekannten Typ double in verschiedenen Programmiersprachen. double EinDoubleWert; Der in CORBA 2.1 eingeführte Typ long double stellt einen IEEE-Gleitkommawert mit erweiterter
doppelter Genauigkeit (double extended) dar, der einen Exponenten von mindestens 15 Bit und eine vorzeichenbehaftete Mantisse von mindestens 64 Bit hat. Integer-Typen In IDL sind mehrere Integer-Typen definiert. Auch diese werden vielen Programmierern bekannt vorkommen. Im Gegensatz zu vielen bekannten Programmiersprachen definiert IDL jedoch keinen einfachen Typen int, sondern nur Typen der Kategorie short und long. long und long long Der Typ long stellt in IDL einen vorzeichenbehafteten 32-Bit-Wert dar (mit einem Bereich von 231 bis 231-1). Dies entspricht dem Typen int in C/C++ (auf den meisten Plattformen) und dem Typen int in Java (auf allen Plattformen, da Java im Gegensatz zu C und C++ die Größe von int explizit festlegt). long EinLongWert; In CORBA 2.1 wurde der Typ long long hinzugefügt, bei welchem es sich um einen vorzeichenbehafteten 64-Bit-Wert handelt (mit einem Bereich von -263 bis 263-1). unsigned long und unsigned long long Der Typ unsigned long stellt in IDL eine vorzeichenlose Version des Typen long mit einem Bereich von 0 bis 232-1 dar. unsigned long EinVorzeichenloserLongWert; CORBA 2.1 wurde durch den Typen unsigned long long erweitert, bei dem es sich um einen vorzeichenlosen 64-Bit-Wert mit einem Bereich von 0 bis 264-1 handelt. short Der Typ short stellt einen vorzeichenbehafteten 16-Bit-Wert dar. Dies entspricht dem Typen short oder short int in C/C++ (auf den meisten Plattformen) und dem Typen short in Java. Sein Bereich geht von -215 bis 215-1. short EinShortWert; unsigned short Der Typ unsigned short ist die vorzeichenlose Version des Typs short, mit einem Bereich von 0 bis 216-1.
unsigned short EinVorzeichenloserShortWert; octet Bei dem Typen octet handelt es sich um einen 8-Bit-Wert, der während der Übertragung in keiner Weise übersetzt wird. Dieser Typ hat kein direktes Gegenstück in C und C++. Zur Darstellung des entsprechenden Wertetyps werden in C und C++ oft die Typen char und unsigned char verwendet. Java verfügt bekanntlich über den Typen byte, der dem Typen octet sehr ähnlich ist. octet EinOctetWert; string Der Typ string stellt eine Zeichenkette dar und ist dem Typen Cstring in C++ und der Klasse String in Java sehr ähnlich. C verfügt über kein direktes Gegenstück (da es in C keinen »echten« String-Typen gibt); statt dessen werden Zeichenarrays verwendet. IDL unterstützt sowohl Strings mit fester als auch mit variabler Länge. string EinStringVonFesterLaenge[20]; string EinStringVonVariablerLaenge; Der Modifizierer const Zusätzlich zu den besprochenen Standardtypen erlaubt IDL, wie C und C++, durch die Verwendung des Modifizierers const auch die Angabe von Konstantenwerten. Die Verwendung von const ist für Werte hilfreich, die sich nicht ändern sollen, wie zum Beispiel physikalische Konstanten wie pi oder c. Wie bei jedem anderen Wert auch, entspricht der Gültigkeitsbereich des const-Werts dem Gültigkeitsbereich der Schnittstelle oder des Moduls, in dem er definiert wurde. const float EineGleitkommaKonstante = 3.1415926535897932384; const long EineLongKonstante = 12345; const string EineStringKonstante = "Ist IDL nicht eine tolle Sprache?" Zusammengesetzte Typen Zusammengesetzte Typen werden, wie der Name schon sagt, aus anderen Typen zusammengesetzt und ermöglichen die Erzeugung von benutzerdefinierten Typen. Das wohl nützlichste dieser Konstrukte ist die Schnittstelle (interface), welche die Dienste definiert, die von Ihren Anwendungsobjekten bereitgestellt werden. Da IDL die Schnittstellen-Definitionssprache ist, scheint es angemessen, daß Schnittstellen den Hauptbestandteil von IDL-Quelltext ausmachen. Der Aufzählungstyp enum
Der Aufzählungstyp enum erlaubt die Erzeugung von Typen, die einen Wert aus einer durch enum vordefinierten Wertemenge enthalten können. Obwohl die Bezeichner in der Aufzählung eine geordnete Liste bilden, legt IDL keine geordnete Numerierung für die Bezeichner fest. Aus diesem Grund ist der Vergleich von enum-Werten mit ganzzahligen Werten kein sicherer Vorgang und würde so gut wie sicher über Programmiersprachen hinweg nicht portierbar sein. C und C++ verfügen ebenfalls über einen Aufzählungstyp, der ähnliche Merkmale aufweist. Ein Beispiel für den Typ enum finden Sie im Listing 3.3. Listing 3.3: Beispiel zu dem Typ enum 1: 2: 3: 4: 5: 6: 7: 8: 9:
enum WochenTage { Sonntag, Montag, Dienstag Mittwoch, Donnerstag, Freitag, Samstag };
Der Strukturtyp struct IDL stellt mit struct einen Strukturtyp zur Verfügung, der wie in C und C++ eine beliebige Anzahl von unterschiedlichen Elementwerten (sogar weitere struct-Typen) enthalten kann. Der Typ struct ist in IDL besonders nützlich, da struct-Typen, im Gegensatz zu CORBA-Objekten (die durch den Typ interface dargestellt werden), mit ihrem Wert übergeben werden (Call By Value) und nicht durch eine Referenz (Call By Reference). Wenn also, mit anderen Worten, ein struct an ein Remote-Objekt übergeben wird, wird eine Kopie der Werte von diesem struct erzeugt und an das Remote-Objekt übergeben. Im Listing 3.4 finden Sie ein Beispiel zu dem Typ struct. Listing 3.4: Beispiel zu dem Typ struct 1: 2: 3: 4: 5: 6: 7: 8: 9:
struct DatumsStruktur { short Jahr, short Monat, short Tag short Stunde, short Minute, short Sekunde, long Millisekunde };
Der Typ union Der IDL-Typ union stellt, wie auch der Typ struct, Werte dar, die von verschiedenen Typen sind. Der
IDL-Typ union wird C- und C++-Programmierern etwas merkwürdig vorkommen, da er eine Mischung aus einem Typ union aus C/C++ und einer case-Anweisung darstellt. Pascal-Programmierer sollten das Format jedoch erkennen. Ein Beispiel einer union-Definition finden Sie in Listing 3.5. Listing 3.5: Beispiel zu dem Typen union 1: 2: 3: 4: 5: 6: 7: 8: 9:
union MultiplePersoenlichkeiten switch(long) { case 1: short meineShortPersoenlichkeit, case 2: double meineDoublePersoenlichkeit, case 3: default: string meineStringPersoenlichkeit; };
Im Beispiel aus dem Listing 3.5 kann eine Variable vom Typ MultiplePersoenlichkeiten entweder einen short-, double- oder string-Wert enthalten, je nachdem, welchen Wert der Parameter bei der Verwendung des union in einem Methodenaufruf enthält (der Parameter wird als Entscheider, englisch Discriminator, bezeichnet). Ein Entscheider (Discriminator) ist, so wie er in einer IDL-union verwendet wird, derjenige Parameter, der festlegt, welcher Wert in der union verwendet wird. Im Beispiel aus Listing 3.5 wurde ein Typ long für den Entscheider verwendet; es können auch andere Typen verwendet werden, darunter die Typen long, long long, short, unsigned long, unsigned long long, unsigned short, char, boolean oder enum. Der Konstantenwert in der caseAnweisung muß dem Typen des Entscheiders entsprechen.
In der Praxis sind IDL-Unions selten nützlich. Während der traditionelle C-Typ union in der Optimierung von C-spezifischem Code eine Verwendung findet, erscheinen Unions so gut wie nie in verteilten Anwendungen. Das Verhalten von Objekten wird in der Regel durch die Verwendung von Schnittstellen besser abstrahiert. Sollte jedoch die Verwendung des Typs union erforderlich werden, stellt ihn IDL für alle Fälle zur Verfügung.
Der Typ interface Der Typ interface ist der bei weitem vielseitigste aller IDL-Datentypen. Die Schnittstelle
beschreibt die von einem CORBA-Objekt bereitgestellten Dienste (Services). Diese Dienste erscheinen in Form von Operationen (oder Methoden) und ähneln Methoden in objektorientierten Programmiersprachen wie C++ und Java. Der Unterschied ist wiederum, daß IDL nur verwendet wird, um die Schnittstellen dieser Methoden anzugeben, während Sprachen wie Java und C++ verwendet werden, um Schnittstellen anzugeben und (in der Regel) eine Implementierung für die Methoden dieser Schnittstellen bereitzustellen. Der IDL-Typ interface ist dem Typen interface aus Java sehr ähnlich, da beide keine Implementierung für die definierten Methoden bereitstellen (der Hauptunterschied ist jedoch, daß in IDL der Typ interface Parameter enthalten kann, was bei Java nicht der Fall ist). C++ verfügt über kein direktes Gegenstück zu dem IDL-Typ interface, obwohl es für C++-Anwendungen üblich ist, für die Definition der Schnittstelle einer Klasse eine Header-Datei zu verwenden. Eine Schnittstellendefinition in IDL kann deshalb mit einer Header-Datei aus C++ verglichen werden, die eine Klassendefinition enthält. Darüber hinaus kann eine C++-Klasse, deren Methoden alle virtuell sind, als analog zur IDL-Schnittstelle betrachtet werden. Wie eine Java- oder C++-Klasse kann eine IDL-Schnittstelle Attribute enthalten (die allgemein als Elementvariablen bekannt sind) und Operationen (die wiederum als Methoden oder Elementfunktionen bekannt sind). Wie beim Typ interface von Java sind alle Methoden, die innerhalb einer IDL-Schnittstelle definiert werden, öffentlich (als public deklariert), das heißt, sie können von jedem anderen Objekt aufgerufen werden, das einen Verweis auf das Implementierungsobjekt der Schnittstelle enthält. Abschließend stellt IDL auch einige zusätzliche Modifizierer zur weiteren Beschreibung der Schnittstelle und seiner Elemente bereit, da IDL-Schnittstellen in der Regel RemoteObjekte beschreiben. Beispielsweise können Methoden als oneway deklariert werden; Argumente von Methoden können als in, out oder inout deklariert werden, und Attribute können als readonly deklariert werden. Sie werden gleich erfahren, was diese Modifizierer bedeuten und wie sie verwendet werden und warum. Methoden und Parameter Die Methoden einer Schnittstelle sind im wesentlichen das, was die Funktionalität eines Objekts definiert. Obwohl die Implementierung des Objekts festlegt, wie sich das Objekt verhält, legen die Methodendefinitionen der Schnittstelle fest, welches Verhalten das Objekt aufweist, das diese Schnittstelle bereitstellt. Diese Methodendefinitionen werden oft als Methodensignaturen oder einfach als Signaturen bezeichnet. IDL-Methoden können jeden beliebigen IDL-Datentypen als Eingabe- und Ausgabeparameter verwenden - einfache Typen, structs, sequences und sogar Schnittstellen. Die allgemeine Syntax einer Methodendeklaration sieht folgendermaßen aus: [oneway] return_type MethodenName (param1_dir param1_typ param1_name, param2_dir param2_typ param2_name, ...); Der Modifizierer oneway ist optional; return_type gibt die Daten an, die von der Methode zurückgegeben werden, die Modifizierer paramn_dir geben die Richtung eines jeden Parameters an (in, out oder inout), und paramn_typ gibt den Typ eines jeden Parameters an. Modifizierer dieser Art
sind in Programmiersprachen gewöhnlich nicht zu finden und verdienen deshalb besondere Beachtung. Eine Methodensignatur, oft einfach als Signatur bezeichnet, beschreibt, was eine Methode ausführt (im Idealfall sollte der Name der Methode zumindest in groben Zügen andeuten, was die Methode ausführt), welche Parameter (und deren Typen) die Methode als Eingabe benötigt, und welche Parameter (und deren Typen) sie als Ausgabe zurückgibt.
Die Parameter in, out und inout Wie bereits erwähnt, können Parameter einer Methode als in, out oder inout deklariert werden. Diese Namen sind ziemlich selbsterklärend: Ein in-Parameter dient als Eingabe der Methode; ein outParameter ist die Ausgabe der Methode; ein inout-Parameter dient als Eingabe und Ausgabe der Methode. In bezug auf Remote-Methoden bedeutet dies, daß vor Aufruf der Methode etwaige in- und outParameter über das Netzwerk zum Remote-Objekt übertragen werden. Nachdem die Methode ausgeführt wurde, werden alle in- und inout-Parameter, zusammen mit dem Rückgabewert der Methode (falls vorhanden), zurück zum aufrufenden Objekt übertragen. Beachten Sie, daß inoutParameter zweimal übertragen werden, einmal als ein Eingabewert und einmal als Ausgabewert. oneway-Methoden Das typische Musterbeispiel für den Aufruf einer Remote-Methode sieht folgendermaßen aus: Ruft ein Objekt eine Methode eines Remote-Objekts auf, wartet dieses aufrufende Objekt (dieser Vorgang wird als Blockieren bezeichnet) darauf, daß die Methode ausgeführt und zurückgegeben wird. Wenn das Remote-Objekt die Verarbeitung des Methodenaufrufs abgeschlossen hat (oft als Anfrage bezeichnet), kehrt es zum aufrufenden Objekt zurück, welches dann seine Verarbeitung fortsetzt. Dieser Vorgang wird in Abbildung 3.2 erläutert. Im allgemeinen bezieht sich der Begriff Blockieren (Blocking) auf jeden Punkt, an welchem ein Prozeß oder Thread auf besondere Ressourcen oder einen anderen Prozeß oder Thread wartet. Im Zusammenhang mit CORBA wird, wenn ein Client eine Remote-Methode aufruft, und auf das zurückzugebende Ergebnis warten muß, der Client als blockierend bezeichnet.
Eine Anfrage (Request) ist einfach eine andere Bezeichnung für einen Remote-Methodenaufruf. Der Begriff wird weithin verwendet, wenn auf die Operation eines verteilten Systems bezogen wird. Wenn Sie sich die dynamische Aufrufschnittstelle (Dynamic Invocation Interface, DII) von CORBA ansehen, werden Sie feststellen, daß Methoden durch ein Request-Objekt aufgerufen werden können.
Abbildung 3.2: Blockieren eines Remote-Methodenaufrufs Wurde eine Methode als oneway deklariert, bedeutet dies, daß das Objekt, welches diese Methode aufruft, nicht blockieren wird. Statt dessen wird dieses Objekt die Remote-Methode aufrufen und anschließend sofort mit der Verarbeitung fortfahren, während das Remote-Objekt die RemoteMethode ausführt. Der Vorteil dieser Vorgehensweise ist, daß das aufrufende Objekt mit seiner Arbeit fortfahren kann, anstatt darauf zu warten, daß das Remote-Objekt die Anfrage abschließt. Die Abbildung 3.3 veranschaulicht die Vorgehensweise einer oneway-Methode.
Abbildung 3.3: Der oneway-Remote-Methodenaufruf (blockiert nicht) Die Flexibilität des oneway-Aufrufmodells hat jedoch ihren Preis: Da der Methodenaufruf zurückgegeben wird, bevor die Ausführung der Methode abgeschlossen wurde, kann die Methode keinen Wert zurückgeben. Deswegen muß der Rückgabewert einer oneway-Methode als void und alle Parameter als in deklariert werden (out- und inout-Parameter sind nicht zulässig). Darüber hinaus
kann eine oneway-Methode keine Exceptions auslösen. Das aufrufende Objekt hat keine Möglichkeit festzustellen, ob die Methode erfolgreich ausgeführt wurde; die Infrastruktur von CORBA macht einen bestmöglichen Versuch, die Methode auszuführen, jedoch ist eine erfolgreiche Ausführung nicht gewährleistet. (Leser, die mit dem Protokoll User Datagram Protocol vertraut sind, werden hier eine Ähnlichkeit feststellen.) Aus diesem Grund sind oneway-Methoden vor allem für Situationen nützlich, in denen ein Objekt ein anderes Objekt über einen bestimmten Status informieren möchte, jedoch 1) diese Nachricht nicht als entscheidend betrachtet und 2) keine Antwort erwartet (oder wünscht). Eine Methode, die nur über in-Parameter verfügt, void zurückgibt und keine Exceptions auslöst, ist nicht automatisch eine oneway-Methode. Der Unterschied zwischen solchen Methoden und oneway-Methoden: Während bei der letzteren die Ausführung nicht gewährleistet ist (und der Client keine Möglichkeit hat festzustellen, ob sie ausgeführt wurde), wird die erstere dazu führen, daß der Client blockiert, bis ein Ergebnis von der Remote-Methode zurückgegeben wird (auch wenn das Ergebnis Null sein wird). Der zu beachtende Unterschied ist hier, daß die erfolgreiche Ausführung einer Nicht-oneway-Methode festgestellt werden kann, da eine CORBA-System-Exception ausgelöst werden würde, falls dies nicht der Fall wäre.
Es gibt Möglichkeiten, die mit dem Blockieren verbundenen Umstände zu bewältigen. Am häufigsten kann das Blockieren-»Problem« durch die Verwendung von Multithreading umgangen werden, indem ein separater Thread erzeugt wird, um die Remote-Methode aufzurufen. Während dieser Thread blockiert ist, solange er auf das zurückzugebende Ergebnis wartet, können andere Threads mit der Arbeit fortfahren. Am 10. Tag werden Sie solche Themen detaillierter analysieren und einige Lösungsmöglichkeiten kennenlernen. Attribute Ein Attribut einer IDL-Schnittstelle entspricht einem Attribut einer Java- oder C++-Klasse, mit dem Unterschied, daß IDL-Attribute immer über einen öffentlichen Gültigkeitsbereich verfügen. (Tatsächlich verfügt alles in einer IDL-Schnittstelle über einen öffentlichen Gültigkeitsbereich.) Die allgemeine Syntax für die Definition eines Attributs sieht wie folgt aus: [readonly] attribute attribut_typ attributName; In Java und C++ wird es allgemein als guter Programmierstil angesehen, Datenzugriffs- und Datenänderungsmethoden (Accessor- und Mutator-Methoden) für Attribute einer Klasse bereitzustellen und die Klasse selbst als geschützt (protected) oder privat (private) zu deklarieren. IDL führt dieses Konzept einen Schritt weiter: IDL-Attribute zeigen auf Datenzugriffs- und Datenänderungsmethoden, wenn der IDL-Quelltext kompiliert wird. So bildet die Definition attribute short meinChannel;
folgende zwei Methoden ab: short meinChannel; void meinChannel(short value); readonly Attribute Das vorangegangene Beispiel zeigt die optionale Verwendung des readonly-Modifizierers an. Wie der Name andeutet, wird dieser Modifizierer verwendet, um anzugeben, daß ein bestimmtes Attribut nur zum Lesen gedacht ist; sein Wert kann durch ein externes Objekt nicht direkt geändert werden. (Das Objekt, welches die Schnittstelle implementiert, kann selbstverständlich die Werte seiner eigenen Attribute nach Bedarf ändern.) Obwohl ein Nicht-readonly-Attribut auf ein Paar von Datenzugriffsund Datenänderungsmethoden zuordnet, wird der IDL-Compiler für ein readonly-Attribut nur eine Datenzugriffsmethode erzeugen. Vererbung von Schnittstellen IDL-Schnittstellen unterstützen, wie die ihnen ähnelnden Java- und C++-Konstrukte, das Konzept der Vererbung. Dies bedeutet, daß eine Schnittstelle die Methoden und Attribute einer anderen Schnittstelle erben kann (man kann auch davon sprechen, daß sich eine neuere Schnittstelle von einer älteren ableitet oder davon abgleitet wurde). Zusätzlich zur Vererbung der Methoden und Attribute seiner übergeordneten Klasse kann eine abgeleitete Klasse eigene Methoden und Attribute definieren. Die abgeleitete Klasse kann auch überall ersetzt werden, wo die übergeordnete Klasse erwartet wird: Erfordert beispielsweise eine Methode einen Parameter vom Typ Fisch, und eine Schnittstelle Heilbutt ist eine abgeleitete Klasse der Fisch-Schnittstelle, dann kann diese Methode mit einem Parameter vom Typ Heilbutt statt Fisch aufgerufen werden. Eine abgeleitete Klasse, im Englischen Subclass, ist eine Klasse, die Methoden und Attribute von einer anderen Klasse erbt. Die Klasse, von der diese Methoden und Attribute geerbt wurden, wird als übergeordnete Klasse, oder englisch Superclass, bezeichnet. Obwohl IDL Schnittstellen und nicht Klassen verwendet, können diese allgemeinen Begriffe auch hier angewendet werden.
In der Terminologie der objektorientierten Programmierung und der Objekte bezieht sich Polymorphie auf die Fähigkeit, eine abgeleitete Klasse in einem Parameter, der die übergeordnete Klasse dieser Klasse erwartet, zu ersetzen. Da die abgeleitete Klasse eine Instanz der übergeordneten Klasse ist (so wie ein Beagle ein Hund ist), kann jede Operation, die auf eine übergeordnete Klasse angewendet wird, auch auf eine abgeleitete Klasse angewendet werden. Polymorphie stellt ein grundlegendes Merkmal der objektorientierten Architektur dar.
Die Syntax für die Definition von Schnittstellenvererbung ähnelt der von C++ und Java verwendeten Syntax. Die Ausnahme in IDL ist, daß keine Sichtbarkeit für die übergeordnete Klasse angegeben wird (denken Sie daran, daß Methoden und Attribute im Grunde alle öffentlich sind, also als public deklariert gelten). Die Vererbungssyntax wird in Listing 3.6 veranschaulicht. Listing 3.6: Beispiel zur Vererbungssyntax 1: 2: 3: 4: 5: 6:
interface Fisch { ... }; interface Heilbutt : Fisch { ... };
Im Listing 3.6 wird die Schnittstelle Heilbutt von der Schnittstelle Fisch abgeleitet, wie durch den Doppelpunkt-Operator (:) angezeigt wird. Die Attribute und Methoden wurden in diesem Beispiel weggelassen. Eine letzte Anmerkung bezüglich der Vererbung von IDL-Schnittstellen: IDL-Schnittstellen können, wie C++-Klassen, von mehr als einer übergeordneten Klasse erben. Diese Fähigkeit wird als Mehrfachvererbung bezeichnet und ist in Java nicht verfügbar. Java erlaubt es einer einzelnen Klasse, mehrere Schnittstellen zu implementieren, was oft zum gleichen Ergebnis wie Mehrfachvererbung führt. Die Syntax für Mehrfachvererbung von IDL wird in Listing 3.7 gezeigt. Listing 3.7: Beispiel zur Mehrfachvererbungssyntax 1: 2: 3: 4: 5: 6: 7: 8:
interface LandFahrzeug { ... }; interface WasserFahrzeug { ... }; interface AmphibienFahrzeug : LandFahrzeug, Wasserfahrzeug { ...
9: }; Im Listing 3.7 erbt die Schnittstelle AmphibienFahrzeug, die sowohl ein LandFahrzeug als auch ein WasserFahrzeug darstellt, von diesen beiden Schnittstellen. Beachten Sie, daß wegen des polymorphen Verhaltens von abgeleiteten Klassen ein AmphibienFahrzeug sowohl ein LandFahrzeug als auch ein WasserFahrzeug ersetzen kann. Definitionssyntax von Schnittstellen Die Syntax einer Schnittstellendefinition ist der in C++ oder Java verwendeten Syntax nicht unähnlich. Der Hauptteil der Schnittstelle enthält Methodensignaturen und Attributdeklarationen, die keiner bestimmten Anordnung entsprechen müssen. Einige Beispiele zu Schnittstellendefinitionen finden Sie in Listing 3.8. Listing 3.8: Beispiel-IDL-Schnittstellen 1: // Dieses Modul definiert einige nützliche Haushaltsgeräte. 2: module Geraete { 3: // Definition der Schnittstelle für den Fernseher 4: interface Fernseher { 5: // Meine Seriennummer 6: readonly attribute string meineSerienNummer; 7: // Meine aktuell eingestellte Lautstärke 8: attribute short meineLautstaerke 9: // Mein aktueller Kanal 10: attribute short meinKanal; 11: // Fernseher einschalten 12: void einSchalten(); 13: // Fernseher ausschalten 14: void ausSchalten(); 15: // 16: void 17: }; 18: interface WWWFernseher : Fernseher { 19: // den angegebenen URL besuchen 20: void geheZuURL(in Internet::URL url); 21: }; 22: }; Eine genauere Betrachtung ergibt, daß IDL-Schnittstellen keinen Konstruktor oder Destruktor angeben. Für Java-Entwickler dürfte dies nicht überraschend sein, da auch Java-Schnittstellen keine Konstruktoren oder Destruktoren enthalten (andererseits sind in Java auch keine Destruktoren vorhanden). C++-Entwickler dürften sich fragen, was mit den Konstruktoren und Destruktoren passiert ist. Wie sich herausstellt, sind Konstruktoren und Destruktoren immer noch Bestandteil von CORBA-Objekten; sie sind nur nicht als Bestandteil der Objektschnittstelle enthalten. Sie werden den
Grund hierzu im nächsten Kapitel erfahren, wenn Sie mit der Erzeugung einer CORBA-Anwendung beginnen.
Weitere IDL-Konstrukte IDL unterstützt einige andere nützliche Konstrukte. Darunter fällt auch die Fähigkeit, sich auf jeden IDL-Typen durch einen benutzerdefinierten Typnamen zu beziehen, und die Fähigkeit, einen Typnamen zu deklarieren, ohne ihn zu definieren, was für die Behandlung von zirkulären Abhängigkeiten hilfreich ist. typedef IDL unterstützt, wie C und C++, für die Erzeugung eines benutzerdefinierten Typnamens die Anweisung typedef. Durch typedef kann jeder IDL-Typ durch Angabe eines vom Benutzer ausgewählten Typnamens zugänglich gemacht werden. Diese Fähigkeit fügt der Sprache IDL eine gewisse Benutzerfreundlichkeit hinzu. Beispielsweise enthält die Schnittstelle Fernseher in Listing 3.8 das Element meineSerienNummer, welches vom Typen string ist. Da die Verwendung des Typen string nicht besonders aufschlußreich ist, wäre die Definition eines Typs SerienNummer vorzuziehen. Dieser Typ könnte dann von jeder Schnittstelle und jeder Methode verwendet werden, die eine Seriennummer erfordert. Angenommen, der Typ string ist für das Speichern von Seriennummern angemessen, wäre es angenehm, einen String-Typen zu verwenden, sich auf diesen jedoch über die Bezeichnung SerienNummer zu beziehen. Die Anweisung typedef string SerienNummer; weist den Compiler an, den Typen SerienNummer immer als String-Typ zu behandeln. Vorwärtsdeklarationen Sie werden hin und wieder Schnittstellen erzeugen, die sich gegenseitig referenzieren. Dies kann zum Beispiel vorkommen, wenn in der ersten Schnittstelle ein Attribut vom Typ der zweiten Schnittstelle enthalten ist oder eine Methode, die den Typen der zweiten Schnittstelle als Parameter oder Rückgabewert verwendet. Die zweite Schnittstelle wird die erste Schnittstelle auf die gleiche Weise referenzieren. Listing 3.9 veranschaulicht dieses Konzept, das als zirkuläre Abhängigkeit bezeichnet wird. Eine zirkuläre Abhängigkeit tritt ein, wenn zwei Klassen (oder Schnittstellen im Zusammenhang mit IDL) jeweils über Attribute oder Methoden verfügen, die sich auf die andere Klasse beziehen.
Listing 3.9: Beispiel zur zirkulären Abhängigkeit 1: module Zirkulaer {
2: 3: 4: 5: 6: 7: 8:
interface A { void verwendeB(in B einB); } interface B { void verwendeA(in A einA} } };
Im Listing 3.9 referenziert die Schnittstelle A die Schnittstelle B, welche selbst die Schnittstelle A referenziert. Würden Sie versuchen, den Quelltext wie angezeigt zu kompilieren, so würde der IDLCompiler einen Fehler in der Methode verwendeB() ausgeben, da die Schnittstelle B unbekannt ist. Würden Sie die Definitionsfolge umkehren und zuerst die Schnittstelle B definieren, so würde der IDLCompiler einen Fehler in der Methode verwendeA() ausgeben, da die Schnittstelle A dann unbekannt wäre. Wie Sie sehen können, beißt sich bei dem Problem der zirkulären Referenz die Katze in den Schwanz. C- und C++-Programmierer kennen vielleicht schon die Lösung des Problems der zirkulären Referenz: die Vorwärtsdeklaration. Eine Vorwärtsdeklaration erlaubt es Ihnen, dem IDL-Compiler mitzuteilen, daß Sie beabsichtigen, den deklarierten Typen später zu definieren, ohne den Typen zu diesem Zeitpunkt definieren zu wollen (an der Definition an sich werden Sie nicht vorbeikommen). Die Syntax einer Vorwärtsdeklaration, die der C- und C++-Syntax sehr ähnelt, ist einfach: interface B; Dies teilt dem Compiler mit, daß die Definition für die Schnittstelle B zu einem späteren Zeitpunkt folgen wird, und für den Augenblick die Referenz zur noch undefinierten Schnittstelle akzeptiert werden soll. Listing 3.10 veranschaulicht, wie eine Vorwärtsdeklaration zu dem vorherigen Beispiel aussehen würde. Listing 3.10: Beispiel zur zirkulären Abhängigkeit 1: module Zirkulaer { 2: //Vorwärtsdeklaration der Schnittstelle B 3: interface B; 4: interface A { 5: void verwendeB(in B einB); 6: } 7: interface B { 8: void verwendeA(in A einA} 9: } 10: }; Wenn der IDL-Compiler in Listing 3.10 die Definition für verwendeB() erreicht, stellt er fest, daß die
Schnittstelle B bereits deklariert wurde (durch die Vorwärtsdeklaration). Er gibt deswegen keine Fehlermeldung aus. Sie hätten genausogut auch die Schnittstelle B als erstes definieren können und eine Vorwärtsdeklaration auf die Schnittstelle A machen können; der IDL-Compiler legt keine bestimmte Reihenfolge fest, in der die Schnittstellen definiert werden müssen.
Container-Typen Die meisten Programmiersprachen verfügen über Konstrukte für die Behandlung von mehreren Werten eines ähnlichen Typs. Arrays werden in vielen Programmiersprachen verwendet: Java verfügt über java.util.Vector, C++ über die Bibliothek Standard Template Library (STL) und natürlich auch zahlreiche Bibliotheken von Container-Klassen. IDL stellt hier keine Ausnahme dar und bietet zwei solcher Konstrukte: den Typ sequence, bei dem es sich um ein dynamisch größenveränderbares Array handelt und den Typ array, der das Array-Konstrukt widerspiegelt, das auch in vielen anderen Programmiersprachen zu finden ist. Der Typ sequence Der Typ sequence ist einfach ein dynamisch größenveränderbares Array von Werten. Diese Werte können dynamisch eingefügt werden oder aus der Sequenz entfernt werden; die Sequenz verwaltet ihre Größe entsprechend. Alle Werte einer Sequenz müssen vom gleichen Typ sein oder vom gleichen Typ abgeleitet sein (mit Ausnahme des Typs any). Zum Beispiel: sequence temperaturSequenz Dieses Beispiel definiert eine Sequenz von Gleitkommawerten und weist diesen Typen der Variablen temperaturSequenz zu. Werte vom Typ float können anschließend temperaturSequenz hinzugefügt werden, oder Werte können entfernt werden. Denken Sie an das Haushaltsgeräte-Beispiel aus Listing 3.8; sie können auch eine Sequenz von Fernseher-Objekten erzeugen: sequence BestandAnFernsehern; In diesem Fall kann BestandAnFernsehern mit Objekten vom Typ Fernseher ausgefüllt werden oder einem anderen, von Fernseher abgeleiteten Objekt, in diesem Fall WWWFernseher. Falls Sie eine dritte, von Fernseher abgeleitete Schnittstelle erzeugt haben, wie zum Beispiel TragbarerFernseher, könnte die Sequenz BestandAnFernsehern auch Fernseher, WWWFernseher und TragbarerFernseher enthalten. Das Array
Ein Array in IDL entspricht den Array-Konstrukten in C, C++ und Java. Ein Array speichert eine Serie von ähnlichen Datentypen bekannter Größe. Zum Beispiel: string WochenTage[7] Dieses Beispiel definiert ein Array, das sieben String-Werte enthalten kann und die Bezeichnung WochenTage trägt. Arrays können Elemente eines jeden beliebigen IDL-Datentyps enthalten; bezüglich des Haushaltsgeräte-Beispiels (siehe Listing 3.8) könnten Sie folgendes Array definieren: Fernseher TVArray[10]; Hierdurch wird ein Array mit zehn Fernsehern definiert. Denken Sie daran, daß jedes Array-Element, dank der Polymorphie, entweder einen Fernseher oder einen davon abgeleiteten Typen WWWFernseher enthalten kann.
Der Typ exception Ein Konzept, das in letzter Zeit von vielen Entwicklern aufgegriffen wird, ist die Verwendung von Exceptions zur Durchführung einer Fehlerbehandlung. Exceptions, die in objektorientierten Sprachen wie Java und C++ integriert sind, stellen Konstrukte dar, die bereitgestellt wurden, um bestimmte Fehlerbedingungen zu bezeichnen. Wenn die aufrufende Methode die Exception abfängt, kann sie entweder die Exception behandeln oder sie bis zur aufrufenden Stelle dieser Methode weiterreichen. Dieser Vorgang könnte bis hin zur obersten Ebene (in der Regel main()) fortgesetzt werden; falls die Methode der obersten Ebene die Exception nicht behandelt, wird die Anwendung in der Regel beendet (eine Anwendung, die dies nicht verhindert, wird als schlecht programmiert angesehen). Wenn eine Methode eine Exception an die aufrufende Stelle weitergibt, spricht man davon, daß die Methode eine Exception auslöst (im Englischen werden für diesen Vorgang die Begriffe »throw an exception« und »raise an exception« verwendet).
CORBA und IDL unterstützen die Exception-Behandlung über vordefinierte Standard-Exceptions und benutzerdefinierte Exceptions vollständig. IDL erlaubt es Entwicklern, Exceptions zu definieren und anzugeben, welche Exceptions von welchen Methoden ausgelöst werden. Wenn eine Exception ausgelöst wird, reicht das ORB diese Exception zurück an das ORB des aufrufenden Objekts, welches dann die Exception zurückgibt an das aufrufende Objekt. Auf diese Art erweitert CORBA den bekannten Exception-Übergabemechanismus zu einer verteilten Architektur. Zusätzlich zu ihrer verteilten Natur unterscheiden sich Exceptions in IDL auch in anderer Hinsicht von ihren Gegenstücken in C++ und Java. C++-Exceptions können praktisch von jedem beliebigen Typ sein; C++ erwartet nicht einmal, daß Exception-Objekte von einem bestimmten Typ abgeleitet werden (beispielsweise könnte eine Methode eine const char* auslösen, falls dies gewünscht wäre). Java-Exceptions können von jedem Typ sein, die die Schnittstelle java.lang.Throwable implementiert.
IDL jedoch ist hier etwas stärker einschränkend als diese Sprachen; Exception-Objekte müssen explizit als solche deklariert werden. Darüber hinaus unterstützt IDL keine Vererbung von ExceptionTypen (C++ und Java erlauben es, Exception-Typen von anderen Typen abzuleiten) . exception Der IDL-Typ exception selbst hat eine Ähnlichkeit mit dem Typen struct; er enthält verschiedene Datenelemente (jedoch keine Methoden). Die Definition eines Exception-Typs erinnert auch an die Definition von struct, wie das Beispiel in Listing 3.11 zeigt. Das Beispiel demonstriert, daß eine Exception nicht unbedingt über Elemente verfügen muß; manchmal stellt die einfache Auslösung einer Exception genügend Information zur Fehlerbehandlung bereit. Listing 3.11: Beispiel zur Exception-Definition 1: // Diese Exception könnte verwendet werden, wenn eine zu öffnende 2: // Datei nicht gefunden werden konnte. Da es für den Aufrufer 3: // hilfreich sein könnte, den ungültigen Namen zu erfahren, stellt 4: // die Exception diese Information bereit 5: exception DateiNichtGefundenException { 6: string DateiName; 7: }; 8: // Diese Exception könnte für eine bestimmte Operation verwendet 9: // werden, die nicht wie erwartet innerhalb eines zuvor festgelegten 10: // Zeitraums ausgeführt werden konnte. Die Exception stellt den Namen 11: // der Operation und den für die Operation vorgesehenen Zeitraum 12: // bereit. 13: exception ZeitueberschreitungFuerOperationException { 14: string NameDerOperation; 15: long Zeitlimit; 16: }; 17: // Diese Exception könnte verwendet werden, wenn der Versuch, sich in 18: // einem System anzumelden, fehlgeschlagen ist. Es wird keine weitere 19: // Information benötigt. 20: exception UngueltigeAnmeldungException { 21: }; Standard-Exceptions CORBA erlaubt es dem Anwender, benutzerdefinierte Exceptions zu erzeugen und stellt darüber hinaus zahlreiche Standard-Exceptions, auch als System-Exceptions bezeichnet, bereit. Die
Exceptions dieser Gruppe können von jedem Remote-Methodenaufruf ausgelöst werden. IDLMethodendefinitionen deklarieren nicht explizit, daß sie eine System-Exception auslösen; statt dessen werden diese Exceptions implizit ausgelöst. (Wenn der IDL-Quelltext kompiliert wird, deklarieren die generierten Methodendefinitionen, daß CORBA-System-Exceptions ausgelöst werden, sowie etwaige benutzerdefinierte Exceptions, die von diesen Methoden ausgelöst werden.) Zusätzlich zu regulären Methoden können sogar die Datenzugriffs- und Datenänderungsmethoden (Accessor- und MutatorMethoden) - entsprechend den Attributen von Schnittstellen - Exceptions auslösen, auch wenn sie keine benutzerdefinierten Exceptions auslösen können. Die in CORBA verfügbaren Standard-Exceptions sind in Tabelle 3.1 aufgelistet. Tabelle 3.1: Standard-Exceptions in CORBA Exception
Beschreibung
UNKNOWN
Unbekannte Exception
BAD_PARAM
Ein ungültiger Parameter wurde übergeben
NO_MEMORY
Fehler bei Reservierung dynamischen Speichers
IMP_LIMIT
Verletzung der Implementationsbegrenzung
COMM_FAILURE
Kommunikationsfehler
INV_OBJREF
Ungültige Objektreferenz
NO_PERMISSION
Keine Zugriffsrechte für aufgerufene Operation
INTERNAL
Interner ORB-Fehler
MARSHAL
Fehler beim Abrufen des Parameters oder Ergebnisses
INITIALIZE
Fehler bei der ORB-Initialisierung
NO_IMPLEMENT
Implementation der Operation nicht verfügbar
BAD_TYPECODE
Fehlerhafter TypeCode
BAD_OPERATION
Ungültiger Vorgang
NO_RESOURCES
Unzureichende Ressourcen zur Ausführung der Anfrage
NO_RESPONSE
Antwort auf Anfrage noch nicht verfügbar
PERSIST_STORE
Fehler bei persistentem Speicher
BAD_INV_ORDER
Aufruf der Routinen in ungültiger Folge
TRANSIENT
Vorübergehender Fehler - Anfrage erneut durchführen
FREE_MEM
Kann Speicher nicht freigeben
INV_IDENT
Ungültige Bezeichner-Syntax
INV_FLAG
Angabe eines ungültigen Flags
INTF_REPOS
Fehler beim Zugriff auf Schnittstellenablage
BAD_CONTEXT
Fehler beim Verarbeiten des Kontextobjekts
OBJ_ADAPTER
Fehler vom Objektadapter festgestellt
DATA_CONVERSION Fehler bei der Datenumwandlung OBJECT_NOT_EXIST Objekt nicht vorhanden - Referenz sollte gelöscht werden
Der Typ any Für jene gelegentlich vorkommenden Methoden, die jede Art eines CORBA-Objekts als Parameter akzeptieren müssen oder einen Parameter erwarten, der potentiell einer von mehreren nicht zusammenhängenden Datentypen sein könnte (mit anderen Worten, keiner der Datentypen ist von einem der anderen abgeleitet), stellt IDL den Typen any zur Verfügung. Wenn any als der Typ eines Parameters oder Rückgabewerts verwendet wird, kann dieser Wert praktisch von jedem beliebigen IDL-Typen sein. Eine Methode, die any als Eingabeparameter akzeptiert, wird in der Regel zunächst genau ermitteln müssen, welcher Objekttyp übergeben wurde, bevor sie das Objekt bearbeiten kann; Sie werden sehen, wie dies bewerkstelligt wird, sobald Sie anfangen, CORBA-Clients und -Server zu
implementieren. Für eine Methode, die aus einer Vielzahl von Typen von CORBAObjekten einen als Parameter akzeptieren muß, ist any nicht immer die einzige Option. Falls der Typ des Parameters aus einer kleineren Anzahl von Typen stammen kann, und der Typ des Parameters bekannt ist, könnte der Typ union die bessere Alternative sein. Jedoch müßten bei der Verwendung des Typs union alle diese Typen im Vorfeld bekannt sein; dies ist für den Typ any nicht erforderlich. Ein any-Parameter kann praktisch jeden Objekttyp enthalten, sogar einen, der dem den anyParameter annehmenden Server unbekannt ist.
Ein Beispiel für die Verwendung des Parameters any finden Sie im Listing 3.12. In diesem Beispiel akzeptiert die Methode ObjektAnzeigen() einen einzigen Parameter (mit der Bezeichnung Object), der von einem beliebigen Typ sein kann. Ein Client, der diese Methode aufruft, kann ein Objekt von einem beliebigen IDL-Typen verwenden, um diesen Parameter zu belegen. Innerhalb von ObjektAnzeigen() wird die Methode versuchen festzustellen, von welchem Typen Object ist. Falls sie feststellt, daß Object ein bekannter Objekttyp ist, wird die Verarbeitung fortgesetzt. Ansonsten wird eine Exception vom Typ UnbekannterObjektTyp ausgelöst und diese an die aufrufende Stelle zurückgegeben. Listing 3.12: Beispiel zu dem Typ any 1: 2: 3: 4: 5: 6:
interface ObjektAnzeige { exception UnbekannterObjektTyp { string meldung; }; void ObjektAnzeigen(in any object) raises (UnbekannterObjektTyp); };
Der Pseudotyp TypeCode Zusammen mit dem Typ any tritt der Pseudotyp TypeCode auf. TypeCode ist kein IDL-Typ, sondern stellt einer CORBA-Anwendung Typinformation zur Verfügung. In den meisten Methodenaufrufen sind die Typen der übergebenen Parameter bekannt, da sie von der IDL-Methodensignatur angegeben werden. Wenn eine Methode als Parameter jedoch den Typ any akzeptiert, ist der aktuelle Typ dieses Objekts unbekannt. An dieser Stelle findet TypeCode Verwendung. Da mit jedem CORBA-Typ (sowohl Standardtypen wie long und string als auch benutzerdefinierte Typen wie Fernseher) ein eindeutiger Typcode verknüpft ist, kann eine Methodenimplementation feststellen, welcher Objekttyp über den Parameter any übergeben wurde. Nachdem der Objekttyp ermittelt wurde, kann die Methode mit dem Objekt weiterarbeiten. Stellen Sie sich TypeCode als eine Art Laufzeit-Typinformation für
CORBA-Anwendungen vor.
Zusammenfassung In diesem Kapitel wurden die grundlegenden Datentypen der CORBA-Programmiersprache Interface Definition Language (IDL), wie numerische Integer- und Gleitkommawerte, boolesche Werte sowie Zeichen und Zeichenketten, vorgestellt. Da viele dieser Datentypen sehr den Datentypen aus Programmiersprachen wie C, C++ und Java ähneln, werden Leser, die mit einer dieser Programmiersprachen vertraut sind, keine Probleme mit den IDL-Typen haben. Sie haben Ihr Wissen von IDL nun um die erweiterten Datentypen (insbesondere um die benutzerdefinierten Typen) und deren Verwendung ergänzt. Am wichtigsten ist, daß Sie mit dem Schnittstellen-Konstrukt vertraut sind und wissen, wie es das Verhalten von CORBA-Objekten definiert. Da interface einer der fundamentalen IDL-Datentypen ist, ist ein klares Verstehen dieses Konstrukts für den Entwurf von CORBA-Anwendungen wesentlich. Sie können ohne Schnittstellen keine CORBA-Anwendungen entwerfen oder implementieren. Sie haben heute einige nützliche IDL-Datentypen und -Konstrukte kennengelernt, insbesondere die Typen sequence und array, die zum Speichern von mehreren Werten eines ähnlichen Typs verwendet werden. Sie haben außerdem die Exceptions in IDL, einschließlich der vordefinierten StandardExceptions, kennengelernt. Abschließend haben Sie den Typ any untersucht, der einen Wert eines beliebigen IDL-Typen enthalten kann, und sein Gegenstück, den Pseudotyp TypeCode, mit dessen Hilfe Datentypen ermittelt werden können, die an eine Methode übergeben werden. Diese Konstrukte, insbesondere sequence und exception, sind wesentlich für die Erzeugung von stabilen CORBAAnwendungen. Sie haben nun praktisch alles behandelt, was es über IDL zu wissen gibt; nun ist es an der Zeit, dieses Wissen anzuwenden. Im nächsten Kapitel - »Tag 4, Erzeugen einer CORBA-Anwendung« - werden Sie genau dies bewerkstelligen, indem Sie IDL-Definitionen in funktionierende CORBA-Server und Client-Anwendungen übersetzen.
Fragen & Antworten Frage: Was ist der Zweck der Existenz sowohl des Typs sequence als auch des Typs array? Antwort: Array-Typen sind nützlich, wenn die Anzahl der Elemente in einem Array fest und bereits im Vorfeld bekannt ist. Oft ist dies nicht der Fall, da die Größe der Arrays sich ändert, wenn Elemente dynamisch hinzugefügt und entfernt werden. Konsequenterweise ist ein dynamisch größenveränderbares Array, wie der IDL-Typ sequence, oft wesentlich praktischer. Für eine größtmögliche Flexibilität bietet IDL beide Typen an.
Frage: Was sind all diese Exceptions und wann (bzw. wie) werden sie ausgelöst? Antwort: Zunächst einmal haben nicht-verteilte Methoden wenig Verwendung für viele der CORBAStandard-Exceptions. Sie machen jedoch sehr viel mehr Sinn in einer verteilten Umgebung. Wie zuvor erwähnt, werden die Standard-Exceptions grundsätzlich nicht in dem von Ihnen geschriebenen Quelltext ausgelöst, sondern sie werden automatisch durch den ORB ausgelöst, wenn eine Fehlerbedingung eintritt. Denken Sie daran, daß alle IDL-Methoden implizit diese Exceptions auslösen, sogar die vom IDL-Compiler für jedes Attribut erzeugten Datenzugriffs- und Datenänderungsmethoden (Accessor- und Mutator-Methoden). Frage: Warum wird der Datentyp »...« (fügen Sie hier Ihren bevorzugten, einfachen Datentypen ein) nicht unterstützt? Antwort: Da eines der Hauptziele von IDL in der Sprachneutralität (Implementationsunabhängigkeit) besteht, ist es nicht sinnvoll, alle einfachen Datentypen aller Sprachen zu unterstützen. Anstatt zu versuchen, diese (kaum zu erreichende) Ebene der Unterstützung zu erreichen, stellt IDL die gebräuchlichsten und nützlichsten einfachen Datentypen zur Verfügung. Frage: Wie ändert ein Objekt eines seiner Attribute, wenn dieses Attribut als readonly deklariert wurde? Antwort: Wenn Sie sich Fragen wie diese stellen, denken Sie ganz eindeutig voraus. Wenn Sie die Implementierung von IDL-Schnittstellen am 4. Tag lernen, wird klar werden, wie dies bewerkstelligt wird. Im Moment sollte die Information ausreichen, daß das Objekt, welches eine Schnittstelle implementiert, vollen Zugriff auf seinen eigenen Status hat. Ein readonly-Attribut wird einer Methode dieses Objekts zugeordnet, und diese Methode kann jeden beliebigen, vom Objekt gewünschten Wert zurückgeben. In der Regel wird das implementierende Objekt eigene aktuelle Attribute definieren, die den Attributen in der IDL-Schnittstelle entsprechen, obwohl dies nicht unbedingt erforderlich ist. Frage: Da Java keine Mehrfachvererbung unterstützt, wie wird Mehrfachvererbung von IDL-Schnittstellen in
Java erzielt? Antwort: Wenn Sie mit der Implementierung von IDL-Schnittstellen in Java beginnen, werden Sie feststellen, daß die IDL-Sprachabbildung für Java IDL-Schnittstellen in Java-Schnittstellen abbildet. Obwohl Mehrfachvererbung von Klassen in Java nicht unterstützt wird, wird Mehrfachvererbung von Schnittstellen unterstützt.
Workshop Der folgende Abschnitt wird Ihnen dabei helfen zu überprüfen, wie gut Sie das in diesem Kapitel vorgestellte Material verstanden haben und das Gelernte in die Praxis umzusetzen. Die Antworten auf die Quizfragen und die Auflösung der Übungen finden Sie in Anhang A. Quiz 1. Definieren Sie einen Typ (unter Verwendung von typedef) mit der Bezeichnung temperaturSequenz, der eine Sequenz in Form von sequence of floats darstellt. (Ja, dies ist erlaubt.) 2. Warum könnte ein Typ, wie in der vorherigen Frage beschrieben, sinnvoll sein? 3. Wozu sind Exceptions hilfreich? 4. Wozu ist das Konstrukt module hilfreich? 5. Benennen Sie einige Einsatzmöglichkeiten für den Datentyp octet. 6. Definieren Sie einen Aufzählungstyp, der die Monate eines Jahres enthält. 7. Warum könnte eine nicht-blockierende Methode im Vergleich zu einer blockierenden Methode von Vorteil sein? 8. Stellen Sie sich einen zusammengesetzten Datentyp mit einer großen Anzahl von Datenelementen vor. Dieser Datentyp wird häufig von der Client-Anwendung verwendet werden, die im allgemeinen auf alle Datenelemente zugreifen muß. Wäre es effizienter, diesen Datentyp in einem struct oder in einer Schnittstelle zu verkapseln? Begründen Sie Ihre Antwort. 9. Eine IDL-Methode kann einen Wert zurückgeben. Was ist jedoch der Zweck der Parametertypen in und inout? 10. Warum ist eine oneway-Methode nicht in der Lage, einen Wert an die aufrufende Stelle zurückzuliefern? Können Sie sich einen Mechanismus vorstellen, der oneway-Aufrufe verwendet, um der aufrufenden Stelle ein Ergebnis zu übergeben? Übungen 1. Nehmen Sie folgende Klassen an: Leitungsrohr, Wasserhahn, Sicherungskasten, Steckdose, Rohr, Wassererhitzer, Wasserpumpe, Leitung. Wie würden Sie diese Klassen partitionieren? Welche Verbindung, falls überhaupt, liegt zwischen den von Ihnen erzeugten Partitionen vor? 2. Erzeugen Sie eine Schnittstelle, die einen Radiowecker beschreibt (mit dem man die Stunden, Minuten, Weckzeit und so weiter setzen kann).
Ein Imprint des Markt&Technik Buch- und Software-Verlag GmbH. Elektronische Fassung des Titels: Corba in 14 Tagen, ISBN: 3-8272-2031-9
Tag 4 Entwicklung einer CORBA-Anwendung Entwicklung eines CORBA-Servers Implementierung der Server-Schnittstellen Entwicklung eines CORBA-Clients Zusammenfassung Fragen & Antworten Workshop
Bis hierher habe ich Ihnen vor allem Kenntnisse zur Interface Definition Language (IDL) von CORBA vermittelt. Sie haben gelernt, daß die IDL zur Definition der Schnittstellen von CORBAObjekten verwendet wird, und Sie haben sogar schon eigene IDL-Schnittstellen definiert. Jetzt ist es an der Zeit, daß Sie Ihre IDL-Kenntnisse weiter in die Praxis umsetzen, und zwar nicht nur zur Definition von Schnittstellen, sondern auch zur Implementierung dieser Schnittstellen im Rahmen der Entwicklung eines CORBA-Servers. Hierzu werden Sie die folgenden Hauptschritte durchführen: 1. Definieren der Server-Schnittstellen mit Hilfe der IDL. 2. Wahl eines Implementierungsansatzes für die Server-Schnittstellen. (Sie werden sehen, daß CORBA hierzu zwei Ansätze anbietet: Vererbung und Delegation.) 3. Generieren von Client-Stubs und Server-Skeletons für die Server-Schnittstellen mit Hilfe des IDL-Compilers. (Zunächst werden Sie nur mit den Server-Skeletons zu tun haben.) 4. Implementieren der Server-Schnittstellen.
5. Kompilieren der Server-Anwendung. 6. Ausführen der Server-Anwendung. (Mit ein wenig Glück wird alles auf Anhieb klappen!) Anschließend werden Sie einen Client entwickeln, der die im ersten Teil dieses Kapitels implementierten Dienste nutzt. Hinsichtlich der Konzeption ist der Prozeß der Erstellung eines Clients sehr viel einfacher, denn Sie brauchen nur festzulegen, welche Aufgaben der Client ausführen soll, die geeigneten Client-Stubs für die zu verwendenden Server-Objekttypen mit aufzunehmen und schließlich die Client-Funktionen zu implementieren. Und schon ist alles für die Kompilierung und Ausführung des Clients bereit.
Entwicklung eines CORBA-Servers Der erste Schritt bei der Entwicklung einer CORBA-Anwendung besteht normalerweise in der Implementierung der Server-Funktionen. Dies hat den Grund, daß zwar ein Server (zumindest in beschränktem Maße) auch ohne einen Client getestet werden kann, es jedoch im allgemeinen ungleich schwieriger ist, einen Client ohne einen funktionierenden Server zu testen. Es gibt natürlich auch Ausnahmen von dieser Regel, aber normalerweise müssen Sie zumindest die Server-Schnittstellen definiert haben, bevor Sie den Client implementieren können. Damit das Arbeiten nicht zu kompliziert wird, werden im Rahmen dieses Buches zunächst die Server-Funktionen und anschließend die Client-Funktionen implementiert. Zur Erstellung eines Servers müssen Sie zunächst die Server-Schnittstellen definieren (über diese wird festgelegt, welche Funktionen der Server zur Verfügung stellt und wie der Zugriff auf diese Funktionen erfolgen soll), anschließend diese Schnittstellen implementieren und zum Schluß den Server kompilieren und ausführen. Im Verlauf dieser Arbeiten werden an der einen oder anderen Stelle Fragen auftauchen, die ich in diesem Abschnitt bereits behandeln möchte. Definition der Server-Schnittstellen Für diese Aufgabe können Sie erstmals Ihre am 3. Tag erworbenen Kenntnisse einsetzen. Sie haben in diesem Kapitel viel über IDL gelernt, hatten aber noch keine echte Gelegenheit, dieses Wissen anzuwenden. Mit Hilfe dieser Kenntnisse können Sie quasi aus dem Nichts einen Systementwurf erstellen. Einige Worte zum Systementwurf Es versteht sich von selbst, daß zur Entwicklung eines Systems zunächst eine Grundvorstellung über die Aufgaben des Systems vorhanden sein muß. Bevor Sie auch nur eine einzige IDL-Zeile schreiben können, müssen Sie sich darüber im klaren sein, was Sie mit dem System erreichen möchten. Obwohl die umfassende Behandlung des Themas Systementwurf den Rahmen dieses Buches bei weitem sprengen würde, werden Sie am 5. Tag einige der Grundlagen zum Entwerfen und Abbilden eines Systementwurfs in IDL kennenlernen. Im vorliegenden Kapitel werden Sie von dieser Aufgabe entlastet, im wahren »Programmiererleben« können Sie kaum mit so etwas rechnen. Das Beispiel eines Börsen-Servers
An dieser Stelle möchte ich Ihnen den Vorgang zur Implementierung eines CORBA-Servers anhand eines einfachen Beispiels näherbringen. Bei einem komplexeren Beispiel würde der Lernerfolg vermutlich durch das Labyrinth der einzelnen Implementierungsdetails gefährdet, daher werden wir es uns für später aufheben. Stellen Sie sich eine Börse vor: Dort benötigt man einen Dienst, der bei Angabe einer bestimmten Aktie deren Wert zum momentanen Zeitpunkt ausgibt. Als zusätzliche Leistung gibt der Dienst auf Anforderung auch eine Liste aller bekannten Aktien aus. Eine flüchtige Analyse dieses Szenarios ergibt, daß eine Schnittstelle mit dem Namen BoersenServer definiert werden könnte, die zwei Dienste (Methoden) zur Verfügung stellt: getAktienWert() und getAktienSymbole(). Die Methode getAktienWert() müßte den Parameter AktienSymbol für das Aktiensymbol annehmen und als Ergebnis einen Gleitkommawert liefern. (Ein Wert vom Typ float müßte hierfür ausreichen.) getAktienSymbole() benötigt keine Parameter und soll eine Liste der AktienSymbol-Objekte liefern. Während der Festlegung der Systemfunktionalität für BoersenServer wurde unversehens die Klasse AktienSymbol erzeugt. Diese spontane Klassengenerierung, die häufig als Nebeneffekt einer objektorientierten Analyse abläuft, kann das Verständnis für die Abläufe in einem bestimmten System fördern.
Listing 4.1: BoersenMarkt.idl. 1: // BoersenMarkt.idl 2: 3: // Das Modul BoersenMarkt besteht aus Definitionen, die zum Erstellen 4: // börsenorientierter Anwendungen geeignet sind. 5: module BoersenMarkt { 6: 7: // Der Typ AktienSymbol wird für Symbole (Namen) 8: // von Aktien verwendet. 9: typedef string AktienSymbol; 10: 11: // Eine AktienSymbolListe ist einfach eine Sequenz 12: // von AktienSymbolen. 13: typedef sequence AktienSymbolListe; 14: 15: // Die Schnittstelle BoersenServer ist die Schnittstelle für einen Server, 16: // der Börseninfos zur Verfügung stellt.
17: // (Weitere Informationen hierzu finden Sie in den Kommentaren 18: // zu den einzelnen Methoden.) 19: interface BoersenServer { 20: 21: // getAktienWert() liefert den aktuellen Wert eines 22: // AktienSymbols. Ist das angegebene 23: // AktienSymbol unbekannt, ist das Ergebnis undefiniert (dies wäre eine 24: // gute Stelle, um eine Exception auszulösen). 25: float getAktienWert(in AktienSymbol symbol); 26: 27: // getAktienSymbole() gibt eine Sequenz mit allen 28: // AktienSymbolen zurück, die diesem BoersenServer bekannt sind. 29: AktienSymbolListe getAktienSymbole(); 30: }; 31: }; Die Abbildung dieses speziellen Entwurfs in IDL ist ein eindeutiger Prozeß, dessen Endergebnis, BoersenMarkt.idl, in Listing 4.1 zu sehen ist. Da es sich empfiehlt, zusammengehörende Schnittstellen und Typen in IDL-Modulen zu gruppieren, sollten Sie zunächst alle Definitionen in ein Modul mit dem Namen BoersenMarkt: module BoersenMarkt { aufnehmen. Befassen Sie sich dann damit, wie die Klasse AktienSymbol eingesetzt werden soll. Im Rahmen dieses Beispiels benötigt diese Klasse keine über den Typ string hinausgehende Funktionalität. Daher können Sie entweder den Typ AktienSymbol an allen Stellen durch den Typ string ersetzen oder AktienSymbol mit Hilfe von typedef als String definieren. In einem komplexen System erleichtert die Verwendung von speziellen Datentypen wie AktienSymbol das Verständnis für den Systementwurf. Wenden Sie diese Vorgehensweise gleich an, indem Sie mit Hilfe von typedef den speziellen Typ AktienSymbol als Zeichenkette definieren: typedef string AktienSymbol; Jetzt können Sie als nächstes die Schnittstelle für das Objekt BoersenServer definieren: interface BoersenServer { Die erste Methode in BoersenServer erhält als Eingabe den Typ AktienSymbol und liefert als Ausgabe einen Wert des Typs float. Diese Festlegung kann in IDL ohne Probleme folgendermaßen ausgedrückt werden: float getAktienWert(in AktienSymbol symbol);
Es ist vorstellbar, daß ein Client die Methode getAktienWert() mit einem ungültigen Namen für AktienSymbol aufruft. In einem solchen Fall könnte (und müßte) getAktienWert() bei der Übergabe des ungültigen Namens eine Exception auslösen. Aus Gründen der Einfachheit werden wir im vorliegenden Beispiel jedoch nicht mit Exceptions arbeiten.
Die andere BoersenServer-Methode erhält keine Parameter und gibt eine Liste von AktienSymbolWerten zurück. Hier sei daran erinnert, daß die IDL zwei Konstrukte zur Darstellung von Listen anbietet: Sequenzen und Arrays. Da in diesem Fall die Größe der auszugebenden Liste unbekannt ist, wäre die Verwendung einer Sequenz vorteilhaft. Eine Methode kann jedoch eine Sequenz (sequence) nicht direkt zurückgeben. Zunächst muß mit typedef eine AktienSymbol-Sequenz für diese Methode definiert werden. Aus praktischen Gründen empfiehlt es sich, die typedef-Definition unmittelbar nach der typedef-Definition für den Typ AktienSymbol einzubauen: typedef sequence AktienSymbolListe; Jetzt können Sie die Methode getAktienSymbole() zur BoersenServer-Schnittstelle hinzufügen. Diese Methode wird in der IDL folgendermaßen beschrieben: AktienSymbolListe getAktienSymbole(); Dies ist der ganze IDL-Quelltext, den Sie für dieses Beispiel benötigen. Ausgestattet mit der Datei BoersenMarkt.idl sind Sie nun bereit für den nächsten Schritt, nämlich die Entscheidung, wie diese IDL-Definitionen implementiert werden sollen. Wahl eines Ansatzes für die Implementierung Bevor Sie die Server-Funktion tatsächlich implementieren, müssen Sie zunächst über den Ansatz für die Implementierung entscheiden. CORBA unterstützt zwei Mechanismen für die Implementierung von IDL-Schnittstellen. Entwickler, die sich mit objektorientierten Konzepten bereits auskennen, dürften diese Mechanismen oder zumindest deren Namen kennen. Es handelt sich hierbei um den Vererbungsmechanismus, bei dem eine Klasse eine Schnittstelle durch Vererbung der Methoden aus der betreffenden Schnittstellenklasse implementiert, und um den Delegationsmechanismus, bei dem die Methoden der Schnittstellenklasse die Methoden der implementierenden Klasse aufrufen (die Implementierung an diese Methoden delegieren). Diese Konzepte sind in den Abbildungen 4.1 bzw. 4.2 dargestellt. Abb. 4.2 zeigt ferner, daß eine bindende Klasse die Methoden einer beliebigen Klasse oder keiner Klasse erben kann - ganz im Gegensatz zum Vererbungsansatz, bei dem die Implementierungsklasse die Methoden von der Schnittstellenklasse erben muß, die sie implementieren soll.
Implementierung durch Vererbung ist ein Implementierungsmechanismus, bei dem eine Basisklasse die Schnittstellen eines bestimmten Objekts definiert und eine gesonderte Klasse, die die Definitionen von dieser Klasse erbt und die tatsächliche Implementierung dieser Schnittstellen darstellt.
Implementierung durch Delegation ist ein Implementierungsmechanismus, bei dem eine Klasse die Schnittstellen für ein Objekt definiert und anschließend ihre Implementierung an eine andere Klasse bzw. an andere Klassen delegiert. Der Hauptunterschied dieses Ansatzes zum Vererbungsmechanismus besteht darin, daß hier die Implementierungsklassen nicht von einer bestimmten Klasse abgeleitet sein müssen. Eine bindende Klasse (Tie Class) ist diejenige Klasse, an die die Implementierungen beim Delegationsansatz delegiert werden. Daher wird dieser Ansatz häufig einfach als Binden bezeichnet. Die meisten IDL-Compiler akzeptieren Befehlszeilenparameter zur Festlegung, für welchen Implementierungsansatz Quelltext generiert werden soll. Bevor Sie also mit dem IDL-Compiler Quelltext aus Ihren IDL-Definitionen generieren, müssen Sie den zu verwendenden Ansatz festlegen. Der Dokumentation zu Ihrem IDL-Compiler können Sie entnehmen, welche Befehlszeilenparameter dieser ggf. erwartet.
Abbildung 4.1:
Implementierung durch Vererbung
Abbildung 4.2: Implementierung durch Delegation Wie wählt man den geeigneten Implementierungsansatz? Sie werden sich nun sicherlich fragen, wie Sie den geeigneten Ansatz zur Implementierung auswählen sollen. In vielen Fällen können Sie sich dabei nach Ihrer persönlichen Vorliebe richten. Es gibt jedoch auch Fälle, in denen ein bestimmter Ansatz besser geeignet ist als der andere. Hierbei ist beispielsweise zu bedenken, daß die Implementierungsklasse von einer vom IDL-Compiler zur Verfügung gestellten Klasse abgeleitet wird. Wenn eine Anwendung zur Implementierung einer Schnittstelle bereits vorhandenen, älteren Quelltext (Legacy-Code) nutzt, wäre es unpraktisch, die Klassen in diesem vorhandenen Quelltext zu ändern, damit diese die Methoden einer vom IDLCompiler generierten Klasse erben können. Bei einer solchen Anwendung wäre es daher sinnvoller, den Delegationsansatz zu verwenden, denn vorhandene Klassen können problemlos in bindende Klassen umgewandelt werden.
Nachdem Sie nun den Ansatz zur Implementierung gewählt und einen großen Teil des Quelltextes geschrieben haben, sollten Sie sinnvollerweise beim für diesen Server gewählten Ansatz bleiben. Zwar ist es möglich, von einem Implementierungsansatz zum anderen zu wechseln, doch handelt es sich hierbei um einen recht mühevollen Prozeß, wenn bereits ein großer Teil des Quelltextes geschrieben wurde. Diese Problematik stellt sich nicht sehr oft, aber Sie sollten den Aspekt nicht ganz außer acht lassen.
Für das vorliegende Beispiel ist jeder der beiden Implementierungsansätze geeignet, tatsächlich werden wir hier den Delegationsmechanismus verwenden. Die Implementierung des Servers mit Hilfe der Vererbung bleibt Ihnen als Übung vorbehalten. Beachten Sie, daß die beiden Implementierungsansätze innerhalb einer einzigen Server-Anwendung nebeneinander eingesetzt werden können. Zwar wählen Sie pro Schnittstelle jeweils nur einen einzigen Ansatz, doch können Sie für die einzelnen Schnittstellen im System unterschiedliche Ansätze verwenden. Wenn Sie sich beispielsweise grundsätzlich für den Vererbungsansatz als den am besten geeigneten Mechanismus entschieden haben, aber gleichzeitig einige ältere, bereits im Einsatz befindliche Klassen einsetzen, die den Bindungsansatz erfordern, können Sie diesen Ansatz für die älteren Klassen und den Vererbungsansatz für alle übrigen Klassen wählen. Verwendung des IDL-Compilers Nachdem Sie die IDL-Definitionen für die Objektschnittstellen des Systems erstellt und den Implementierungsansatz gewählt haben, ist nun alles für die Kompilierung der IDL-Datei bzw. Dateien (in einem komplexeren System) vorbereitet. Die Methode und die Befehlszeilenparameter zum Aufrufen des IDLCompilers unterscheiden sich von Plattform zu Plattform und von Produkt zu Produkt. Nähere Anweisungen hierzu entnehmen Sie der Produktdokumentation Ihres IDL-Compilers.
Denken Sie daran, daß für dieses Beispiel der Delegationsansatz (auch Binden genannt) verwendet werden soll, daher benötigen Sie aus der Dokumentation zum IDL-Compiler die geeigneten Befehlszeilenparameter (sofern erforderlich) zum korrekten Generieren von Dateien und Quelltext. Zum Aufrufen des IDL-Compilers, der mit der Java-IDL von Sun geliefert wird, müssen Sie beispielsweise folgenden Befehl eingeben: idltojava -fno-cpp -fclient -fserver BoersenMarkt.idl In diesem Fall heißt der IDL-Compiler idltojava. Der Schalter fno-cpp bewirkt, daß der IDL-Compiler den C-Präprozessor nicht vor der Kompilierung der Datei aufruft. Die Schalter -fclient und -fserver
weisen den IDL-Compiler an, Client-Stubs bzw. Server-Skeletons zu generieren. Zunächst bräuchte der Schalter -fclient noch nicht mit eingegeben zu werden, weil jetzt nur der Server implementiert werden soll, aber da Sie die Client-Stubs später auch noch brauchen werden, sparen Sie Zeit, wenn Sie sie bei dieser Gelegenheit gleich mit generieren. Der Befehl zum Aufrufen des in VisiBroker/C++ für Windows 95 von Visigenic enthaltenen IDLCompilers lautet folgendermaßen: orbeline -h h BoersenMarkt.idl Mit diesem Befehl generiert der IDL-Compiler namens orbeline Client-Stubs und Server-Skeletons für die Datei BoersenMarkt.idl. Der Schalter -c cpp diente dazu, daß der Compiler die Dateinamenerweiterung .cpp für C++-Quelltextdateien verwendet; analog dazu bewirkt hier der Schalter -h h, daß der Compiler die Dateinamenerweiterung.h für Header-Dateien verwendet. Selbstverständlich können Sie die hier genannten Dateinamenerweiterungen durch die von Ihnen bevorzugten ersetzen. Wie für jedes andere Dienstprogramm, das von der Befehlszeile aus gestartet wird, müssen Sie auch vor der Ausführung des IDL-Compilers die PATH-Variable in Ihrer Systemumgebung so ändern, daß das Verzeichnis, in dem sich der IDL-Compiler befindet, darin enthalten ist. Die Dokumentation zu Ihrem CORBA-Produkt enthält normalerweise Informationen darüber, wie die gewünschten Verzeichnisse in die PATHVariable eingetragen werden.
Client-Stubs und Server-Skeletons Beim Aufrufen des IDL-Compilers wird entsprechend der Sprachabbildung, die vom betreffenden Produkt unterstützt wird, Quelltext generiert. Der IDL-Compiler generiert eine Anzahl von Dateien, bei denen es sich um drei verschiedene Typen handelt: Hilfsklassen, Client-Stub-Klassen und ServerSkeleton-Klassen.
Am 2. Tag haben Sie gelernt, daß die Client-Stubs für eine Schnittstelle kleine Quelltextabschnitte sind, die mit den Client-Anwendungen kompiliert werden, welche diese Schnittstelle verwenden. Diese Stubs dienen nur dazu, den ORB des Clients anzuweisen, die Formatübertragung für abgehende und ankommende Parameter durchzuführen. Analog dazu handelt es sich bei den Server-Skeletons um Quelltextsegmente, die das Gerüst für den Server bilden. Diese Skeletons übergeben ankommende Parameter an den von Ihnen, dem Entwickler, geschriebenen Implementierungsquelltext und leiten die abgehenden Parameter wieder an den Client zurück.
Die Namen der vom IDL-Compiler generierten Dateien hängen von der verwendeten Sprachabbildung und manchmal auch von den an den IDL-Compiler übergebenen Befehlszeilenparametern ab. (Beispielsweise akzeptieren einige IDL-Compiler Schalter, mit denen zum Klassennamen hinzuzufügende Präfixe und Suffixe festgelegt werden.) Der Inhalt dieser Dateien ist im wesentlichen vom verwendeten IDL-Compiler unabhängig (vorausgesetzt, die betreffenden Produkte verwenden die Standard-Sprachabbildungen). Beispiel: Die Ausgabe des in Orbix/C++ von IONA enthaltenen IDL-Compilers unterscheidet sich nicht wesentlich von der Ausgabe des in VisiBroker/C++ von Visigenic enthaltenen IDL-Compilers. Auch die entsprechenden Java-Produkte liefern annähernd den gleichen Quelltext. Genaugenommen ist die Bezeichnung »IDL-Compiler« nicht ganz korrekt, denn während ein Compiler normalerweise Quelltext in Objektcode umwandelt, hat der IDL-Compiler mehr die Funktion eines Übersetzers. Er übersetzt IDL-Quelltext in C++-Quelltext oder in JavaQuelltext usw. Der generierte Quelltext wird dann zusammen mit den vom Entwickler zur Verfügung gestellten Implementierungen vom C++Compiler oder vom Java-Compiler usw. tatsächlich kompiliert.
Implementierung der Server-Schnittstellen Nachdem Sie mit dem IDL-Compiler erfolgreich Server-Skeletons und Client-Stubs für Ihre Anwendung generiert haben, ist nun alles für die Implementierung der Server-Schnittstellen bereit. Der IDL-Compiler generiert zu jeder IDL-Schnittstelle eine Quelltextdatei und eine Header-Datei für den Client-Stub sowie eine Quelltextdatei und eine Header-Datei für das Server-Skeleton, so daß pro Schnittstelle vier Dateien erzeugt werden. (Dies gilt für einen IDL-Compiler, der den IDL-Quelltext in C++-Quelltext übersetzt; bei einer Übersetzung in Java-Quelltext werden natürlich keine HeaderDateien generiert.) Ferner kann der IDL-Compiler gesonderte Verzeichnisse für die IDL-Module und zusätzliche Dateien für Hilfsklassen erstellen. Auch bieten die meisten IDL-Compiler die Möglichkeit, die Datennamenerweiterung für Client-Stubs und Server-Skeletons anzugeben. Beispielsweise könnten die Client-Stub-Dateien BoersenMarkt_c.h und BoersenMarkt_c.cpp oder
BoersenMarkt_st.h und BoersenMarkt_st.cpp genannt werden. In der Dokumentation zu Ihrem IDLCompiler können Sie nachlesen, welche Typen von Dateien erzeugt werden, welche Dateinamen verwendet werden und wie Sie die Standard-Dateinamenerweiterungen für die Dateinamen ändern können. Damit unser Beispiel möglichst einfach bleibt, werden wir Java als Implementierungssprache verwenden. Java ist für dieses Beispiel besonders gut geeignet, weil sich diese Sprache dadurch auszeichnet, daß sie vor allem bei der Entwicklung von CORBA-Anwendungen relativ einfach handzuhaben ist. Von allen häufig zur Entwicklung von CORBA-Anwendungen verwendeten Sprachen stellt Java dem Entwickler wohl die wenigsten Hindernisse in den Weg und ist daher die Sprache der Wahl für ein einführendes Beispiel. Im weiteren Verlauf dieses Buches wird vor allem mit C++-Beispielquelltext gearbeitet werden, mit Ausnahme der Java-spezifischen Kapitel 13 und 14. Verwendung von Server-Skeletons Das Server-Skeleton stellt, wie Sie bereits gelernt haben, ein Gerüst für die Server-Implementierung dar. Bei C++ besteht ein Server-Skeleton aus einer Gruppe von Klassen, die rein virtuelle Methoden (Methoden ohne Implementierung) oder Methoden für die Delegation an Methoden in anderen Klassen (die zuvor behandelten bindenden Klassen) enthalten. Als Entwickler liefern Sie dazu die Implementierung für diese Methoden. Bei Java vereint ein Server-Skeleton eine Gruppe von Hilfsklassen mit einer Schnittstelle, für die wiederum Sie als Entwickler die Implementierung liefern. Wenn Sie die Server-Skeletons für Ihre Anwendung mit Hilfe des Java-IDL-Compilers von Sun erzeugen, werden Sie feststellen, daß der Compiler entsprechend dem Namen des in BoersenMarkt.idl definierten IDL-Moduls ein Verzeichnis namens BoersenMarkt anlegt. In diesem Verzeichnis befinden sich eine Reihe von Dateien, die die Definitionen für die Client-Stubs und die ServerSkeletons enthalten: AktienSymbolHelper.java AktienSymbolListeHolder.java AktienSymbolListeHelper.java BoersenServer.java BoersenServerHolder.java BoersenServerHelper.java _BoersenServerStub.java _BoersenServerImplBase.java Jetzt werden Sie sich lediglich mit dem Server-Skeleton-Teil dieser Dateien beschäftigen. Beachten Sie, daß die Java-Schnittstelle, die die BoersenServer-Dienste beschreibt, in der Datei BoersenServer.java enthalten ist. Ihr Inhalt ist in Listing 4.2 dargestellt. Die Datei BoersenServerImplBase.java enthält eine Hilfsklasse, aus der Sie Ihre Server-Implementierungsklasse ableiten werden. Sie brauchen sich mit dem Inhalt der Datei nicht zu befassen, da die darin definierten Funktionen für Sie im Verborgenen arbeiten. Listing 4.2: BoersenServer.java.
1: /* 2: * File: ./BoersenMarkt/BoersenServer.java 3: * From: BoersenMarkt.idl 4: * Date: Mon Jul 21 16:12:26 1997 5: * By: D:\BIN\DEVEL\JAVA\JAVAIDL\BIN\IDLTOJ~1.EXE JavaIDL 6: * Thu Feb 27 11:22:49 1997 7: */ 8: 9: package BoersenMarkt; 10: public interface BoersenServer 11: extends org.omg.CORBA.Object { 12: float getAktienWert(String symbol) 13: ; 14: String[] getAktienSymbole() 15: ; 16: } Bei näherer Betrachtung von Listing 4.2 sehen Sie, daß sich die BoersenServer-Schnittstelle im Package BoersenMarkt befindet. Ferner ist zu erkennen, daß diese Schnittstelle eine Erweiterung der Schnittstelle org.omg.CORBA.Object darstellt. Alle Schnittstellen für CORBA-Objekte sind Erweiterungen dieser Schnittstelle, aber Sie brauchen sich auch mit dem Inhalt dieser Schnittstelle jetzt nicht zu befassen. Schließlich werden Sie noch bemerken, daß die BoersenServer-Schnittstelle zwei Methoden enthält, die den IDL-Methoden in BoersenServer.idl entsprechen. Die IDL-Typen wurden mit ihren Java-Gegenstücken abgebildet: Der Typ AktienSymbol, der mit typedef als IDLZeichenkette (String) definiert worden war, wird auf eine Java-Zeichenkette abgebildet, der Typ AktienSymbolListe, der als Sequenz (sequence) des Typs AktienSymbol definiert worden war, wird als ein Array von Java-Zeichenketten abgebildet. Der IDL-Typ float, wird, was keine Überraschung ist, als Java-Typ float abgebildet. Schreiben der Implementierung Die Implementierung der BoersenServer-Schnittstelle ist einfach. In diesem Abschnitt werden Sie die Implementierung Zeile für Zeile durcharbeiten (im Beispiel lautet der Klassenname BoersenServerImpl, Sie können die Klasse jedoch beliebig nennen), wobei für jeden Schritt erläutert wird, was dabei vor sich geht. Listing 4.3: BoersenServerImpl.java. 1: 2: 3: 4: 5: 6: 7:
// BoersenServerImpl.java package BoersenMarkt; import java.util.Vector; import org.omg.CORBA.ORB;
8: import org.omg.CosNaming.NameComponent; 9: import org.omg.CosNaming.NamingContext; 10: import org.omg.CosNaming.NamingContextHelper; 11: 12: // BoersenServerImpl implementiert die BoersenServer-IDLSchnittstelle. 13: public class BoersenServerImpl extends _BoersenServerImplBase implements 14: BoersenServer { 15: 16: // Aktiensymbole und deren entsprechende Werte. 17: private Vector meineAktienSymbole; 18: private Vector meineAktienWerte; 19: 20: // Die Zeichen, aus denen die Bezeichnungen der AktienSymbole generiert werden. 21: private static char unsereZeichen[] = { 'A', `B', `C', `D', `E', `F', 22: `G', `H', `I', `J', `K', `L', `M', `N', `O', `P', `Q', `R', 23: `S', `T', `U', `V', `W', `X', `Y', `Z' }; 24: 25: // Pfad für BoersenServer-Objekte. 26: private static String unserPfad = "BoersenServer"; 27: 28: // Erzeuge neue BoersenServerImpl. 29: public BoersenServerImpl() { 30: 31: meineAktienSymbole = new Vector(); 32: meineAktienWerte = new Vector(); 33: 34: // Die Symbole und die Werte mit Zufallswerten initialisieren. 35: for (int i = 0; i < 10; i++) { 36: 37: // Eine Zeichenkette aus vier zufälligen Zeichen erzeugen. 38: StringBuffer AktienSymbol = new StringBuffer(" "); 39: for (int j = 0; j < 4; j++) { 40: 41: AktienSymbol.setCharAt(j, unsereZeichen[(int)(Math.random() 42: * 26f)]); 43: } 44: 45: meineAktienSymbole.addElement(AktienSymbol.toString()); 46: 47: // Den Aktienwert als Zahl zwischen 0 und 100 angeben. In diesem 48: // Beispiel behält die Aktie diesen Wert für die Dauer der
49: // Anwendungsausführung bei. 50: meineAktienWerte.addElement(new Float(Math.random() * 100f)); 51: } 52: 53: // Die oben generierten Aktiensymbole ausgeben. 54: System.out.println("Generierte Aktiensymbole:"); 55: for (int i = 0; i < 10; i++) { 56: System.out.println(" " + meineAktienSymbole.elementAt(i) + " " + 57: meineAktienWerte.elementAt(i)); 58: } 59: System.out.println(); 60: } 61: 62: // Den aktuellen Wert für das jeweilige AktienSymbol zurückgeben. 63: public float getAktienWert(String symbol) { 64: 65: // Versuch, das angegebene Symbol zu finden. 66: int AktienIndex = meineAktienSymbole.indexOf(symbol); 67: if (AktienIndex != -1) { 68: 69: // Symbol gefunden; Wert wird zurückgegeben. 70: return ((Float)meineAktienWerte.elementAt(AktienIndex)). 71: floatValue(); 72: } else { 73: 74: // Symbol wurde nicht gefunden. 75: return 0f; 76: } 77: } 78: 79: // Eine Sequenz aller diesem BoersenServer bekannten Aktiensymbole zurückzugeben. 80: public String[] getAktienSymbole() { 81: 82: String[] symbols = new String[meineAktienSymbole.size()]; 83: meineAktienSymbole.copyInto(symbols); 84: 85: return symbols; 86: } 87: 88: // Ein BoersenServer-Objekt erzeugen und initialisieren. 89: public static void main(String args[]) { 90: 91: try {
92: 93: // Den ORB initialisieren. 94: ORB orb = ORB.init(args, null); 95: 96: // Ein BoersenServerImpl-Objekt erzeugen und 97: // beim ORB registrieren. 98: BoersenServerImpl BoersenServer = new BoersenServerImpl(); 99: orb.connect(BoersenServer); 100: 101: // Root-Bezeichnungskontext abrufen. 102: org.omg.CORBA.Object obj = orb. 103: resolve_initial_references("NameService"); 104: NamingContext namingContext = NamingContextHelper.narrow(obj); 105: 106: // Die BoersenServer-Objektreferenz im 107: // Bezeichnungskontext binden. 108: NameComponent nameComponent = new NameComponent(unserPfad, 109: ""); 110: NameComponent path[] = { nameComponent }; 111: namingContext.rebind(path, BoersenServer); 112: 113: // Auf Aufrufe von Clients warten. 114: java.lang.Object warteAufMich = new java.lang.Object(); 115: synchronized (warteAufMich) { 116: warteAufMich.wait(); 117: } 118: } catch (Exception ex) { 119: System.err.println("BoersenServer konnte nicht gebunden werden: " + ex. 120: getMessage()); 121: } 122: } 123: } Der gesamte Quelltext für BoersenServerImpl.java ist in Listing 4.3 dargestellt. Im weiteren Verlauf dieses Abschnitts werden Sie die Datei Schritt für Schritt durcharbeiten, so daß Sie die Vorgänge in allen Einzelheiten mitverfolgen können. package BoersenMarkt; Da die BoersenServer-Schnittstelle Teil des Moduls BoersenMarkt ist, werden die Java-Klassen- und Schnittstellendefinitionen in das package (Paket) BoersenMarkt aufgenommen. Aus praktischen Gründen wird auch BoersenServerImpl in dieses Package aufgenommen. (Wenn Sie sich mit Java oder mit dieser Art von Packages nicht auskennen, können Sie diesen Quelltextabschnitt für den Augenblick auch ignorieren, ohne daß Ihnen etwas entgeht.)
import java.util.Vector; BoersenServerImpl verwendet die Klasse Vector. Die Anweisung import müßte einem JavaEntwickler bereits bekannt vorkommen. Wenn Sie mit Java nicht vertraut sind, sei gesagt, daß sich die Anweisung import weitgehend wie die Präprozessor-Direktive #include in C++ verhält; die Klasse java.util.Vector ist eine Container-Klasse, die als erweiterbares Array von Elementen definiert ist. import import import import
org.omg.CORBA.ORB; org.omg.CosNaming.NameComponent; org.omg.CosNaming.NamingContext; org.omg.CosNaming.NamingContextHelper;
Die hier importierten Klassen werden in CORBA-Anwendungen häufig verwendet. Die erste stellt erkennbar die ORB-Funktionalität zur Verfügung, die anderen Klassen gehören zum CORBABezeichnungsdienst, den Sie am 12. Tag näher kennenlernen werden. // BoersenServerImpl implementiert die IDL-BoersenServerSchnittstelle. public class BoersenServerImpl extends _BoersenServerImplBase implements BoersenServer { Im Falle der IDL-Schnittstelle für BoersenServer generiert der IDL-Compiler eine Klasse mit dem Namen BoersenServerImplBase und eine Schnittstelle mit dem Namen BoersenServer. Zur Implementierung der IDL-Schnittstelle für BoersenServer muß die Klasse BoersenServerImpl die Klasse _BoersenServerImplBase erweitern und BoersenServer implementieren. Genau dies wird durch die folgenden Zeilen deklariert: // Aktiensymbole und die zugehörigen Werte. private Vector meineAktienSymbole; private Vector meineAktienWerte; BoersenServerImpl speichert die Aktiensymbole und ihre Werte in der Klasse Vector. // Die Zeichen, aus generiert werden. private static char `F', `G', `H', `I', `J', `S', `T', `U', `V',
denen die Bezeichnungen der AktienSymbole unsereZeichen[] = { `A', `B', `C', `D', `E', `K', `L', `M', `N', `O', `P', `Q', `R', `W', `X', `Y', `Z' };
Das Array unsereZeichen enthält die Zeichen, aus denen Aktiensymbole zusammengesetzt sind. // Pfad für BoersenServer-Objekte.
private static String unserPfad = "BoersenServer"; Die Variable unserPfad dient zum Speichern des Pfads, über den dieses Objekt der Klasse BoersenServer im Bezeichnungsdienst gesucht werden kann. Hier könnten Sie auch einen beliebigen Namen angeben, aber für dieses Beispiel wird BoersenServer verwendet. // Eine neue BoersenServerImpl-Klasse erzeugen. public BoersenServerImpl() { meineAktienSymbole = new Vector(); meineAktienWerte = new Vector(); Obwohl Konstruktoren kein Bestandteil einer IDL-Schnittstelle sind, verfügt die Implementierungsklasse für diese Schnittstelle dennoch über Konstruktoren, so daß der Server die Implementierungsobjekte erzeugen kann. BoersenServerImpl verfügt nur über einen Standardkonstruktor, aber eine Klasse, die eine IDL-Schnittstelle implementiert, kann - wie jede beliebige andere Klasse - eine beliebige Anzahl von Konstruktoren aufweisen. Der folgende Teil des Konstruktors erzeugt Vektoren, in denen dann die Aktiensymbole und die dazugehörigen Werte gespeichert werden. // Die Symbole und die Werte mit Zufallswerten initialisieren. for (int i = 0; i < 10; i++) { Die Klasse BoersenServerImpl erzeugt zehn zufällige Aktiensymbole. // Eine Zeichenkette aus vier zufälligen Zeichen erzeugen. StringBuffer AktienSymbol = new StringBuffer(" "); for (int j = 0; j < 4; j++) { AktienSymbol.setCharAt(j, unsereZeichen[(int)(Math.random() * 26f)]); } meineAktienSymbole.addElement(AktienSymbol.toString()); Für jedes Aktiensymbol erzeugt die Klasse BoersenServerImpl eine Zeichenkette aus vier zufälligen Zeichen (die aus dem vorangehenden Array unsereZeichen ausgewählt werden). Die Länge vier Zeichen wurde wie die Anzahl der Symbole willkürlich ausgewählt. Der Einfachheit halber wird keine Prüfung auf doppelt vorhandene Zeichenketten durchgeführt. // Den Aktienwert als Zahl zwischen 0 und 100 angeben. In diesem // Beispiel behält die Aktie diesen Wert für die Dauer der // Anwendungsausführung bei. meineAktienWerte.addElement(new Float(Math.random() * 100f)); } Hiermit wird jedem Aktiensymbol ein Zufallswert zwischen 0 und 100 zugewiesen. In diesem
Beispiel wird der zugewiesene Wert für die gesamte Ausführungsdauer von BoersenServerImpl beibehalten. // Die oben generierten Aktiensymbole ausgeben. System.out.println("Generierte Aktiensymbole:"); for (int i = 0; i < 10; i++) { System.out.println(" " + meineAktienSymbole.elementAt(i) + " " + meineAktienWerte.elementAt(i)); } System.out.println(); } Abschließend gibt der Konstruktor die Aktiensymbole und zugehörigen Werte aus. // Den aktuellen Wert für das jeweilige Aktiensymbol zurückgeben. public float getAktienWert(String symbol) { // Versuch, das angegebene Symbol zu finden. int AktienIndex = meineAktienSymbole.indexOf(symbol); if (AktienIndex != -1) { // Symbol gefunden; Wert wird zurückgegeben. return ((Float)meineAktienWerte.elementAt(AktienIndex)). floatValue(); } else { // Symbol wurde nicht gefunden. return 0f; } } Die Methode getAktienWert() übernimmt eine Zeichenkette (String), versucht, im Datenelement meineAktienSymbole eine Entsprechung zu finden, und gibt den Wert für das Aktiensymbol (sofern es gefunden wurde) zurück. Wenn das Aktiensymbol nicht gefunden wird, wird der Wert Null zurückgegeben. Natürlich ist die Methode getAktienWert() hervorragend dazu geeignet, eine Exception auszulösen. Wenn ein ungültiges Aktiensymbol an die Methode zurückgegeben wird, könnte diese z.B. die Exception UngueltigesAktienSymbolException auslösen, anstatt den hier gelieferten Wert Null zurückzugeben. Dieser Fall ist Gegenstand einer Übung am Ende des Kapitels.
// Eine Sequenz aller diesem BoersenServer bekannten
// Aktiensymbole zurückgeben. public String[] getAktienSymbole() { String[] symbols = new String[meineAktienSymbole.size()]; meineAktienSymbole.copyInto(symbols); return symbols; } Die Methode getAktienSymbole() erzeugt einfach ein Array von Zeichenketten, kopiert die (in meineAktienSymbole enthaltenen) Aktiensymbole in das Array und gibt das Array zurück. // Ein BoersenServer-Objekt erzeugen und initialisieren. public static void main(String args[]) { Die Methode main() in der Klasse BoersenServerImpl erzeugt ein BoersenServerImpl-Objekt, bindet dieses Objekt an einen Bezeichnungskontext und wartet dann darauf, daß Clients die Methoden dieses Objekts aufrufen. try { Da die Methoden, die von main() später aufgerufen werden können, Exceptions auslösen könnten, werden die betreffenden Aufrufe in einen Block vom Typ try ... catch eingebunden. // Den ORB initialisieren. ORB orb = ORB.init(args, null); Bevor der ORB für irgendeine Aktivität eingesetzt werden kann, muß er zunächst einmal initialisiert werden. // Ein BoersenServerImpl-Objekt erzeugen und beim ORB // registrieren. BoersenServerImpl BoersenServer = new BoersenServerImpl(); orb.connect(BoersenServer); Hiermit wird ein neues BoersenServerImpl-Objekt erzeugt und beim ORB registriert. // Den Root-Bezeichnungskontext abrufen. org.omg.CORBA.Object obj = orb. resolve_initial_references("NameService"); NamingContext namingContext = NamingContextHelper.narrow(obj); Nun folgt ein wenig »schwarze Magie«. Bei dem CORBA-Bezeichnungsdienst handelt es sich um einen Dienst, der es CORBA-Objekten ermöglicht, mit einem Namen registriert und anschließend über diesen Namen von anderen CORBA-Objekten gefunden zu werden. Wie weiter oben bereits erwähnt, wird die Verwendung des Bezeichnungsdienstes erst am 12. Tag ausführlich beschrieben,
aber die Client-Beispielanwendung in diesem Kapitel muß trotzdem in der Lage sein, einen Server zu finden. Aus diesem Grund wird der Bezeichnungsdienst hier bereits eingeführt, was im Grunde sicherlich etwas verfrüht ist. Folglich sollten Sie sich keine Sorgen machen, wenn Sie im weiteren Verlauf dieses Kapitels nicht alle Details zum Bezeichnungsdienst oder zu seiner Verwendung verstehen. Damit Clients mit der Klasse BoersenServerImpl verbunden werden können, müssen sie auf irgendeine Weise diesen Dienst im Netzwerk ausfindig machen können. Eine Möglichkeit hierfür stellt der CORBA-Bezeichnungsdienst dar. Hierbei wird ein Objekt vom Typ NamingContext durch Auflösung einer Referenz zu einem Objekt namens NameService gefunden. // Die BoersenServer-Objektreferenz im Bezeichnungskontext // binden. NameComponent nameComponent = new NameComponent(unserPfad, ""); NameComponent path[] = { nameComponent }; namingContext.rebind(path, BoersenServer); Hier wird das Objekt NamingContext aufgefordert, das Objekt BoersenServerImpl an den zuvor definierten Pfad (BoersenServer) zu binden. Die Clients können nun den Bezeichnungsdienst nach einem Objekt dieses Namens abfragen, und der Bezeichnungsdienst gibt eine Referenz an dieses BoersenServerImpl-Objekt zurück. // Auf Aufrufe von Clients warten. java.lang.Object warteAufMich = new java.lang.Object(); synchronized (warteAufMich) { warteAufMich.wait(); } Da das BoersenServerImpl-Objekt nun beim Bezeichnungsdienst registriert ist, braucht es jetzt nur noch auf Clients zu warten, die Methoden dieses Objekts aufrufen. Da die tatsächliche Behandlung dieser Methodenaufrufe in einem gesonderten Thread erfolgt, braucht der Thread main() einfach nur endlos zu warten. } catch (Exception ex) { System.err.println("BoersenServer konnte nicht gebunden werden: " + ex. getMessage()); } } } Wenn von einer der aufgerufenen Methoden eine Exception ausgelöst wird, wird diese hier abgefangen und behandelt.
Kompilierung und Ausführung des Servers Jetzt ist es so weit, daß der Server kompiliert und ausgeführt werden kann. Wenn Sie mit einer integrierten Entwicklungsumgebung arbeiten, können Sie den »Build«-Befehl dieses Werkzeugs (oder einen entsprechenden Befehl) zur Erzeugung der Anwendung verwenden. Bei Verwendung des JDK von der Befehlszeile aus müssen Sie zuvor in das Verzeichnis wechseln, in dem sich BoersenMarkt.idl befindet (in diesem Verzeichnis müßte auch ein Verzeichnis namens BoersenMarkt enthalten sein). Führen Sie dann den folgenden Befehl aus: javac BoersenMarkt\BoersenServerImpl.java (Möglicherweise müssen Sie im obigen Befehl den umgekehrten Schrägstrich durch das für Ihre Plattform gültige Verzeichnistrennzeichen ersetzen.) Mit diesem Befehl werden die ServerImplementierung und alle Dateien, von denen diese abhängt, kompiliert. Überprüfen Sie vor der Kompilierung des Servers, ob in der Anweisung CLASSPATH das korrekte Verzeichnis bzw. die korrekte Datei für die CORBA-Klassen eingetragen ist. Beim Paket JavaIDL von Sun ist die Datei /lib/classes.zip (Verzeichnis, in dem JavaIDL installiert ist) in der Anweisung CLASSPATH eingetragen. Die korrekte Einstellung für CLASSPATH finden Sie in der Dokumentation zu Ihrem CORBA-Produkt.
Unter der Voraussetzung, daß die Server-Anwendung korrekt kompiliert wurde, können Sie den Server nun ausführen. Zuvor muß jedoch noch der Bezeichnungs-Server ausgeführt werden. (Wie bereits erwähnt, macht die Client-Anwendung die Server-Anwendung mit Hilfe des CORBABezeichnungsdienstes ausfindig; der Bezeichnungs-Server stellt den Mechanismus zur Verfügung, der diesen Vorgang ermöglicht.) Die genaue Methode für die Ausführung des Bezeichnungs-Servers unterscheidet sich je nach Produkt, doch das Endergebnis ist jeweils das gleiche. Bei JavaIDL von Sun wird der BezeichnungsServer einfach durch Eingabe von nameserv gestartet. Sobald der Bezeichnungs-Server ausgeführt wird, ist alles für die Ausführung Ihres Servers vorbereitet. Sie können den Server mit dem folgenden Befehl aufrufen: java BoersenMarkt.BoersenServerImpl Der Einfachheit halber sollten der Bezeichnungs-Server und der von Ihnen entwickelte Server erst einmal auf dem gleichen Computer laufen. Wenn alles korrekt funktioniert, erfolgt eine Ausgabe wie in Listing 4.4 dargestellt. Natürlich wird die Ausgabe andere Aktiensymbole und zugehörige Werte enthalten, aber sie wird dem Listing entsprechen, und am Ende der Ausgabe werden keine ExceptionMeldungen stehen.
Listing 4.4: Beispielausgabe für BoersenServer 1: Generierte Aktiensymbole: 2: PTLF 72.00064 3: SWPK 37.671585 4: CHHL 78.37782 5: JTUX 75.715645 6: HUPB 41.85024 7: OHQR 14.932466 8: YOEX 64.3376 9: UIBP 75.80115 10: SIPR 91.13683 11: XSTD 16.010124 Wenn Sie so weit gekommen sind, herzlichen Glückwunsch! Sie haben eine CORBA-ServerAnwendung erfolgreich entworfen, implementiert und zum Einsatz gebracht. Wenn Sie Ihren Erfolg ausreichend genossen haben, können Sie die Anwendung beenden, da erst am Ende dieses Kapitels ein Client zur Verfügung stehen wird, der mit der Server-Anwendung verbunden werden könnte. Sie können den Server aber auch weiterlaufen lassen, um sich später die Mühe zu ersparen, ihn neu zu starten (oder um Ihre Freunde zu beeindrucken und in Erstaunen zu versetzen).
Entwicklung eines CORBA-Clients Am Ende des ersten Teils dieses Kapitels stehen Sie mit einem Server da, mit dem Sie kaum etwas anfangen können, weil es keine Clients gibt, die mit ihm verbunden werden könnten. Im weiteren Verlauf dieses Kapitels werden Sie diese unbefriedigende Situation bereinigen, indem Sie einen Client implementieren, der die Dienste des von Ihnen entwickelten Servers nutzen kann. Da Sie die IDL-Schnittstellen bereits programmiert und kompiliert (und eigentlich auch schon implementiert) haben, wird die Implementierung des Clients sehr viel einfacher vor sich gehen. Hinzu kommt, daß Clients oft (wenn auch nicht immer) von Natur aus einfacher aufgebaut sind als Server. Daher ist ihre Implementierung in dieser Hinsicht auch leichter. Implementierung des Clients Wie bereits erwähnt, ist die Implementierung des Clients ein einfacher Vorgang. Hierbei müssen nur einige wenige Konzepte berücksichtigt werden: Wie werden Client-Stubs in der ClientImplementierung verwendet, wie wird das Auffinden eines Server-Objekts realisiert, und wie werden die Schnittstellen eines Server-Objekts nach dessen Auffinden verwendet? Verwendung von Client-Stubs Bei der Kompilierung von BoersenServer.idl hat der IDL-Compiler sowohl Client-Stubs als auch Server-Skeletons generiert. Da die Client-Stubs bei der Server-Implementierung nicht gebraucht wurden, haben Sie die Stubs zu diesem Zeitpunkt beiseite gelassen. Jetzt ist die Zeit für ihren Einsatz
gekommen. Wenn Sie neugierig sind, können Sie die Datei _BoersenServerStub.java öffnen und sich den Inhalt ansehen. Sie werden eine große Menge unverständlichen Quelltext zusammen mit zwei wohlvertrauten Methoden entdecken: public float getAktienWert(String symbol) { ... } public String[] getAktienSymbole() { ... } Die Implementierungen für diese Methoden dienen, wie weiter oben erwähnt, zur Formatübertragung für die Parameter durch den ORB zum Remote-Objekt und anschließend zur Formatübertragung für die Rückgabewerte an den Client. (Genau diese Aufgabe erfüllt der so unverständlich aussehende Quelltext.) Sie brauchen sich mit dem Inhalt der Datei _BoersenServerStub.java nicht näher zu befassen. Jetzt brauchen Sie nur zu wissen, daß diese Datei den Client-Stub für die BoersenServer-Schnittstelle enthält. Der Java-Compiler ist so »intelligent«, daß er diese Datei automatisch kompiliert, aber wenn Sie einen Client in C++ implementieren würden, müßten Sie das Client-Stub-Objekt mit dem Rest der Client-Anwendung verbinden. Ferner sollten Sie über den Client-Stub noch wissen, daß er die tatsächlichen Schnittstellen für das Server-Objekt angibt. Anders gesagt, Sie können aus dem vorhergehenden Beispiel erkennen, daß die Methode getAktienWert() den Java-Datentyp string als Parameter verwendet und den Java-Datentyp float zurückgibt. Analog dazu verwendet getAktienSymbole() keine Parameter und gibt ein JavaArray vom Typ string zurück. Auffinden eines Server-Objekts Genauso, wie eine Server-Anwendung praktisch nutzlos ist, wenn sie ihren Standort nicht bekanntgeben kann, kann eine Client-Anwendung keine nützliche Arbeit verrichten, wenn sie die zu verwendenden Dienste nicht findet. An dieser Stelle kommt wieder der CORBA-Bezeichnungsdienst ins Spiel. Nachdem sich ein Server beim Bezeichnungs-Server hat registrieren lassen, können Clients dieses Server-Objekt über den Bezeichnungs-Server finden, einen Bindevorgang zu diesem ServerObjekt durchführen und anschließend Methoden des Server-Objekts aufrufen. Auch hier brauchen Sie sich keine Sorgen machen, wenn Sie noch nicht alle Details im Zusammenhang mit dem Bezeichnungsdienst versehen, denn dieser wird am 12. Tag noch ausführlich behandelt. Für den Client BoersenMarktClient erfolgt das Binden an das Server-Objekt über die Methode connect(), wie in Listing 4.5 gezeigt. Diese Methode stellt zunächst eine Bindung zum BezeichnungsServer her, indem es nach einem Objekt mit dem Namen NameService sucht. Nach erfolgreichem Auffinden eines Bezeichnungs-Servers wird der Client an ein Objekt mit dem Namen BoersenServer gebunden, wobei es sich zufällig um denselben Namen handelt, unter dem Sie das Objekt BoersenServerImpl registriert haben (tatsächlich müssen die Namen gleich sein, wenn das Beispiel
funktionieren soll). Nach dem Binden dieses Objekts kann der Client die gewünschten Aufgaben erfüllen. Listing 4.5: Binden an den Server BoersenServer . 1: // Verbindung mit dem BoersenServer herstellen. 2: protected void connect() { 3: 4: try { 5: 6: // Root-Bezeichnungskontext abrufen. 7: org.omg.CORBA.Object obj = unserORB. 8: resolve_initial_references("NameService"); 9: NamingContext namingContext = NamingContextHelper.narrow(obj); 10: 11: // BoersenServer-Objekt im Bezeichnungskontext suchen. 12: NameComponent nameComponent = new NameComponent("BoersenServer", 13: ""); 14: NameComponent path[] = { nameComponent }; 15: meinBoersenServer = BoersenServerHelper.narrow(namingContext. 16: resolve(path)); 17: } catch (Exception ex) { 18: System.err.println("Konnte BoersenServer nicht auflösen: " + ex); 19: meinBoersenServer = null; 20: return; 21: } 22: 23: System.out.println("Bindung mit einem BoersenServer erfolgreich."); 24: } Verwendung von Server-Objektschnittstellen Listing 4.6 zeigt ein Beispiel für die Verwendung der Server-Objektschnittstellen, nachdem der Client die Bindung mit dem Server-Objekt vorgenommen hat. Wie Sie vermutlich schon erwarten, ruft der Client die Server-Methoden einfach nach Bedarf auf. Erneut können Sie in der Datei für den ClientStub (_BoersenServerStub.java) oder noch besser in der Datei für die BoersenServer-Schnittstelle (BoersenServer.idl) nachsehen, welche Methodensignaturen für BoersenServer vorhanden sind. Die Verwendung dieser Methoden ist eindeutig, wie in Listing 4.6 dargestellt. Listing 4.6: Verwendung der BoersenServer-Dienste. 1: // Einige interessante Dinge mit dem BoersenServer ausführen. 2: protected void fuehreEtwasAus() {
3: 4: try { 5: 6: // Die gültigen Aktiensymbole vom BoersenServer abrufen. 7: String[] AktienSymbole = meinBoersenServer.getAktienSymbole(); 8: 9: // Aktiensymbole und deren Werte anzeigen. 10: for (int i = 0; i < AktienSymbole.length; i++) { 11: System.out.println(AktienSymbole[i] + " " + 12: meinBoersenServer.getAktienWert(AktienSymbole[i])); 13: } 14: } catch (org.omg.CORBA.SystemException ex) { 15: System.err.println("Schwerwiegender Fehler: " + ex); 16: } 17: } In Listing 4.6 wird zunächst BoersenServer über einen getAktienSymbole()-Aufruf aufgefordert, eine Liste aller vom Server erkannten Aktiensymbole zu liefern. Der Client durchläuft dann schrittweise die Liste der Aktiensymbole und fragt den Server mit Hilfe von getAktienWert() nach dem Wert für die einzelnen Aktien ab. Jedes Aktiensymbol und der dazugehörige Wert werden an die Standardausgabe übergeben. Kompilierung und Ausführung des Clients Der gesamte Quelltext für BoersenMarktClient.java ist in Listing 4.7 enthalten. Beachten Sie, daß der Großteil der Arbeit in den Methoden connect() und fuehreEtwasAus() erfolgt, die Sie sich zuvor bereits angesehen haben. Listing 4.7: BoersenMarktClient.java. 1: // BoersenMarktClient.java 2: 3: package BoersenMarkt; 4: 5: import org.omg.CORBA.ORB; 6: import org.omg.CosNaming.NameComponent; 7: import org.omg.CosNaming.NamingContext; 8: import org.omg.CosNaming.NamingContextHelper; 9: 10: // BoersenMarktClient ist ein einfacher BoersenServer-Client. 11: public class BoersenMarktClient { 12: 13: // Einen neuen BoersenMarktClient erzeugen. 14: BoersenMarktClient() { 15:
16: } 17: 18: // BoersenMarktClient ausführen. 19: public void run() { 20: 21: connect(); 22: 23: if (meinBoersenServer != null) { 24: fuehreEtwasAus(); 25: } 26: } 27: 28: // Verbindung zu BoersenServer herstellen. 29: protected void connect() { 30: 31: try { 32: 33: // Root-Bezeichnungskontext abrufen. 34: org.omg.CORBA.Object obj = unserORB. 35: resolve_initial_references("NameService"); 36: NamingContext namingContext = NamingContextHelper.narrow(obj); 37: 38: // Ein BoersenServer-Objekt im Bezeichnungskontext 39: // suchen. 40: NameComponent nameComponent = new NameComponent("BoersenServer", 41: ""); 42: NameComponent path[] = { nameComponent }; 43: meinBoersenServer = BoersenServerHelper.narrow(namingContext. 44: resolve(path)); 45: } catch (Exception ex) { 46: System.err.println("Konnte BoersenServer nicht auflösen: " + ex); 47: meinBoersenServer = null; 48: return; 49: } 50: 51: System.out.println("Bindung mit einem BoersenServer erfolgreich."); 52: } 53: 54: // Einige interessante Dinge mit dem BoersenServer ausführen. 55: protected void fuehreEtwasAus() { 56: 57: try { 58:
59: // Die gültigen Aktiensymbole vom BoersenServer abrufen. 60: String[] AktienSymbole = meinBoersenServer.getAktienSymbole(); 61: 62: // Aktiensymbole und deren Werte anzeigen. 63: for (int i = 0; i < AktienSymbole.length; i++) { 64: System.out.println(AktienSymbole[i] + " " + 65: meinBoersenServer.getAktienWert(AktienSymbole[i])); 66: } 67: } catch (org.omg.CORBA.SystemException ex) { 68: System.err.println("Schwerwiegender Fehler: " + ex); 69: } 70: } 71: 72: // Einen BoersenMarktClient starten. 73: public static void main(String args[]) { 74: 75: // ORB initialisieren. 76: unserORB = ORB.init(args, null); 77: 78: BoersenMarktClient AktienClient = new BoersenMarktClient(); 79: 80: AktienClient.run(); 81: 82: // Dieser Abschnitt wartet "ewig". Dadurch bleibt das DOS-Fenster erhalten 83: // (für Entwickler die Windows-Entwicklungsumgebungen verwenden). 84: while (true) 85: ; 86: } 87: 88: // Mein ORB. 89: public static ORB unserORB; 90: 91: // Mein BoersenServer. 92: private BoersenServer meinBoersenServer; 93: } Das Kompilieren der Client-Anwendung ist ein unkomplizierter Vorgang. Wie der Server, so kann auch der Client ganz einfach mit dem folgenden Befehl kompiliert werden: javac BoersenMarkt\BoersenMarktClient.java Auch beim Kompilieren des Clients sollten Sie sich vergewissern, daß das richtige Verzeichnis gewählt ist, nämlich dasselbe Verzeichnis, in dem auch der Server kompiliert wurde.
Unter der Voraussetzung, daß die Client-Anwendung korrekt kompiliert wurde, kann die Anwendung nun ausgeführt werden. Starten Sie zunächst den Bezeichnungsdienst und die Anwendung BoersenServer (wie zuvor), wenn Sie diese am Ende des ersten Teils beendet haben. Geben Sie dann zur Ausführung der Client-Anwendung den folgenden Befehl ein: java BoersenMarkt.BoersenMarktClient Wenn der Client erfolgreich läuft, sehen Sie eine Ausgabe wie in Listing 4.8. Listing 4.8: Ausgabe für BoersenMarktClient. 1: Bindung mit einem BoersenServer erfolgreich. 2: PTLF 72.00064 3: SWPK 37.671585 4: CHHL 78.37782 5: JTUX 75.715645 6: HUPB 41.85024 7: OHQR 14.932466 8: YOEX 64.3376 9: UIBP 75.80115 10: SIPR 91.13683 11: XSTD 16.010124 Die Aktiensymbole und ihre Werte werden genauso angezeigt wie in der Ausgabe für die ServerAnwendung.
Zusammenfassung In diesem Kapitel haben Sie anfangs mit Hilfe von IDL die Schnittstellen für einen CORBA-Server definiert. Anschließend haben Sie unter Verwendung des Vererbungsmechanismus (im Gegensatz zum Delegationsmechanismus) den Server implementiert und nebenher ein wenig über den CORBABezeichnungsdienst gelernt. Danach folgte die Erzeugung einer einfachen Client-Anwendung, welche die von der Anwendung BoersenServer zur Verfügung gestellten Dienste nutzt. Hierbei haben Sie gelernt, wie das BoersenServer-Objekt mit Hilfe des Bezeichungsdienstes aufgefunden werden kann und wie die Client-Stubs sich in die Client-Anwendung einfügen. Schließlich haben Sie CORBAServer und -Client zusammen ausgeführt und dabei etwas erzeugt, das als Ihre allererste verteilte CORBA-Anwendung bezeichnet werden kann. Herzlichen Glückwunsch! Dies war kein Kinderspiel. Ausblick Mit diesem Kapitel ist der erste Teil dieses Buchs, in der die CORBA-Architektur und -Methodik beleuchtet wurden, abgeschlossen. Im nächsten Hauptabschnitt, der die Tage 5 bis 9 umfaßt, werden Sie eine umfangreichere und komplexere CORBA-Anwendung entwickeln, wobei Sie zunächst die grundlegende Funktionalität erstellen und in den nachfolgenden Kapiteln weitere Leistungsmerkmale hinzufügen werden. Hierbei werden Sie die im ersten Teil gelernten Techniken weiter anwenden. Der
einzige Unterschied besteht darin, daß Sie es dann mit komplexerem IDL-Quelltext und daher auch mit komplexeren Servern und Clients zu tun haben werden. Sie verfügen inzwischen über die gesamten Grundlagenkenntnisse zur Entwicklung einer vollständigen CORBA-Anwendung. In den nächsten Tagen werden Sie Gelegenheit haben zu üben, wie diese Kenntnisse auf ein komplexeres System angewendet werden können.
Fragen & Antworten Frage: Ich bin C++-Programmierer und bin mir nicht ganz sicher, ob ich die ganze Java-Syntax verstehen werde. Antwort: Die Syntax von Java ähnelt der C++-Syntax in weiten Bereichen, aber Java enthält zusätzlich noch einige eigene Konstrukte. Solange Sie erfassen, was mit dem jeweiligen Quelltext bewirkt wird, brauchen Sie sich keine zu großen Sorgen machen, wenn Sie einige Merkmale dieser Sprache nicht verstehen. Frage: Wenn ich meine Server-Schnittstellen mit Hilfe des Vererbungsmechanismus implementiere und den Server dann nachträglich umschreibe, um den Delegationsmechanismus zu verwenden, muß ich dann meinen Client bzw. meine Clients ebenfalls umprogrammieren? Antwort: Unabhängig vom Mechanismus, mit dem der Server implementiert wurde, bleibt der ClientQuelltext immer gleich. Sie können daher beruhigt sein: Sie brauchen keine Änderungen an den Clients vorzunehmen, wenn Sie einmal gezwungen sein sollten, einen Server für einen anderen Implementierungsmechanismus umzuprogrammieren. Frage: Wie können die Klassen, die IDL-Schnittstellen implementieren, über Konstruktoren verfügen, wenn in der IDL keine spezifiziert sind? Antwort: Die IDL spezifiziert lediglich öffentliche (public) Schnittstellen; das heißt, daß die Methoden von anderen Objekten an einer beliebigen Stelle im Netzwerk verwendet werden können. Die Klasse, die eine IDL-Schnittstelle implementiert, kann jedoch auch eigene Methoden zur Verfügung stellen, allerdings sind diese außerhalb des Prozeßraums des betreffenden Objekts nicht sichtbar. Solche
Methoden können dennoch auch innerhalb der Server-Anwendung nützlich sein; Konstruktoren sind ein Beispiel hierfür. (Server-Objekte müssen ja auch irgendwie erzeugt werden.) Haben Sie also keine Scheu, zusätzliche Methoden (öffentliche, geschützte und private) in Ihre ServerImplementierungen einzubinden, wenn dies sinnvoll ist.
Workshop Der folgende Abschnitt soll Ihnen dabei helfen, Ihr Verständnis des heute vorgestellten Stoffs zu prüfen und das Gelernte in die Praxis umzusetzen. Die Antworten auf die Quizfragen und die Auflösungen für die Übungen finden Sie in Anhang A. Quiz 1. Welchen Zweck erfüllen die Server-Skeletons und die Client-Stubs? 2. Warum muß der Server das Implementierungsobjekt beim CORBA-Bezeichnungsdienst registrieren? 3. Warum müssen Client und Server Exceptions abfangen, vor allem dann, wenn die von Ihnen definierten IDL-Operationen keine auslösen? Übungen 1. Im Beispiel BoersenMarkt wurde herausgestellt, daß es sinnvoll wäre, in der Methode getAktienWert() eine Exception auszulösen, wenn ein ungültiger AktienSymbol-Wert übergeben wird. Ändern Sie die Datei BoersenMarkt.idl so ab, daß die Methode eine Exception vom Typ UngueltigesAktienSymbolException auslösen kann. (Sie müssen auch eine Definition für diese Exception hinzufügen.) 2. Im BoersenMarkt-Beispiel wurde eine Implementierung nach dem Delegationsmechanismus vorgenommen. Implementieren Sie den Server BoersenServer unter Verwendung des Vererbungsmechanismus. (Für besonders Ehrgeizige: Integrieren Sie auch den Exceptionauslösenden Mechanismus aus der ersten Übung.)
Ein Imprint des Markt&Technik Buch- und Software-Verlag GmbH. Elektronische Fassung des Titels: Corba in 14 Tagen, ISBN: 3-8272-2031-9
Tag 5 Entwerfen des Systems: ein Schnellkurs in objektorientierter Analyse und Entwurfstechnik Was versteht man unter objektorientierter Analyse und Entwurfstechnik? Einführung in die Unified Modeling Language (UML) Die Beispielbankanwendung Zusammenfassung Fragen & Antworten Workshop
Mit dem bisher erlangten Verständnis der Grundlagen von CORBA, wozu beispielsweise das ORBKonzept (Object Request Broker, Objektanfragenvermittler) und die IDL (Interface Definition Language, Schnittstellendefinitionssprache) gehören, sowie mit den Kenntnissen der IDL sind Sie nun
in der Lage, auf CORBA basierende Anwendungen zu entwickeln und einzusetzen. Sie haben bereits erste Erfahrungen beim Implementieren eines einfachen CORBA-Servers und -Clients gemacht. Nun werden Sie eine komplexere CORBA-Anwendung entwerfen, implementieren und dann noch verbessern. Was Sie in den nächsten Tagen erwartet, können Sie der folgenden Übersicht entnehmen: ■
■
■
■
■
Heute werden Sie das Beispielsystem entwerfen, eine kleine Bankanwendung. Da es sich bei CORBA um eine objektorientierte Architektur handelt, werden Sie in diesem Kapitel mit objektorientierten Analyse- und Entwurfskonzepten vertraut gemacht, während Sie den Entwurf des Systems vornehmen. Am 6. Tag werden Sie den Systementwurf in eine IDL-Spezifikation umsetzen. Dabei werden Sie sehen, wie Systemobjekte auf IDL-Klassen und Partitionen auf IDL-Module abgebildet werden, und Sie lernen weitere Aspekte beim Definieren der Schnittstelle kennen. Sie werden auch die verschiedenen CORBAservices und CORBAfacilities begutachten, um zu sehen, wie diese in den Systementwurf eingebracht werden können. Dann werden Sie die grundlegende Funktionalität der Bankanwendung implementieren, wobei Sie die zuvor erstellten IDLDefinitionen als Ausgangspunkt verwenden, von dem aus Sie getrennte Implementierungen für den Server und den Client schreiben. Am 7. Tag werden Sie die grundlegende Funktionalität der Anwendung erweitern, indem Sie eine Exception-Behandlung hinzufügen. Die Exception-Behandlung bietet Fehlerprüffunktionen, so daß die Anwendungsimplementierung robuster wird. Änderungen an der Anwendung beginnen beim IDL-Quelltext und werden dann in den Implementierungsquelltext übernommen. Am 8. Tag wird die Anwendung noch komplexer, wenn Sie einen weiteren CORBA-Client hinzufügen: den Geldautomat-Client. CORBA-Anwendungen umfassen in der Regel mehrere Client-Komponenten, und Sie erhalten in diesem Kapitel eine Einführung in die Verwaltung mehrerer Clients in einer CORBA-Anwendung. Am 9. Tag werden Sie das Beispiel für die Bankanwendung fertigstellen und eine letzte Verbesserung hinzufügen: die Möglichkeit, Aktualisierungsinformationen vom Server auf die Clients zu »schieben«, wobei Client-Callbacks verwendet werden.
Was versteht man unter objektorientierter Analyse und Entwurfstechnik? Obwohl es objektorientierte Technologien bereits eine ganze Weile gibt, hat der Begriff »objektorientiert« in den letzten Jahren sehr an Popularität gewonnen (er ist regelrecht zum Modewort geworden). In der Tat wurde mit diesem Begriff recht verantwortungslos umgegangen, so daß seine wahre Bedeutung eher im Dunkeln liegt. Was die Angelegenheit noch komplizierter macht, ist die Tatsache, daß er zur Beschreibung der verschiedensten Sachverhalte, von Entwicklungsumgebungen über Programmiersprachen bis hin zu Datenbanken verwendet wird. Was bedeutet der Begriff »objektorientiert« denn nun eigentlich? Mit diesem Begriff wird recht unterschiedslos alles Mögliche benannt, so könnte sowohl eine Programmiersprache als auch ein Zeichenprogramm als »objektorientiert« bezeichnet werden. Innerhalb dieses Buches werden wir uns im wesentlichen auf drei Anwendungsbereiche der objektorientierten Methodik konzentrieren: objektorientierte Analyse (OOA), die sich mit den Entwurfsanforderungen und der globalen
Architektur eines Systems befaßt, objektorientierter Entwurf (Object-Oriented Design, OOD), bei dem eine Systemarchitektur in Programmierkonstrukte (wie beispielsweise Schnittstellen, Klassen und Methodenbeschreibungen) umgesetzt wird, und schließlich objektorientierte Programmierung (OOP), bei der diese Programmierkonstrukte implementiert werden. Für unsere Zwecke steht »objektorientiert« für die verschiedenen methodischen Verfahrensweisen, die ich im Folgenden noch kurz beschreiben werde und die zum Entwerfen und Implementieren von Software dienen. Dieses Kapitel befaßt sich vorrangig mit der objektorientierten Analyse. Am 6. Tag werden Sie den objektorientierten Entwurf praktisch kennenlernen. Wenn Sie die Systemfunktionalität implementieren, werden Sie objektorientierte Programmiertechniken verwenden. Obwohl Ihnen dieses Buch eine Einführung in die Konzepte der objektorientierten Analyse, Entwurfstechnik und Programmierung gibt, erhebt es nicht den Anspruch, diese Themen im Detail abzudecken. Es wurden bereits eine Reihe von Büchern zu diesem Thema verfaßt, die eine definierende Einführung und Erläuterung dieser Konzepte bieten. Die Zeit, die Sie aufbringen, um sich mit diesen Konzepten vertraut zu machen, ist auf jeden Fall sinnvoll investiert.
Einführung in die Unified Modeling Language (UML) Die UML (Unified Modeling Language, vereinheitlichte Modelliersprache) ist ein leistungsfähiges Werkzeug zum Darstellen von objektorientierten Entwürfen. Sie wurde von der Rational Software Corporation entwickelt und ist eine Weiterentwicklung bisheriger Modelliersprachen und -techniken. Eine Beschreibung der UML sowie Links zu anderen Dokumenten über die UML finden Sie auf der WWW-Seite von Rational unter http://www.rational.com/uml/index.html. Die Geschichte der UML Bei der UML handelt es sich um eine Weiterentwicklung bisheriger Modelliersprachen und techniken. Vor der UML gab es bereits viele objektorientierte Methodikansätze. Zu diesen gehören die drei wichtigsten überhaupt: die Booch-1993-Methode von Grady Booch, die Object Modeling Technique von Jim Rumbaugh (OMT) und das Object-Oriented Software Engineering (OOSE) von Ivar Jacobson. Im Oktober 1994 taten sich Booch und Rumbaugh zusammen, um ihre Methoden zusammenzuführen. Das Ergebnis war die Unified Methode 0.8 im Oktober 1995. Etwa zu dieser Zeit stieß Jacobson zu ihnen und führte die OOSE-Methode mit dem Werk von Booch und Rumbaugh zusammen, woraus im Juni 1996 UML 0.9 entstand. Das UML Partners-Konsortium, das aus Unternehmen wie Digital, Hewlett-Packard, IBM, Microsoft, Oracle, Rational und Unisys besteht, wurde dann gegründet, um die UML noch zu verfeinern, wodurch im Januar 1997 UML 1.0 entstand. Die UML-1.0-Dokumente wurden dann zur Standardisierung bei der Object Management Group (OMG) eingereicht, welche, wie Sie ja bereits wissen, die für die Definition der CORBA-Standards zuständige Organisation ist. Wichtige Begriffe und Symbole Die UML ist eine in hohem Maße visuell aufgebaute Sprache; neben Wörtern und Text besteht sie auch (und eigentlich im wesentlichen) aus Graphen und Symbolen. Wohl eines der wichtigsten Diagramme, die Ihnen bei der objektorientierten Analyse und Entwurfstechnik begegnen werden, ist
das Klassendiagramm, das wiederum die Notationen für Klassen, Assoziationen und Vererbung enthält (es gibt auch noch andere, aber diese drei Aspekte sollen hier behandelt werden). Das Klassendiagramm Ein wichtiges Element der UML (oder auch jeder anderen Modelliersprache) ist das Klassendiagramm, das manchmal fälschlicherweise als Objektdiagramm oder Objektmodell bezeichnet wird. Es beschreibt die Klassen und ihre Beziehungen zu anderen Klassen im System. Das Klassendiagramm gibt nur statische Beziehungen (Beziehungen der Klassen zueinander) an, und keine dynamischen Beziehungen (der Erstellungszeitpunkt der Objekte oder das Aufrufen der Dienste eines anderen Objekts). Ein Klassendiagramm dient zur grafischen Darstellung der Beziehungen zwischen den Klassen in einem System. Je nachdem auf welcher Ebene der Gültigkeitsbereich des Diagramms liegt, sind auch die Attribute sowie die Operationen beschrieben, die von jeder Klasse zur Verfügung gestellt werden.
Das Klassendiagramm ist eines der wichtigsten Elemente in einer objektorientierten Methodik. Es ist für das Verständnis einer komplexen Systemarchitektur von entscheidender Bedeutung und bietet tiefe Einblicke in den Entwurf eines Systems. Klassen Natürlich impliziert das Vorhandensein des Klassendiagramms, daß es auch Klassen gibt. Wie zu erwarten, entsprechen die Klassen in der UML denen in einer objektorientierten Programmiersprache wie Java oder C++. Jede Klasse weist einen Namen, keine oder mehrere Attribute und keine oder mehrere Operationen auf. Die Attribute kann man sich wie Elementdaten und Operationen als Elementfunktionen oder Methoden vorstellen. In einem Klassendiagramm kann eine Klassenbeschreibung eine der in Abbildung 5.1 gezeigten Formen annehmen.
Abbildung 5.1: Beschreibungen der UML-Klassen Abbildung 5.1 zeigt drei Beispiele für die Darstellung einer Klasse. Im ersten Beispiel ist nur der Klassenname sichtbar. Diese Form eignet sich für ein Klassendiagramm, bei dem der Schwerpunkt auf der Beziehung zwischen den einzelnen Klassen liegt. Wenn Sie z.B. mit einem äußerst komplexen Klassendiagramm arbeiten, wird diese Vereinfachung hilfreich sein, insbesondere wenn es Ihnen nur
einen Überblick über das gesamte System verschaffen soll. Im zweiten Beispiel sind der Name, die Attribute und die Operationen der Klasse zu sehen, für die Attribute und Operationen sind allerdings nur die Namen angegeben, die Typen und Parameter wurden bei dieser Darstellung weggelassen. Das dritte Beispiel zeigt eine komplette Beispiel-Klassenbeschreibung mit Namen, Attributen und Typen sowie Operationen einschließlich Parametern und Rückgabewerten. Diese Art der Klassenbeschreibung ist hilfreich, wenn Sie Detailinformationen zu einem System und seinen Klassen benötigen. Eine Klassenbeschreibung kann auch Sichtbarkeitsmodifizierer für Attribute und Operationen zur Verfügung stellen. Der Sichtbarkeitsmodifizierer, der optional ist, steht direkt vor dem Attribut bzw. der Operation, das bzw. die beschrieben wird. (Wenn kein Sichtbarkeitsmodifizierer angegeben ist, wird davon ausgegangen, daß das Attribut bzw. die Methode öffentlich ist.) Eine Beschreibung dieser Modifizierer finden Sie in Tabelle 5.1. Tabelle 5.1: Sichtbarkeitsmodifizierer in der UML. Symbol
Beschreibung
+
Öffentliche Attribute und Operationen einer Attribut bzw. Klasse stehen der Klasse selbst sowie allen Operation ist öffentlich übrigen Klassen zur Verfügung.
#
Geschützte Attribute und Operationen einer Attribut bzw. Klasse stehen nur der Klasse selbst sowie den Operation ist geschützt von ihr abgeleiteten Klassen zur Verfügung.
-
Attribut bzw. Operation ist privat
Private Attribute und Operationen einer Klasse stehen nur der Klasse selbst zur Verfügung, nicht einmal den von ihr abgeleiteten Klassen.
Abgeleitetes Attribut
Ein abgeleitetes Attribut hängt von einem anderen Attribut ab. Beispiel: Das Alter einer Person kann als Attribut angesehen werden, es hängt jedoch vom aktuellen Datum sowie vom Geburtsdatum der betreffenden Person ab. Abgeleitete Attribute können öffentlich, geschützt oder privat sein.
/
Bedeutung
Klassenattribut bzw. operation
$
Auf ein Klassenattribut oder eine Klassenoperation kann zugegriffen werden, ohne daß eine Instanz der Klasse vorhanden ist. Klassenattribute und -operationen entsprechen den statischen Klassenelementen in C++ oder Java. Auch Klassenattribute und -operationen können öffentlich, geschützt oder privat sein.
Assoziationen Eine Klasse existiert in der Regel nicht für sich allein, sondern wird normalerweise mit anderen Klassen in Interaktion stehen. Daher kann man sagen, daß eine Klasse Beziehungen zu anderen Klassen aufweist. In der UML werden solche Beziehungen als Assoziationen bezeichnet. Eine Klasse weist eine Assoziation mit einer anderen Klasse auf, wenn sie deren Dienste auf irgendeine Weise nutzt. Bei Bedarf kann die Assoziation benannt werden, und den Rollen, die jede Klasse in einer Assoziation spielt, können ebenfalls Namen gegeben werden. In Abbildung 5.2 ist die Notation für eine Assoziation gezeigt; es handelt sich dabei um eine Linie, die zwischen zwei Klassen gezeichnet wird. Eine Assoziation zwischen zwei Klassen bedeutet, daß die beiden Klassen in irgendeiner Weise miteinander in Beziehung stehen. In der Regel sagt dies aus, daß eine der Klassen Dienste der anderen nutzt oder ein Element aufweist, das eine Instanz der anderen Klasse ist.
Für eine Assoziation kann auch eine Multiplizität definiert sein, was bedeutet, daß eine bestimmte Zahl einer Klasse mit einer bestimmten Zahl einer anderen Klasse assoziiert werden kann. So zeigt Abbildung 5.2, daß ein Kunde mehr als ein Konto haben kann. Außerdem kann es zu einem Konto mehr als einen Kunden geben (so wie dies bei Gemeinschaftskonten der Fall ist). Schließlich kann ein Konto immer nur bei einer Bank eröffnet werden. Es gibt somit drei Arten von Multiplizität bei Beziehungen. ■
■
■
Eine Eins-zu-Eins-Assoziation ist eine Assoziation, bei der genau ein Objekt eines Typs mit genau einem Objekt eines anderen Typs assoziiert ist. Eine Eins-zu-Viele-Assoziation (oder andersherum eine Viele-zu-Eins-Assoziation) ist eine Assoziation zwischen genau einem Objekt eines Typs mit keinem oder mehreren Objekten eines anderen Typs. Eine Viele-zu-Viele-Assoziation ist eine Assoziation zwischen keinem oder mehreren Objekten eines Typs mit keinem oder mehreren Objekten eines anderen Typs.
Multiplizität bezeichnet die Anzahl der Klassen, die an einer Assoziation beteiligt sind. Beispiele sind Eins-zu-Eins, Eins-zu-Viele und Viele-zuViele.
Abbildung 5.2: UML-Klassenassoziationen Die Angabe der Multiplizität ist optional, in Klassendiagrammen der höheren Ebene werden diese Informationen manchmal der Übersichtlichkeit halber weggelassen. Es ist allerdings erforderlich, die Multiplizitäten der Assoziationen vor dem Implementieren des Entwurfs festzulegen, da Einzelheiten bei der Implementierung von dieser Information abhängen. Vererbung Die Vererbung ist eigentlich ein Sonderfall einer Assoziation. Dieser Begriff hat dieselbe Bedeutung wie in einer objektorientierten Sprache. Eine Klasse, die ihre Definition von einer anderen Klasse erbt, d.h. die von dieser Klasse abgeleitet ist (am 3. Tag wurde dies als übergeordnete Klasse bezeichnet), erbt die nicht-privaten Attribute und Methoden dieser übergeordneten Klasse. Am 3. Tag wurde weiterhin gesagt, daß die abgeleitete Klasse an allen Stellen, an denen die ihr übergeordnete Klasse erforderlich ist, eingesetzt werden kann (beispielsweise als Parameter bei einem Methodenaufruf). Dieses Verhalten heißt Polymorphie. In der UML wird die Vererbung durch einen Pfeil dargestellt, der von der abgeleiteten Klasse zur übergeordneten Klasse zeigt. Die UML unterstützt auch die Mehrfachvererbung, in diesem Pfeil werden Pfeile von der abgeleiteten Klasse zu allen ihr übergeordneten Klassen dargestellt. Beispiele für die UML-Darstellung von Vererbungsassoziationen finden Sie in Abbildung 5.3. Die grundlegende Methodik
Wiederum würde es den Rahmen dieses Buches sprengen, die methodischen Vorgehensweisen für die objektorientierte Analyse und die Entwurfstechnik eingehend zu behandeln. Es ist jedoch ein sinnvoller erster Schritt in der Analyse-Phase, die Objekte bzw. Klassen zu identifizieren, aus denen das System besteht. Viele Objekte im System lassen sich leicht identifizieren, eine Methode hierbei ist es, zunächst eine Beschreibung des Systems und seiner Funktion zu formulieren. Wenn diese Beschreibung vollständig ist, müssen Sie sie durchgehen und darin nach Substantiven suchen. Wenn Sie eines finden, ist es ziemlich wahrscheinlich, daß es ein Objekt im System darstellt. Bei dem Satz »Kunden haben Konten bei einer Bank« sind die Objektkandidaten Kunden, Konto und Bank. Dieser Prozeß, bei dem die Objekte entdeckt werden, ist für das Verständnis des Umfangs eines bestimmten Systems sehr hilfreich.
Abbildung 5.3: UML-Vererbungsassoziationen Nachdem Sie die Objekte gefunden haben, müssen Sie die Beziehungen zwischen den einzelnen
Klassen definieren. Diese Beziehungen werden oft durch Verben in der Systembeschreibung ausgedrückt. Im Beispiel von oben hieß es: Kunden haben Konten, was eine Beziehung zwischen beiden Klassen nahelegt. Ferner ist ersichtlich, daß Konten Teil einer Bank sind, obwohl nicht ganz klar ist, was für eine Beziehung zwischen einem Konto und einer Bank besteht. Die Assoziationen zwischen Klassen brauchen nicht benannt zu werden, obwohl benannte Assoziationen oft zusätzliche Erkenntnisse über den Entwurf eines Systems bieten. Wenn Sie aus dem oben Erwähnten schließen, daß Klassen mit Substantiven und Assoziationen mit Verben benannt werden sollen, haben Sie völlig recht. Dieses Benennungsschema ist eine allgemein anerkannte Konvention für die objektorientierte Analyse. Eine andere Konvention besteht darin, den Klassen Namen im Singular zu geben, also Bank anstelle von Banken.
Nachdem Sie nun die Klassen und ihre Assoziationen festgelegt haben, werden Sie etwas mehr Zeit damit verbringen, die Attribute und Operationen innerhalb der Klassen zu definieren. Für diesen Schritt müssen Sie sich mehr Gedanken machen als für die beiden ersten, und es ist zu vermuten, daß Sie es beim ersten Anlauf nicht gleich richtig hinbekommen. Der gesamte Vorgang erfolgt iterativ: Während Sie die Beziehungen zwischen zwei Klassen definieren, werden Sie unter Umständen neue Klassen entdecken, oder es ist möglich, daß Sie beim Festlegen der Operationen für Objekte neue Assoziationen oder gar neue Klassen entdecken. So kann es vorkommen, daß sich Ihr Entwurf zwischen Anfang und Ende ziemlich stark verändert, aber dies ist beim Entwerfen von Software ganz normal. Durch die Wiederholung dieses Vorgangs erstellen Sie einen robusten Entwurf, und dies hilft Ihnen später in der Entwicklungsphase, Probleme zu vermeiden. (Es läßt sich oft folgendes feststellen: Je später im Entwicklungsprozeß eine Änderung vorgenommen wird, um so mehr Aufwand wird diese Änderung hervorrufen. Daher lohnt es sich auf jeden Fall, viel Zeit in die Verfeinerung des Entwurfs zu investieren.) Zusammenfassung zur UML Die UML ist ein sehr weitreichendes Werkzeug, das nicht nur den statischen Entwurf eines Systems umfaßt (beispielsweise als Klassendiagramm), sondern auch den dynamischen Entwurf (hierzu gehören Use-Cases, Statusübergangsdiagramme und weitere Werkzeuge). Da ich in diesem Buch gerade eben an der Oberfläche zum Thema UML und ihrer Funktionalität als Entwurfswerkzeug gekratzt habe, sollten Sie sich entweder auf der WWW-Seite der Rational Software Corporation (die am Anfang dieses Abschnitts genannt wurde) informieren oder eine andere Informationsquelle zur UML nutzen, wenn Sie das Thema noch vertiefen möchten.
Die Beispielbankanwendung In den nächsten Kapiteln dreht sich alles um ein einziges Beispiel: ein elektronisches Banksystem. In diesem Kapitel werden Sie mit Hilfe der objektorientierten Analyse die Objekte im System definieren und ein Objektmodell für die Anwendung erstellen. In den folgenden Kapiteln werden Sie die
Grundfunktionalität des Systems implementieren und dann zusätzliche Funktionen implementieren sowie das System stabilisieren. Am Schluß werden Sie eine komplexe (aber für eine Geschäftsanwendung immer noch recht triviale) CORBA-Anwendung erstellt haben. Auf dem Weg dorthin werden einige Aspekte beleuchtet, die beim Erstellen eines solchen Systems relevant sind. Unsere Bankanwendung unterstützt das »Telebanking« (auch »elektronischer Bankdienst« genannt). Sie sieht mehrere Banken, mehrere Konten (Giro- und Sparkonten), mehrere Kunden, das Eröffnen und Auflösen von Konten, das Abheben, Einzahlen und Überweisen von einem Konto zum anderen vor. (In den nachfolgenden Kapiteln werden noch weitere Funktionen hinzugefügt, aber die Grundanwendung Bank beginnt mit dieser Funktionalität.) Definition der Systemanforderungen und -funktionen Der erste Schritt beim Entwerfen jedes Systems besteht darin, festzulegen, was das System eigentlich tun soll. Je nach Art der Anwendung kann es in diesem Prozeß erforderlich sein, Kunden und/oder potentielle Systemanwender mit einzubeziehen, Marktforschung zu betreiben oder nur eine Lösung für ein bestimmtes Problem zu finden. Die Anforderungen müssen später oft überarbeitet oder klarer formuliert werden, daher brauchen Sie sich nicht zu wundern, wenn Sie später wieder bei diesem Schritt landen. Für unsere Bankanwendung werden die Funktionen des Grundsystems wie folgt definiert: ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■
Unterstützung mehrerer Banken Unterstützung mehrerer Konten innerhalb der Banken Unterstützung mehrerer Kunden, die ein Konto haben Unterstützung von Kunden, die mehrere Konten haben Unterstützung der Fähigkeit, neue Konten zu eröffnen (zu erstellen) Unterstützung der Fähigkeit, vorhandene Konten aufzulösen (zu löschen) Unterstützung der Fähigkeit, die Konten einer Bank aufzulisten Unterstützung der Fähigkeit, den bzw. die Inhaber eines Kontos zu ermitteln Unterstützung der Fähigkeit, Geld von einem Konto abzuheben Unterstützung der Fähigkeit, Geld auf ein Konto einzuzahlen Unterstützung der Fähigkeit, Geld bankintern von Konto zu Konto zu überweisen Unterstützung der Fähigkeit, Geld bankenübergreifend von einem Konto auf ein anderes zu überweisen Unterstützung von Girokonten (nicht zinsbringend) Unterstützung von Sparkonten (zinsbringend)
Beachten Sie, daß in jeder Zeile eine Funktion beschrieben ist, so daß beispielsweise die Funktionen zum Eröffnen und Auflösen von Konten in zwei verschiedenen Zeilen stehen. Diese Konvention erleichtert die Entwicklung der Testanforderungen, weil die in den einzelnen Zeilen beschriebenen Funktionen auch einzeln getestet werden können. Beim Analysieren der Systemanforderungen empfiehlt es sich ferner sicherzustellen, daß jede Funktion tatsächlich getestet werden kann. So ist eine Anforderung wie »muß einfach zu verwenden sein« subjektiv und kann daher höchstwahrscheinlich nicht getestet werden. Vermeiden Sie daher solche vagen Anforderungen, und formulieren Sie statt dessen lieber eine Gruppe von Anforderungen, in denen spezielle Funktionen einer Benutzeroberfläche
angegeben sind, die »einfach zu verwenden« ist. Definition der Systemobjekte Nachdem Sie nun die Systemanforderungen definiert haben, können Sie ermitteln, welche Objekte im System vorhanden sein müssen. Wie bereits vorgeschlagen, können Sie hierzu die Anwendungsbeschreibung und -anforderungen auf Substantive hin durchsuchen. Dabei werden Sie auf folgendes treffen: Bank, Konto (insbesondere Girokonto und Sparkonto), Kunden und Geld. All dies sind Kandidaten, die in das Objektmodell (oder Klassendiagramm) aufgenommen werden sollten. Wenn Sie feststellen möchten, ob für einen solchen Kandidaten eine Klasse erstellt werden sollte, stellen Sie sich jeweils die folgende Frage: Ist mit diesem Objekt eine Identität oder ein Verhalten verbunden? Wenn dies der Fall ist, sollten Sie ein Objekt erstellen. Versuchen Sie dies mit unserer Liste der Objektkandidaten: ■
■
■
■
Bank: Eine Bank hat in der Tat eine Identität, denn die ABC-Bank läßt sich von der XYZ-Bank unterscheiden. Ferner weist eine Bank ein zugehöriges Verhalten auf: Unter anderem kann sie für die Kunden Konten eröffnen und auflösen. Daraus läßt sich schließen, daß Bank tatsächlich ein Objekt in unserem System ist. Konto: Wie Banken weisen auch Konten eine Identität auf, da sie voneinander unterschieden werden können. Auch haben sie ein zugehöriges Verhalten: Geld kann auf sie eingezahlt oder von ihnen abgehoben oder von einem Konto zum anderen überwiesen werden. Daher wird Konto ein weiteres Objekt in unserem System sein. Kunden: Kunden haben selbstverständlich eine Identität: Kurt Konrad kann von Peter Hegner unterschieden werden, und die Person mit der Personalausweisnummer X123456 7890 unterscheidet sich von der Person mit der Personalausweisnummer Y234567 8901 usw. Den Kunden ist, zumindest für diese Anwendung, kein spezifisches Verhalten zugeordnet, aber aufgrund ihrer Identität ist es sinnvoll, Kunde als Objekt in unserem System zu definieren. Geld: Geld hat wirklich keine Identität, denn der Betrag 150,00 DM ist praktisch gesehen nicht von einem anderen Betrag 150,00 DM zu unterscheiden. Auch ist mit Geld direkt kein Verhalten assoziiert, es kann eingezahlt und abgehoben und von einem Konto zum anderen überwiesen werden, aber dies ist eher ein Verhalten des Kontos als des Geldes selbst. Schließlich kann Geld eher problemlos durch einen einfachen Gleitkommawert dargestellt werden als durch eine Klasse. Aus diesen Gründen empfiehlt es sich, Geld nicht in das Objektmodell der Anwendung aufzunehmen.
Diese Analyse hat gezeigt, daß das System mindestens drei Hauptklassen enthalten muß: Bank, Konto und Kunde. Jetzt werden wir uns mit den Attributen und dem Verhalten der einzelnen Objekte beschäftigen. Die Klasse Bank Einige der Systemanforderungen legen Verhaltensweisen nahe, die die Klasse Bank aufweisen muß: ■
Unterstützung mehrerer Banken
Obwohl dies eigentlich kein Verhalten einer Bank ist, erfordert dies, daß mehr als ein Objekt vom Typ Bank vorhanden sein kann. Ferner legt diese Anforderung nahe, daß in einer CORBAAnwendung ein Mechanismus vorhanden ist, der den Clients Sichtbarkeit auf die Objekte vom Typ Bank gewährt. Ein Ansatz hierfür wäre, die Objekte vom Typ Bank beim Bezeichnungsdienst registrieren zu lassen, so daß diese von den Clients gefunden werden können, ein anderer besteht darin, ein separates Objekt, beispielsweise namens BankServer zu erstellen, das die Sichtbarkeit auf die Objekte vom Typ Bank ermöglicht. In dieser Anwendung soll der zweite Ansatz verwendet werden. In einer mit C++ oder Java geschriebenen Anwendung, die nicht CORBA verwendet, ist es sehr gut möglich, eine statische Methode für Bank zur Verfügung zu stellen, die eine Liste der Objekte vom Typ Bank liefert, was ein sinnvoller Ansatz wäre. Da CORBA-Objekte jedoch keine statischen Methoden unterstützen, wird ein anderer Ansatz, wie der zuvor erwähnte, benötigt.
■
Unterstützung mehrerer Konten innerhalb der Banken Diese Anforderung legt nahe, daß eine Bank eine Liste der Konten verwaltet, obwohl Objekte außerhalb der Bank nicht unbedingt auf diese Liste zugreifen können müssen.
■
Unterstützung der Fähigkeit, neue Konten zu eröffnen (zu erstellen) Diese Anforderung bedeutet, daß eine Bank eine Operation wie erstelleKonto(), für die als Eingabe ein Kunde (oder eine Gruppe von Kunde(n)) verwendet wird, und eventuell einen Eröffnungskontostand unterstützt und das neue Konto-Objekt liefert. Die Bank stellt also die folgende Operation zur Verfügung: erstelleKonto(Kunde : Kunde, AnfangsSaldo : float) : Konto
■
Unterstützung der Fähigkeit, vorhandene Konten aufzulösen (zu löschen) Da ein Konto existieren muß, damit es gelöscht werden kann, gehört dieses Verhalten entweder zur Klasse Bank oder zur Klasse Konto. Aus Gründen der Konsistenz mit erstelleKonto() wird hier die Klasse Bank verwendet. Für loescheKonto(), wie diese Operation genannt werden könnte, sind außer der Angabe des zu löschenden Kontos keine weiteren Informationen erforderlich. Die Signatur könnte also wie folgt aussehen: loescheKonto(Konto : Konto) : void
Sie werden oft Situationen wie die gerade beschriebene erleben, in denen es keine eindeutige Antwort auf die Frage gibt, wo ein bestimmtes Verhalten einzuordnen ist. Gehen Sie nach gesundem Menschenverstand vor, in einigen Fällen werden Sie wahrscheinlich auch eine willkürliche Entscheidung treffen müssen.
■
Unterstützung der Fähigkeit, die Konten einer Bank aufzulisten Zuvor wurde bereits erwähnt, daß eine Bank eine Liste der Konten verwalten soll. Die hier genannte Anforderung legt nahe, daß die Konten einer Bank anderen Objekten zugänglich gemacht werden sollen. In einem System für die Praxis wäre der Zugriff auf die Konteninformationen wohl beschränkt, aber da es in diesem System (noch) keine solche Anforderung gibt, ist diese Operation ganz einfach: getKonten() : Konto[] Die Notation [] gibt an, daß getKonten() ein Array von Objekten des Typs Konto liefert.
Weitere Attribute für Bank wären sicher sinnvoll. So sollte die Bank beispielsweise einen Namen und eine Adresse haben. In dieser Anwendung werden hierfür ganz einfache Attribute verwendet: Name : string Adresse : string Die Klasse BankServer BankServer ist eine Klasse, die in der ersten Analyse noch nicht vorgesehen war, aber bei der Analyse der Klasse Bank aufgetaucht ist. Die Klasse BankServer ist eine einfache Klasse, deren einzige Aufgabe es ist, die Sichtbarkeit auf die Objekte des Typs Bank zur Verfügung zu stellen. Hierzu werden die folgenden Operationen benötigt: Die Bank muß beim BankServer registriert werden, die Registrierung der Bank beim BankServer muß aufgehoben werden können, und es muß eine Liste aller Banken ausgegeben werden können, die derzeit beim BankServer registriert sind. Diese Operationen sind formal wie folgt definiert: registriereBank(bank : Bank) : void aufhebenRegistrierungBank(bank : Bank) : void getBanken() : Bank[] Im Rahmen dieser Anwendung brauchen keine weiteren Funktionen für die Klasse BankServer definiert zu werden. Die Klasse Konto Die nächste Klasse, die wir hier berücksichtigen müssen, ist die Klasse Konto. Über diese wird ein
Großteil der anfänglichen Funktionalität der Bankanwendung implementiert. Die Klasse Konto erfüllt die Systemanforderungen folgendermaßen: ■
Unterstützung mehrerer Kunden, die ein Konto haben Diese Funktion wird durch eine Viele-zu-Eins-Beziehung zwischen den Objekttypen Kunde und Konto erreicht, impliziert aber, daß ein Objekt vom Typ Konto eine Operation unterstützt, die zeigt, welche Objekte vom Typ Kunde mit diesem Konto assoziiert sind: getKunden() : Kunde[]
■
Unterstützung von Kunden, die mehrere Konten haben Diese Funktion, die an die Anforderung gekoppelt ist, daß mehrere Kunden ein Konto haben können, impliziert eine Viele-zu-Viele-Beziehung zwischen den Objekttypen Kunde und Konto (und nicht eine Viele-zu-Eins-Beziehung wie oben erwähnt). Die eigentliche Funktionalität für diese Anforderung gehört zur Definition der Klasse Kunde.
■
Unterstützung der Fähigkeit, den bzw. die Inhaber eines Kontos zu ermitteln Diese Fähigkeit impliziert, daß eine Operation vorhanden ist, die das Objekt Kunde zurückgibt, das mit einem bestimmten Konto assoziiert ist. Diese Operation wurde bereits zuvor zur Verfügung gestellt.
■
Unterstützung der Fähigkeit, Geld von einem Konto abzuheben Die Fähigkeit, Geld von einem Konto abzuheben, würde wahrscheinlich in Form der Operation Abhebung() erfolgen, wobei der abzuhebende Betrag als Argument verwendet wird. Aus praktischen Gründen liefert diese Operation den neuen Kontostand des Objekts Konto: Abhebung(Betrag : float) : float
■
Unterstützung der Fähigkeit, Geld auf ein Konto einzuzahlen Für das Einzahlen von Geld gilt dieselbe Semantik wie beim Abheben. Der einzuzahlende Betrag ist ein Argument, und der Rückgabewert ist der neue Kontostand des Objekts Konto: Einzahlung(Betrag : float) : float
■
Unterstützung der Fähigkeit, Geld bankintern von Konto zu Konto zu überweisen Das Überweisen von Geld von einem Konto zu einem anderen ist etwas komplizierter als das Einzahlen bzw. Abheben. In diesem Fall muß nämlich auch das Zielkonto angegeben werden. Der Betrag der Transaktion darf natürlich auch nicht fehlen. Genauso wie bei den Operationen Einzahlung() und Abhebung() liefert die Operation Ueberweisung() den neuen Kontostand des Kontos, von dem der Betrag abgebucht wurde:
Ueberweisung(anderes : Konto, Betrag : float) : float
■
Unterstützung der Fähigkeit, Geld bankenübergreifend von einem Konto auf ein anderes zu überweisen Diese Fähigkeit wird bereits von der Operation Ueberweisung() unterstützt, weil das Konto, das an diese Operation übergeben wurde, zu jeder beliebigen Bank gehören kann. Daher braucht für das bankenübergreifende Überweisen von Geld von einem Konto zu einem anderen keine separate Operation definiert zu werden.
■ ■
Unterstützung von Girokonten (nicht zinsbringend) Unterstützung von Sparkonten (zinsbringend)
Diese Anforderung geht davon aus, daß Spezialfälle der Klasse Konto definiert werden. Insbesondere werden die Klassen GiroKonto und SparKonto definiert. Man könnte natürlich einwenden, daß es sich bei den Kontoarten eigentlich um Attribute der Klasse Konto handelt, aber für diese Anwendung sollen GiroKonto und SparKonto als abgeleitete Klassen von Konto definiert werden. Dieser Ansatz ist sinnvoll, weil für SparKonto Attribute und Verhaltensweisen definiert werden müssen, die für GiroKonto nicht gelten und umgekehrt. Da diese Typen unterschiedliche Verhaltensweisen aufweisen, ist es sinnvoll, sie als eigene Klassen zu erstellen. Schließlich muß das Konto noch mit einigen Attributen versehen werden. Zunächst wollen wir ihm eine Kontonummer zuweisen, so daß der Mensch als Benutzer es identifizieren kann und außerdem die Kontonummer auf Schecks angegeben werden kann. Ferner wäre es sinnvoll, das Eröffnungsdatum des Kontos festzuhalten. Diese Fähigkeiten haben wir bei der Auflistung der Anforderungen nicht formuliert, es wäre also prinzipiell möglich, auf sie zu verzichten. Sie werden sich aber früher oder später als sinnvoll erweisen, und daher sollen sie jetzt schon integriert werden: KontoNummer : string EroefffnungsDatum : date getKontoNummer() : string getEröffnungsdatum () : date Die hier aufgelisteten Operationen entsprechen Attributen. Dies geschieht in Übereinstimmung mit einer gängigen Praxis, gemäß der Attribute als privat definiert werden und der Zugriff auf sie über Datenzugriffsmethoden erfolgt. Bei nicht mit CORBA programmierten Anwendungen können Sie auch anders vorgehen, aber der Zugriff auf die Attribute von CORBA-Objekten erfolgt immer über Datenzugriffsmethoden. Denken Sie daran, daß ein Vorteil dieser Konvention darin besteht, daß Sie bei Bedarf den externen Zugriff auf Objektattribute so beschränken können, daß diese nur gelesen werden können. Dies ist in diesem Beispiel der Fall, weil nur Datenzugriffsmethoden und keine Datenänderungsmethoden zur Verfügung gestellt werden. Dadurch wird sichergestellt, daß Attribute wie Eröffnungsdatum und Kontonummer nicht geändert werden können. Die Klasse GiroKonto Die Klasse GiroKonto, die von der Klasse Konto abgeleitet ist, stellt zusätzliche Attribute und
Verhaltensweisen zur Verfügung. An diesem Punkt im Anwendungsentwurf wird durch GiroKonto allerdings nichts Neues zu Konto hinzugefügt. Die Klasse SparKonto Die Klasse SparKonto, die ebenfalls von der Klasse Konto abgeleitet wird, stellt ebenfalls zusätzliche Attribute und Verhaltensweisen zur Verfügung. Insbesondere ist wichtig, daß mit dieser Klasse ein Zinssatz mit Datenzugriffs- und Datenänderungsmethode für dieses Attribut assoziiert wird: ZinsSatz : float getZinsSatz() : float setZinsSatz(neuerZinssatz : float) : float Die Operation setZinsSatz() liefert als Service für den Benutzer den bisherigen Zinssatz. Die Klasse Kunde Die Klasse Kunde in dieser Anwendung ist eine relativ einfache Klasse, weil sie mehrheitlich die Dienste nutzt, die von anderen Klassen zur Verfügung gestellt werden. Nur eine der Systemanforderungen fällt in den Bereich der Klasse Kunde. ■
Unterstützung von Kunden, die mehrere Konten haben Da ein Kunde mehrere Konten haben kann, ist es sinnvoll, für die Klasse Kunde eine Operation zu definieren, mit der die Konten aufgelistet werden, die dem Kunden gehören:
getKonten() : Konto[] Außerdem sollen einige Attribute des Kunden hinzugefügt werden, wie beispielsweise sein Name, die Nummer seines Personalausweises (zur Identifikation), seine Adresse und bei verheirateten Frauen der Mädchenname der Mutter (aus Sicherheitsgründen und auch um der guten alten Tradition willen): Name : string PersonalAusweisNummer : string Adresse : string MuttersMaedchenName : string Um die Datenstruktur nicht unnötig kompliziert zu machen, wird hier die Adresse als einfache Zeichenkette definiert und nicht einzeln als Straße, Postleitzahl, Ort usw. Andererseits wäre es im Hinblick auf die Systemrobustheit sicherlich auch nicht verkehrt, eine eigene Klasse Adresse zu definieren, von der noch weitere Klassen abgeleitet werden könnten. Einiges zur Objektidentität Ihnen wird sicher aufgefallen sein, daß bei den zuvor beschriebenen Klassen nirgends die Rede von
Attributen war, die einzig und allein zum Identifizieren des Objekts dienen. (Die einzigen Ausnahmen hiervon sind die Ausweisnummer beim Objekt Kunde und die Kontonummer beim Objekt Konto, auf die ich später noch zurückkommen werde.) Dies liegt daran, daß bei der objektorientierten Analyse von der Annahme ausgegangen wird, daß Objekte grundsätzlich eindeutig sind, so daß ein Attribut zur Identifizierung überflüssig ist. Daher sollten die Klassen auf Analyse-Ebene keine Attribute enthalten, deren einziger Zweck die Identifikation des Objekts ist. Von dieser Regel gibt es Ausnahmen. Eine ist, daß für das Objekt Kunde ein Attribut PersonalAusweisNummer definiert ist, das nur dazu da ist, den Kunden zu identifizieren. Diese Art von Identitätsattribut kommt allerdings deshalb oft vor, weil sie den tatsächlichen Gegebenheiten entspricht. Ein Mensch hat schon durch die Tatsache, daß er existiert, eine eindeutige Identität. Die Personalausweisnummer ist eine eindeutige Methode zur Identifizierung und wird daher gern in Software-Anwendungen benutzt. Es gibt noch andere Identifikationsattribute, die genausogut für einen Anwendungsentwurf verwendet werden könnten. Ein weiteres Beispiel ist die Kontonummer in der Definition der Klasse Konto. Kontonummern werden oft zum Identifizieren eines Kontos oder eines Schecks bzw. von Kontoauszügen verwendet, die der Kunde erhält. Merken Sie sich folgendes, wenn Sie entscheiden müssen, ob die Verwendung von Identitätsattributen angebracht ist oder nicht: Ein künstlich definiertes Identitätsattribut hat in einer Klassenbeschreibung nichts verloren, während ein Identitätsattribut, das es im wirklichen Leben auch gibt, wie beispielsweise PersonalAusweisNummer akzeptabel und sogar hilfreich ist. Erstellung eines Klassendiagramms für die Anwendung Nachdem wir nun die Komponenten (Klassen) des Systems definiert und ihre Attribute und Operationen beschrieben haben, können Sie diese in ein zusammenhängendes Klassendiagramm übertragen. Das Klassendiagramm zeigt nicht nur die Klassen selbst (einschließlich der Attribute oder Operationen, wenn diese dargestellt werden sollen), sondern auch die Beziehungen zwischen den einzelnen Klassen. Abbildung 5.4 zeigt das erste Klassendiagramm für unsere Bankanwendung. (Sie werden von Zeit zu Zeit Änderungen daran vornehmen, wenn der Systementwurf im Verlauf der nächsten Kapitel weiterentwickelt wird.) Beachten Sie, daß in diesem Diagramm die UML-Notierung für die Klassenvererbung eingeführt wird, denn die Klassen SparKonto und GiroKonto sind beide von der Klasse Konto vererbt.
Abbildung 5.4: Das Klassendiagramm für die Bankanwendung An dieser Stelle möchte ich noch einmal darauf hinweisen, daß es sich bei Analyse und Entwurf um einen iterativen Prozeß handelt. Sie werden oft noch einmal zu einem vorherigen Schritt zurückgehen und verschiedene Aspekte des Entwurfs noch einmal überarbeiten, wenn Sie Merkmale finden, die Sie zuvor nicht berücksichtigt hatten oder wenn Sie (zufällig oder nicht) lernen, daß Sie bestimmte Punkte eleganter lösen können. Es ist in diesem Stadium ganz normal, daß Sie bisher Erarbeitetes nochmals prüfen und ggf. ändern. Streben Sie in dieser Phase eine möglichst hohe Systementwurfsqualität an, denn wenn Sie erst einmal mit der Implementierung des Systems begonnen haben, wird dies sehr viel aufwendiger sein. Es ist daher besser, diese Zeit jetzt in der Entwurfsphase zu investieren, wenn das Ändern in jeder Hinsicht noch viel weniger aufwendig ist. Wenn Sie sich noch weiter informieren möchten ... Natürlich wäre zur objektorientierten Analyse und Entwurfstechnik noch viel mehr zu sagen, als Sie hier finden. Ein wichtiger Aspekt bei der objektorientierten Methodik ist die Entwicklung von Use-
Cases (Fallbeispiele), mit denen Szenarien beschrieben werden, in welchen verschiedene Teile des Systems in Interaktion treten. Diese Szenarien enthalten Aktoren, wie einen Benutzer oder ein anderes Objekt, die wieder auf andere Objekte einwirken. Use-Cases beschreiben, wie ein Aktor mit dem System in Interaktion tritt, was für Ergebnisse dies hat und in welcher Reihenfolge diese Ereignisse auftreten. Es gibt viele mögliche Use-Cases für ein einzelnes Szenario. Für ein gegebenes Dialogfeld beispielsweise könnte es ein Use-Case für den Fall geben, daß der Benutzer gültige Daten eingibt, und ein anderes Use-Case für den Fall, daß der Benutzer ungültige Daten eingibt. Ein weiteres wichtiges Werkzeug bei der objektorientierten Analyse und Entwurfstechnik ist das Entwurfsmuster. Diese kann man sich wie die Bausteine für komplexere objektorientierte Konstrukte vorstellen. Ein weit verbreitetes Entwurfsmuster, das Sie vielleicht bereits kennen, ist das Muster »Modell-Sicht«. Bei diesem Muster stellt ein Objekt, das Modell, Daten oder ein Konzept dar. Ein anderes Objekt, die Sicht, teilt dem Modell mit, daß es Aktualisierungsinformationen erhalten möchte, wenn sich der Status des Modells ändert. Ein Beispiel hierfür ist die Modellklasse TemperaturFuehler, die zum Überwachen der Außentemperatur dient. Eine Sicht-Klasse, wie beispielsweise TemperaturAnzeige, könnte eine Sicht der Klasse TemperaturFuehler sein. Dies bedeutet, daß der TemperaturFuehler jede Änderung der Außentemperatur, die er entdeckt, an die TemperaturAnzeige weitergibt. Die TemperaturAnzeige erhält die neue Temperaturinformation und zeigt diese an. Für ein Modell können auch mehrere Sichten definiert sein, in unserem Fall würde der TemperaturFuehler seine Daten bei Änderungen der Außentemperatur an mehrere TemperaturAnzeigen weitergeben. Seit langem sind viele hervorragende Bücher zu Themen wie Use-Cases, Entwurfsmuster und weiteren wichtigen Begriffen des objektorientierten Arbeitens auf dem Markt. Ich möchte Sie ermutigen, sich eingehend mit diesen Themen zu beschäftigen. Kenntnisse in diesem Bereich zahlen sich beim Entwerfen jeder Art von System aus, sie sind nicht nur für CORBA-Anwendungen wichtig.
Zusammenfassung In diesem Kapitel haben Sie im Grunde einen Schnellkurs über objektorientierte Analyse und Entwurfstechnik absolviert. Sie haben einiges über die UML (Unified Modeling Language), ihre Notation und die hierbei relevanten grundlegenden methodischen Vorgehensweisen gelernt. Sie haben dieses Wissen dann beim Entwerfen einer grundlegenden Bankanwendung angewendet. In der Analyseund Entwurfsphase braucht man den Implementierungsdetails nicht allzuviel Aufmerksamkeit zu widmen, es wird sogar empfohlen, in diesem Stadium der Anwendungsentwicklung solche Details außer acht zu lassen. Ein Systementwurf, der nicht von solchen Details abhängig ist, ist flexibler als ein Entwurf, der von Details für eine bestimmte Implementierung abhängt. In der Analyse- und Entwurfsphase haben Sie die drei folgenden Hauptschritte durchgeführt: ■ ■ ■ ■
Ermittlung und Definition der Systemanforderungen Festlegung der möglichen Klassen, aus denen das System besteht Abbildung der Systemanforderungen auf Klassenattribute und -operationen Erstellung eines Klassendiagramms, mit dessen Hilfe die Klassen des Systems in ein zusammenhängendes Modell integriert werden
Ausblick Am 6. Tag werden Sie im letzten Teil der Entwurfsphase den Systementwurf in eine IDLSpezifikation umsetzen. Wie Sie unschwer erraten haben, wird die IDL als Leitlinie für die Systemimplementierung verwendet. Sie werden also im nächsten Kapitel die grundlegende Funktionalität des Systems implementieren, an den folgenden Tagen werden Sie dann die Basisfunktionalität noch optimieren.
Fragen & Antworten Frage: Was ist in der Bankanwendung vorgesehen, um zu verhindern, daß jemand Geld von einem Konto zu einem anderen überweist, ohne hierzu berechtigt zu sein? Antwort: Die Antwort ist kurz: gar nichts. In der Anwendung, die eine übermäßige Vereinfachung eines realen Banksystems ist, wird nicht versucht, irgendeine Art von Sicherheit zu implementieren. In einem kommerziellen System müßten natürlich Sicherheitsmaßnahmen vorgesehen sein, die so etwas verhindern würden. (Wenn Sie ganz ehrgeizig sind, entwerfen Sie übungshalber einen solchen Mechanismus.) Frage: Was versteht man unter Use-Cases? Antwort: Use-Cases sind leistungsfähige Werkzeuge beim Systementwurf. Sie helfen den Systemarchitekten, Entwurfs- und Entwicklungsingenieuren dabei, besser zu verstehen, wie das System funktioniert. Beim Erstellen von Use-Cases werden oft noch Szenarien entdeckt, an die man vielleicht vorher überhaupt nicht gedacht hat. Eingedenk Murphy's Law, das immer zutrifft, wird dann ein Benutzer all die Szenarien entdecken, an die nicht gedacht wurde, in der Regel mit unerwünschtem Ergebnis. So ist es immer besser, wenn sie schon beim Anwendungsentwurf gefunden werden.
Workshop Der folgende Abschnitt soll Ihnen dabei helfen, Ihr Verständnis des heute vorgestellten Stoffs zu prüfen und das Gelernte in die Praxis umzusetzen. Die Antworten auf die Quizfragen und die Auflösung der Übung finden Sie in Anhang A. Quiz
1. Finden Sie die möglichen Objekte im folgenden System: Ein Bestellungssystem ermöglicht es Kunden, Produkte bei einer bestimmten Firma zu bestellen. Jede Bestellung besteht aus einem oder mehreren Einträgen (Zeilen), in denen jeweils eine Anzahl und ein bestimmtes Produkt angegeben ist. Jedem Produkt wiederum ist ein Preis zugeordnet. 2. Was versteht man unter der UML, und wozu dient sie? 3. Beim Systementwurf für ein Bestellungsverarbeitungssystem lautet eine der Anforderungen »es muß schnell sein«. Ist diese Anforderung gut formuliert, oder könnte man das besser ausdrücken? Wenn ja, wie würden Sie es formulieren? Übung Ändern Sie den Systementwurf so ab, daß die Bank aus Zweigstellen besteht, von denen jede einige Kundenkonten hat. Erstellen Sie das Klassendiagramm für den geänderten Entwurf.
Ein Imprint des Markt&Technik Buch- und Software-Verlag GmbH. Elektronische Fassung des Titels: Corba in 14 Tagen, ISBN: 3-8272-2031-9
Tag 6 Implementierung grundlegender Anwendungsfunktionen Implementierung der Grundfunktionen der Bankanwendung Implementierung der grundlegenden Client-Funktionen Ausführung des Beispiel-Quelltexts Zusammenfassung Fragen & Antworten Workshop
Am 5. Tag haben Sie einen Anwendungsentwurf und die zugehörigen Klassen definiert, die zur Festlegung der Struktur dienen, anhand derer der Entwurf verwirklicht werden soll. Heute werden Sie diese IDL-Schnittstellen implementieren und dadurch eine funktionierende Gruppe von Servern und Clients erstellen, die die grundlegenden Funktionen der Bankanwendung implementieren. An den folgenden Tagen werden Sie die Anwendung durch Hinzufügen weiterer Funktionen noch verbessern; heute werden wir uns jedoch auf das Implementieren der Grundfunktionen für die Anwendung beschränken.
Die Beispiele in diesem Kapitel wurden mit VisiBroker/C++ von Visigenic Software entwickelt (http://www.visigenic.com/). Obwohl es eine Standard-IDL-Sprachabbildung für C++ gibt, sind nach wie vor etliche Inkonsistenzen zwischen den verschiedenen CORBAProdukten vorhanden. Wenn Sie mit einem anderen Programm arbeiten, wie beispielsweise Orbix von IONA Technologies, ist es unter Umständen erforderlich, den Beispiel-Quelltext an einigen Stellen geringfügig zu ändern. Falls beim Kompilieren der Beispiele Probleme auftreten, informieren Sie sich in der Programmdokumentation über die Sprachabbildung.
Implementierung der Grundfunktionen der Bankanwendung Die Server-Funktionalität der Bankanwendung ist in die drei Hauptschnittstellen eingebunden: BankServer, Bank und Konto. Die Schnittstelle Konto ist in zwei abgeleitete Schnittstellen unterteilt: GiroKonto und SparKonto. Über diese Schnittstellen ist die Kernfunktionalität der Bankanwendung definiert. Nachdem Sie Implementierungen für diese Schnittstellen zur Verfügung gestellt haben, werden Sie auch die Client-Funktionen implementieren. Die einzige vom Client implementierte Schnittstelle heißt Kunde; sie wird vom Client für den Zugriff auf die verschiedenen Bankdienste verwendet. Implementierung der Schnittstelle BankServer Die erste zu implementierende Server-Schnittstelle heißt BankServer. Wie bereits erwähnt, hat sie den Zweck, Clients das Auffinden der Objekte vom Typ Bank zu ermöglichen. Die Objekte vom Typ BankServer wiederum werden von Clients und Objekten vom Typ Bank über den CORBABezeichnungsdienst oder einen anderen, ähnlichen Mechanismus aufgefunden. Wenn ein Objekt vom Typ Bank erstellt wird, sucht es einen BankServer und läßt sich bei diesem registrieren. Analog dazu macht das Bank-Objekt die Registrierung beim BankServer wieder rückgängig, wenn es beendet wird. Der IDL-Quelltext für die BankServer-Schnittstelle (die Sie am 5. Tag erstellt haben) ist in Listing 6.1 definiert. Listing 6.1: BankServer.idl. 1: 2: 3: 4: 5: 6: 7: 8:
// BankServer.idl #ifndef BankServer_idl #define BankServer_idl #include "Bank.idl" // BankServer ermöglicht den Clients die Sicht auf Bank-Objekte.
9: interface BankServer { 10: 11: // Angegebene Bank mit diesem BankServer registrieren. Die Bank wird 12: // durch getBanken() aufgelistet, bis aufhebenRegistrierungBank() mit dieser 13: // Bank aufgerufen wird. 14: void registriereBank(in Bank bank); 15: 16: // Registrierung der angegebenen Bank vom BankServer aufheben. Falls die Bank 17: // zuvor nicht registriert wurde, wird nichts ausgeführt. 18: void aufhebenRegistrierungBank(in Bank bank); 19: 20: // Liste aller Banken liefern, die aktuell auf diesem 21: // BankServer registriert sind. 22: BankenListe getBanken(); 23: }; 24: 25: #endif Sie müssen nun Implementierungen für die Methoden registriereBank(), aufhebenRegistrierungBank() und getBanken() sowie den bzw. die Konstruktor(en) und den Destruktor für diese Klasse zur Verfügung stellen. Wenn Sie BankServerImpl.h in Listing 6.2 betrachten, wird Ihnen zunächst (in Zeile 10) auffallen, daß die Klasse _sk_BankServer durch die Klasse BankServerImpl erweitert wird. _sk_BankServer ist das Server-Skeleton für die BankServer-Schnittstelle. Wenn Sie die Quelldatei für diese Klasse untersuchen würden, könnten Sie feststellen, daß diese rein virtuelle Methoden zur Verfügung stellt, die den zuvor definierten IDL-Methoden entsprechen. Da es sich jedoch hier um ein Skeleton handelt, stellt es keine Implementierungen für diese Methoden zur Verfügung; dies ist die Aufgabe der Klasse BankServerImpl. Beachten Sie ferner, daß der Name BankServerImpl rein willkürlich gewählt wurde, Sie können die Klassen so benennen, wie Sie möchten, aber es empfiehlt sich, eine Bezeichnungskonvention für die Implementierungsklassen festzulegen und einzuhalten. Listing 6.2: BankServerImpl.h. 1: 2: 3: 4: 5: 6: 7: 8: 9:
// BankServerImpl.h #ifndef BankServerImpl_h #define BankServerImpl_h #include #include "../BankServer_s.h"
10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31:
class BankServerImpl : public _sk_BankServer { public: // Konstruktor. BankServerImpl(); // Destruktor. ~BankServerImpl(); // Diese Methoden werden in BankServer.idl beschrieben. virtual void registriereBank(Bank_ptr bank); virtual void aufhebenRegistrierungBank(Bank_ptr bank); virtual BankenListe* getBanken(); private: // Die Bankenliste dieses BankServers. std::vector meineBanken; }; #endif
Beachten Sie auch die folgenden Listing-Einträge: #include und // Die Bankenliste dieses BankServers. std::vector meineBanken; Wenn Sie vermuten, daß für diese Implementierung die STL (Standard Template Library, StandardSchablonenbibliothek) von C++ verwendet wird, haben Sie völlig recht. Die meisten modernen C++Compiler enthalten die STL, wenn dies bei Ihrem nicht der Fall ist, können Sie sich entweder eine Implementierung der STL besorgen oder den Beispiel-Quelltext so ändern, daß STL nicht verwendet wird. Eine Beschaffungsquelle für die STL-Implementierungen ist ObjectSpace (unter http://www.objectspace.com/). Dort erhalten Sie kostenlose STL-Implementierungen für viele Plattformen und Compiler. Wenn Sie sich BankServerImpl.h genauer ansehen, stellen Sie fest, daß die zuvor definierten IDLMethoden auf die folgenden C++-Methoden abgebildet werden: virtual void registriereBank(Bank_ptr bank); virtual void aufhebenRegistrierungBank(Bank_ptr bank);
virtual BankenListe* getBanken(); Beachten Sie besonders, daß die Referenzen auf Bank auf den Typ Bank_ptr abgebildet wurden und BankenListe auf BankenListe*. Abgesehen von diesen Änderungen und dem Auftreten des Schlüsselworts virtual (das für CORBA-Implementierungsklassen nicht unbedingt erforderlich ist, dessen Verwendung aber empfehlenswert ist), ähneln die C++-Methodendefinitionen stark ihren IDLGegenstücken. Listing 6.3 enthält die Implementierungsklasse BankServerImpl.cpp, über die die Implementierung für die Schnittstelle _sk_BankServer zur Verfügung gestellt wird. Listing 6.3: BankServerImpl.cpp. 1: // BankServerImpl.cpp 2: 3: #include "BankServerImpl.h" 4: 5: #include 6: #include 7: 8: // Von STL abgeleitete unäre Funktion, die True ergibt, wenn Banken gleich sind. 9: class IstBankGleich : public std::unary_function { 10: public: 11: IstBankGleich(argument_type bank) { meineBank = bank; } 12: result_type operator()(argument_type bank) { return bank-> 13: _is_equivalent(meineBank) != 0; } 14: private: 15: argument_type meineBank; 16: }; 17: 18: // Konstruktor. 19: BankServerImpl::BankServerImpl() : meineBanken() { 20: 21: } 22: 23: // Destruktor. 24: BankServerImpl::~BankServerImpl() { 25: 26: } 27: 28: void BankServerImpl::registriereBank(Bank_ptr bank) { 29: 30: // Die angegebene Bank am Listenende einfügen. 31: cout