Sie sind auf Seite 1von 567

Fachbereich Technik

Labor für parallele Prozesse

RMI und Enterprise Java Beans


Hochverfügbarkeit und erweiterte Funktionalität

Diplomarbeit

Karsten Wolke

7. April 2003

Erstprüfer: Prof. Dr. Karl Hayo Siemsen


Zweitprüfer: Prof. Dr. Walter Schumacher
2


c Karsten Wolke, 2003
Emailadresse des Autors: mail@Karsten-Wolke.de

Dieses Dokument unterliegt dem Urheberrecht.Weitergabe und Vervielfältigung der


Diplomarbeit bzw. deren Inhalt an Dritte außerhalb der Hochschule bedarf der Zu-
stimmung des Autors.
Die in dieser Diplomarbeit wiedergegeben Verfahren und Programme werden ohne Rücksicht
auf die Patentlage mitgeteilt. Sie sind für Lehrzwecke bestimmt.
Ich weise darauf hin, dass die in der Diplomarbeit verwendeten Soft- und Hardwarebezeich-
nungen und Markennamen der jeweiligen Firmen im Allgemeinen Warenzeichen-, marken-
oder patentrechtlichem Schutz unterliegen.
Inhaltsverzeichnis 3

Inhaltsverzeichnis
1. Einführung 10
1.1. Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.2. Gliederung der Arbeit . . . . . . . . . . . . . . . . . . . . . . . 10

2. Grundlagen zu den Enterprise JavaBeans und der J2EE-Architektur


15
2.1. Definition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.2. Das Konzept des 3-Schichten-Modells . . . . . . . . . . . . . . . 16
2.2.1. Datenschicht . . . . . . . . . . . . . . . . . . . . . . . . 17
2.2.2. Präsentationsschicht . . . . . . . . . . . . . . . . . . . . 17
2.2.3. Logikschicht . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.3. Allgemeine Anforderungen an Business Objekte . . . . . . . . . 17
2.4. J2EE Technologie . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.4.1. Enterprise JavaBeans (EJB) . . . . . . . . . . . . . . . . 19
2.4.2. Java Remote Method Invocation (RMI) und RMI-IIOP . 19
2.4.3. Java Naming and Directory Interface (JNDI) . . . . . . . 20
2.4.4. Java Database Connectivity (JDBC) . . . . . . . . . . . 20
2.4.5. Java Transaction API (JTA), Java Transaction Services
(JTS) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.4.6. Java Message Services (JMS) . . . . . . . . . . . . . . . 20
2.4.7. JavaMail . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.4.8. Java IDL . . . . . . . . . . . . . . . . . . . . . . . . . . 20

3. Entwickeln von Enterprise JavaBeans 22


3.1. Typen von Enterprise JavaBeans . . . . . . . . . . . . . . . . . 28
3.1.1. Session Beans . . . . . . . . . . . . . . . . . . . . . . . . 29
3.1.2. Entity Beans . . . . . . . . . . . . . . . . . . . . . . . . 33
3.1.3. Message-Driven Beans . . . . . . . . . . . . . . . . . . . 39

4. Installieren und Einrichten der Software 45


Inhaltsverzeichnis 4

4.1. Notwendige Software . . . . . . . . . . . . . . . . . . . . . . . . 45


4.2. Installation der Software . . . . . . . . . . . . . . . . . . . . . . 46

5. Entwicklung einer Container-Managed EJB am Beispiel einer


Adressdatenbank 47
5.1. Quellcodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
5.1.1. Quellcode des Local Interfaces für die AddressBean . . . 50
5.1.2. Quellcode des Local Home Interfaces für die AddressBean 52
5.1.3. Quellcode der AddressBean . . . . . . . . . . . . . . . . 54
5.1.4. Quellcode des remote Interfaces für die AddressCoverBean 57
5.1.5. Quellcode des remote Home Interfaces für die Address-
CoverBean . . . . . . . . . . . . . . . . . . . . . . . . . . 60
5.1.6. Quellcode der AddressCoverBean . . . . . . . . . . . . . 61
5.1.7. Quellcode des Clients . . . . . . . . . . . . . . . . . . . . 64
5.2. Erstellen des Delployment - Descriptors . . . . . . . . . . . . . . 66
5.3. Enterprise JavaBeans auf einen Applikations-Server heraufladen
(deploy) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
5.4. Starten des Clients und Testen der Applikation . . . . . . . . . 74

6. Grundlagen zu RMI 76
6.1. Entwurf einer verteilten Applikation mit Hilfe von RMI . . . . . 77

7. Entwicklung eines einfachen RMI Beispiels (Modifier) 80


7.1. Entwurf und Implementation der Klassen und Schnittstellen . . 80
7.1.1. Entwurf eines remote Interfaces . . . . . . . . . . . . . 81
7.1.2. Implementierung eines remote Interfaces . . . . . . . . . 83
7.1.3. Eine Client Applikation erstellen . . . . . . . . . . . . . 87
7.2. Übersetzen der Quelltexte und Erzeugen der Stubs . . . . . . . 89
7.3. Klassen und Schnittstellen auf die Rechner verteilen . . . . . . 91
7.4. Start und Test der Applikation . . . . . . . . . . . . . . . . . . 91

8. Zusammenspiel zwischen RMI und Enterprise JavaBeans 94


Inhaltsverzeichnis 5

8.1. Einschränkungen von Enterprise JavaBeans . . . . . . . . . . . 96

9. Erläutern des Zusammenspiels von RMI und EJB am Beispiel eines


asynchronen Dateilesers 98
9.1. Aufgabenstellung . . . . . . . . . . . . . . . . . . . . . . . . . . 98
9.2. Umsetzen der Aufgabenstellung . . . . . . . . . . . . . . . . . . 99
9.3. Quellcodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
9.3.1. Quellcode des remote Interface IRmiApp . . . . . . . . . 101
9.3.2. Quellcode des eigenständigen entfernten Objekts RmiApp 102
9.3.3. Quellcode des remote Interface ReadFile . . . . . . . . . 106
9.3.4. Quellcode des remote Home Interfaces ReadFileHome . . 108
9.3.5. Quellcode der Session Bean ReadFileBean . . . . . . . . 109
9.3.6. Quellcode des Clients EjbClient zum Testen des Systems111
9.4. Vorbereitungen zum Start der Anwendung . . . . . . . . . . . . 113
9.5. Testen der Beispielanwendung . . . . . . . . . . . . . . . . . . . 114

10.Hochverfügbarkeit von verteilten Systemen 117


10.1. Konzepte zur Hochverfügbarkeit . . . . . . . . . . . . . . . . . . 117
10.1.1. Mehr Sicherheit mit Hilfe von JNDI . . . . . . . . . . . . 118
10.1.2. Mehr Sicherheit mit Hilfe des Containers . . . . . . . . . 119
10.1.3. Mehr Sicherheit durch den Home Stub . . . . . . . . . . 119
10.1.4. Mehr Sicherheit durch den Remote Stub . . . . . . . . . 121
10.1.5. Mehr Sicherheit durch einen intelligenten Client . . . . . 122
10.2. Fazit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123

11.Framework für intelligente Clients 124


11.1. Der Beobachter . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
11.1.1. Quellcode des Beobachters . . . . . . . . . . . . . . . . . 142
11.1.2. Starten des Observers und des BenchServers . . . . . . 142
11.2. Der Verwaltungs-Server . . . . . . . . . . . . . . . . . . . . . . 146
11.2.1. Quellcode des Verwaltungs-Servers . . . . . . . . . . . . 178
Inhaltsverzeichnis 6

11.2.2. Die Komponente ServerAdmin auf den Applikations-


Server heraufladen . . . . . . . . . . . . . . . . . . . . . 178
11.3. Der Verwaltungs-Client . . . . . . . . . . . . . . . . . . . . . . 179

12.Testen der Hochverfügbarkeitsanwendung 180


12.1. Quellcode des Test-Clients . . . . . . . . . . . . . . . . . . . . . 180
12.2. Vorbereitungen um den Test-Client zu starten . . . . . . . . . . 182
12.3. Starten des Test-Clients . . . . . . . . . . . . . . . . . . . . . . 189

13.Ausblick 190

14.Schlußbetrachtung 192

Abbildungsverzeichnis 193

Tabellenverzeichnis 195

Literatur 195

Glossar 199

Stichwortverzeichnis 204

Eidesstattliche Erklärung 210

A. AddressBean als Bean Managed Entity Bean 211

B. Quellcode der Beobachter Komponente 224


B.1. Quellcode der Schnittstelle IBenchMachine . . . . . . . . . . . . 224
B.2. Quellcode der Klasse BenchMachine . . . . . . . . . . . . . . . . 225
B.3. Quellcode der Klasse BenchServer und Benchmark . . . . . . . 229
B.4. Beispiel für die Konfigurationsdatei des BenchServer . . . . . . 236
B.5. Quellcode der Klasse ObserverAppServer . . . . . . . . . . . . 238
B.6. Quellcode der Klasse ObserverBenchMachine . . . . . . . . . . 241
B.7. Quellcode der Klasse ObserverService . . . . . . . . . . . . . . 245
Inhaltsverzeichnis 7

B.8. Quellcode der Klasse ServiceType . . . . . . . . . . . . . . . . 252


B.9. Quellcode der Schnittstelle IAppServerListener . . . . . . . . 254
B.10.Quellcode der Schnittstelle IServiceListener . . . . . . . . . . 255
B.11.Quellcode der Klasse AppServerEvent . . . . . . . . . . . . . . 256
B.12.Quellcode der Klasse ServiceEvent . . . . . . . . . . . . . . . . 258
B.13.Quellcode der Schnittstelle IObserver . . . . . . . . . . . . . . 261
B.14.Quellcode der Klasse Observer . . . . . . . . . . . . . . . . . . 271
B.15.Beispiel für die Konfigurationsdatei des Observer . . . . . . . . 312

C. Quellcode der Verwaltungs Server Komponente 315


C.1. Quellcode der Schnittstelle ServerAdminHome . . . . . . . . . . 315
C.2. Quellcode der Schnittstelle ServerAdmin . . . . . . . . . . . . . 316
C.3. Quellcode der Klasse ServerAdminBean . . . . . . . . . . . . . . 335
C.4. Quellcode der Schnittstelle ServerAdminClientHome . . . . . . 374
C.5. Quellcode der Schnittstelle ServerAdminClient . . . . . . . . . 375
C.6. Quellcode der Klasse ServerAdminClientBean . . . . . . . . . . 375
C.7. Quellcode der Klasse AppServerServiceClone . . . . . . . . . . 378
C.8. Quellcode der Schnittstelle ServerAdminObserverHome . . . . . 381
C.9. Quellcode der Schnittstelle ServerAdminObserver . . . . . . . . 382
C.10.Quellcode der Klasse ServerAdminObserverBean . . . . . . . . 383
C.11.Quellcode der Klasse AppServerListener . . . . . . . . . . . . 386
C.12.Quellcode der Klasse ServiceListener . . . . . . . . . . . . . . 388
C.13.Quellcode der Schnittstelle AdministratorLocalHome . . . . . . 389
C.14.Quellcode der Schnittstelle AdministratorLocal . . . . . . . . 391
C.15.Quellcode der Klasse AdministratorBean . . . . . . . . . . . . 393
C.16.Quellcode der Schnittstelle AppServerStatusLocalHome . . . . 401
C.17.Quellcode der Schnittstelle AppServerStatusLocal . . . . . . . 403
C.18.Quellcode der Klasse AppServerStatusBean . . . . . . . . . . . 404
C.19.Quellcode der Schnittstelle BenchmarkLocalHome . . . . . . . . 412
C.20.Quellcode der Schnittstelle BenchmarkLocal . . . . . . . . . . . 414
Inhaltsverzeichnis 8

C.21.Quellcode der Klasse BenchmarkBean . . . . . . . . . . . . . . . 416


C.22.Quellcode der Schnittstelle ApplicationServerLocalHome . . . 424
C.23.Quellcode der Schnittstelle ApplicationServerLocal . . . . . . 427
C.24.Quellcode der Klasse ApplicationServerBean . . . . . . . . . . 429
C.25.Quellcode der Schnittstelle R BenchmarkObserverLocalHome . . 440
C.26.Quellcode der Schnittstelle R BenchmarkObserverLocal . . . . . 442
C.27.Quellcode der Klasse R BenchmarkObserverBean . . . . . . . . . 443
C.28.Quellcode der Schnittstelle R AppServerServiceObserver-
LocalHome . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451
C.29.Quellcode der Schnittstelle R AppServerServiceObserverLocal 454
C.30.Quellcode der Klasse R AppServerServiceObserverBean . . . . 455
C.31.Quellcode der Schnittstelle R AppServerServiceLocalHome . . . 463
C.32.Quellcode der Schnittstelle R AppServerServiceLocal . . . . . 467
C.33.Quellcode der Klasse R AppServerServiceBean . . . . . . . . . 470
C.34.Quellcode der Schnittstelle ObserverLocalHome . . . . . . . . . 484
C.35.Quellcode der Schnittstelle ObserverLocal . . . . . . . . . . . . 486
C.36.Quellcode der Klasse ObserverBean . . . . . . . . . . . . . . . . 491
C.37.Quellcode der Schnittstelle ServiceLocalHome . . . . . . . . . . 503
C.38.Quellcode der Schnittstelle ServiceLocal . . . . . . . . . . . . 505
C.39.Quellcode der Klasse ServiceBean . . . . . . . . . . . . . . . . 506
C.40.Quellcode der Schnittstelle ServiceStatusLocalHome . . . . . . 513
C.41.Quellcode der Schnittstelle ServiceStatusLocal . . . . . . . . 515
C.42.Quellcode der Klasse ServiceStatusBean . . . . . . . . . . . . 516
C.43.Quellcode der Schnittstelle ServiceTypeLocalHome . . . . . . . 523
C.44.Quellcode der Schnittstelle ServiceTypeLocal . . . . . . . . . . 525
C.45.Quellcode der Klasse ServiceTypeBean . . . . . . . . . . . . . . 526
C.46.Quellcode der Klasse AdministratorClone . . . . . . . . . . . . 534
C.47.Quellcode der Klasse AppServerStatusClone . . . . . . . . . . 536
C.48.Quellcode der Klasse BenchmarkClone . . . . . . . . . . . . . . 538
C.49.Quellcode der Klasse ApplicationServerClone . . . . . . . . . 541
Inhaltsverzeichnis 9

C.50.Quellcode der Klasse R BenchmarkObserverClone . . . . . . . . 545


C.51.Quellcode der Klasse R AppServerServiceObserverClone . . . 547
C.52.Quellcode der Klasse R AppServerServiceClone . . . . . . . . . 549
C.53.Quellcode der Klasse ObserverClone . . . . . . . . . . . . . . . 554
C.54.Quellcode der Klasse ServiceClone . . . . . . . . . . . . . . . . 562
C.55.Quellcode der Klasse ServiceStatusClone . . . . . . . . . . . . 564
C.56.Quellcode der Klasse ServiceTypeClone . . . . . . . . . . . . . 565
Einführung 10

1. Einführung

1.1. Motivation

In den letzten Jahren ist die Nachfrage nach verteilten Systemen sprunghaft
gestiegen. Theoretische Grundlage sind Enterprise JavaBeans, die Spezifi-
kation EJB 2.0 und Konzepte, wie einige der Einschränkungen von EJBs
umgangen beziehungsweise auf andere Weise implementiert werden (beispiel-
weise Dateizugriff und Multithreading). Das Umgehen der Einschränkungen
wird mit Hilfe der Technologie Remote Method Invocation (RMI) realisiert.

Weiterhin werden in dieser Arbeit Konzepte vorgestellt, um verteilte Anwen-


dungen hochverfügbar zu machen. Dazu wird eine Anwendung entwickelt, die
die Hochverfügbarkeit von EJB- und RMI-Diensten gewährleistet.

Darüber hinaus unterstützt die Anwendung Lastverteilung, um den Clients


eine hohe Performance zu geben.

Da die Technologien noch jung sind (Die Spezifikation EJB 2.0 wurde im
März 2002 veröffentlicht) und es einiger Werkzeuge bedarf, um selbst EJBs
bzw. RMI Objekte zu entwickeln, werden die Konzepte und Schritte detailliert
erläutert. Der Leser kann auf den verwendeten Beispielen aufbauen und selbst
Beispiele entwickeln.

1.2. Gliederung der Arbeit

Die Enterprise JavaBeans (EJBs) bieten ein Framework, verteilte Systeme


standardisiert zu entwickeln. Mit dieser Technologie setzen Programment-
Einführung 11

wickler die Java-Technologie für das Implementieren wieder verwendbarer


Serverkomponenten für geschäftliche Anwendungen ein. Der Entwickler im-
plementiert nur die Geschäftslogik. Allgemeine Geschäftsmechanismen (bei-
spielweise Transaktionsverwaltung und Authentification) werden bereits als
Dienste bereitgestellt und werden in die Anwendung eingebunden. Die EJBs
werden auf Applikations-Server geladen. Der Applikations-Server selbst ist
für die allgemeinen Dienste zuständig, die EJBs für die speziellen kundenbe-
zogenen Anwendungen brauchen. Das Konzept wird in Kapitel 2 detailliert
erläutert.

Bei EJBs unterscheidet man drei verschiedene Typen (Session Bean, Enti-
ty Bean und Message-Driven Bean). Session Beans werden verwendet, um
Anwendungslogik zu implementieren. Entity Beans können Daten persistent
in einer Datenbank halten, Message-Driven Beans erlauben es, asynchrone
Dienste bereitzustellen. Bei Session Beans und Entity Beans wird zwischen
zwei Untertypen differenziert. Session Beans sind entweder zustandbehaftet
oder zustandlos. Der Unterschied besteht darin, ob sich die Session Bean den
Status einer vorausgegangenen Anfrage merkt oder nicht. Entity Beans unter-
gliedert man darin, ob die Datenabfragebefehle der persistenten Datenhaltung
in den Quellcode der EJB implementiert werden (Bean-Managed Persistence)
oder ob sie vom Applikations-Server generiert werden (Container-Managed
Persistence). Der Applikations-Server verwaltet diese Typen. Die Beans stellen
ihm Schnittstellen und Methoden bereit. Wie man die verschiedenen Typen
von EJBs entwickelt und auf einen Applikations-Server herauflädt, wird in
Kapitel 3 erläutert.

Nachdem die Grundlagen der EJBs erörtert sind, wird in Kapitel 5 eine
Beispiel-EJB entwickelt. Eine Adressdatenbank wird mit Hilfe von Container
Einführung 12

Managed Entity Beans implementiert und mit einem Client getestet. Dazu
werden die typischen Attribute einer Adresse (Name, Vorname, Straße, Post-
leitzahl, Ort) standardisiert in einer Datenbank abgelegt ( schreiben“) und

aus der Datenbank ausgelesen. Dies geschieht über EJB-QL letzlich mit Hilfe
der standardisierten Datenabfragesprache SQL.

EJBs werden auf Applikations-Servern ausgeführt. Sie unterliegen dort ge-


wissen Einschränkungen. Sie dürfen beispielsweise keine Threads starten und
nicht auf das Dateisystem zugreifen. Eine Liste und Diskussion aller Ein-
schränkungen der EJB-Spezifikation enthält das Kapitel 8. Oft kann man
auf einige dieser Dienste für die Geschäftsanwendung nicht verzichten. Das
Umgehen der Einschränkungen wird mit Hilfe von RMI (Remote Method In-
vocation) umgesetzt. RMI erlaubt es, auf Java Objekte außerhalb der eigenen
Java Virtual Machine (JVM) zuzugreifen. Dadurch können EJBs auf Objekte
außerhalb des Applikations-Servers zugreifen. Somit können die Teile einer
Geschäftanwendung, die nach der EJB Spezifikation nicht erlaubt sind, auf
eigenständige Objekte auslagert werden. Kapitel 6 erläutert die Grundlagen
zu RMI.

Im Kapitel 7 wird ein minimales Beispiel entwickelt, dass zeigt, wie mit
Hilfe von RMI ein Objekt ein Attribut eines anderen Objektes außerhalb der
eigenen JVM verändert.

In Kapitel 9 wird dann eine Beispiel-EJB entwickelt, die im Hintergrund RMI


nutzt, um auf ein Objekt außerhalb des Applikations-Servers zuzugreifen. Das
Beispiel ist ein asynchroner Dateilesers. Zum einen wird mit RMI auf ein
Dateisystem zugegriffen, zum anderen wird ein zusätzlicher Thread gestartet
Einführung 13

(der die Datei einliest). Beides ist der EJB innerhalb des Applikations-Servers
nicht erlaubt.

Sobald man verstanden hat, wie Enterprise JavaBeans entwickelt werden,


stellt sich die Frage, wie man ein solches System gegen Abstürze sichert (es
hochverfügbar macht, failsafe). Typischerweise greift man von einem Brow-
ser auf einem Client über das Netz auf EJBs zu. Das bedeutet: eine EJB
stellt sozusagen einen Dienst bzw. Service zur Verfügung, den sich ein Client
(Applet, Servlet, JSP, Standalone Anwendung, EJB, ...) zu Nutze macht.
Es ist unwesentlich, wo dieser Service angeboten wird. Es ist sogar denkbar,
dass ein solcher Dienst mehrfach, d.h. auf verschiedenen Applikations-Servern
angeboten wird. Aus der Sicht des Clients ist es also vorteilhaft, nicht fest,
sondern verteilt an Applikations-Servern gebunden zu sein. Im Falle der festen
Bindung bedeutet z.B. ein Absturz des Applikations-Servers auch einen Ab-
sturz des Clients. Um den Service verteilter Bindung an Applikations-Server
zu gewährleisten, gibt es verschiedene Ansätze. In Kapitel 10 werden zunächst
Konzepte vorgestellt, um Hochverfügbarkeit zu erreichen.

In Kapitel 11 wird eine Hochverfügbarkeitsanwendung entwickelt. Mit ihr


erhält man Hochverfügbarkeit für Enterprise JavaBean- und RMI-Dienste.
Zudem unterstützt die Anwendung Lastverteilung, um Clients eine hohe
Performance zu geben. Dies wird mit Hilfe einer eigenständigen, in dieser
Arbeit entwickelten Benchmark-Komponente auf den Applikations-Servern
erreicht. Damit diese Anwendung selbst hochverfügbar ist, besteht sie aus
eigenständigen Komponenten, die mehrfach eingesetzt werden.

In Kapitel 12 wird die Hochverfügbarkeitsanwendung getestet. Dazu wird ein


Einführung 14

Test-Client entwickelt, der als Argument den Namen eines Dienstes erhält
und daraufhin alle verfügbaren Applikations-Server auf die Konsole ausgibt.
Als Beispieldienste dienen die Adressdatenbank aus Kapitel 5, der asynchrone
Dateileser aus Kapitel 9 und die Benchmark-Komponente aus Kapitel 11.
Die einzelnen Dienste sollen zeigen, dass die Hochverfügbarkeitsanwendung
sowohl EJB-Dienste als auch RMI-Dienste überwacht. Die Adressdatenbank
ist eine EJB mit Datenbankanbindung. Die Benchmark-Komponente ist ein
RMI-Dienst, der asynchrone Dateileser eine Kombination aus EJB und RMI.

Abschließend wird in Kapitel 13 ein Ausblick gegeben, wie die Hochverfügbar-


keitsanwendung zu erweitern ist, um sie industriell einzusetzen.
Grundlagen zu den Enterprise JavaBeans und der J2EE-Architektur 15

2. Grundlagen zu den Enterprise JavaBeans und


der J2EE-Architektur

2.1. Definition

Die Definition von Sun Microsystems für Enterprise JavaBeans lautet:


Die Enterprise JavaBeans-Architektur ist eine Komponenten-Architektur für
die Entwicklung und Inbetriebnahme komponentenbasierter Geschäftsanwen-
dungen. Applikationen, die unter Verwendung der Enterprise JavaBeans-
Architektur geschrieben werden, sind skalierbar, transaktionsorientiert und
Mehrbenutzer - geeignet. Diese Applikationen können einmal geschrieben
und dann auf jeder Serverplattform in Betrieb genommen werden, die die
Enterprise JavaBeans-Spezifikation unterstützt.1

Zunächst werden die Begriffe Komponentenmodell und Komponente ein wenig


näher erläutert. Die nächsten Abschnitte sollen die Struktur und das Zusam-
menspiel der EJBs mit dem J2EE-Server erläutern.

Eine Komponenten-Architektur (component architecture) unterstützt


die Erstellung wiederverwendbarer Software-Stücke, sogenannten Komponen-
ten. Ein Komponentenmodell beschreibt, aus welchen einzelnen Komponenten
sich ein System zusammensetzt, welche Schnittstellen (Interfaces) von den
Komponenten angeboten werden und wie die Komponenten diese nutzen. Die
Abhängigkeiten und das grundsätzliche Zusammenwirken der Komponenten
sind festgelegt. Ein Komponentenmodell soll wiederkehrende Implementati-
onsarbeit abnehmen und ein Framework für verteilte Anwendungen bilden.

1
The Enterprise JavaBeans architecture is a component architecture for the development
and deployment of component-based distributed business applications. Applications writ-
ten using the Enterprise JavaBeans architecture are scalable, transactional, and multi-
user secure. These applications may be written once, and then deployed on any server
platform that supports the Enterprise JavaBeans Specification.
Grundlagen zu den Enterprise JavaBeans und der J2EE-Architektur 16

Eine Komponente (component) ist ein Code, der eine Menge definierter
Schnittstellen (Interfaces) implementiert. Komponenten sind keine vollständi-
gen Applikationen, die für sich ablaufen. Sie können eher als Bausteine
angesehen werden, um ein größeres Problem leichter zu lösen. Die Idee von
Komponenten ist sehr mächtig. Ein Unternehmen kann eine Komponente
kaufen, sie mit anderen kombinieren und so auf einfache Art und Weise ein
neues Produkt entstehen lassen.

Eine wesentliche Motivation für die komponentenbasierte Softwareentwicklung


stammt aus dem Bedürfnis nach visuellen Entwicklungswerkzeugen, in denen
GUI-Elemente wie Schaltflächen, Listen, Eingabefelder etc. grafisch bearbeitet
werden. Diese Art von Komponenten ist auch als JavaBeans bekannt.

2.2. Das Konzept des 3-Schichten-Modells

Ein weiterer wichtiger Begriff ist das sogenannte ’three tier model’ ( 3-
Schichten-Modell ). Es geht um ein Programmieren von drei Schichten.

1. Datenschicht

2. Logikschicht

3. Präsentationsschicht

Früher war es für Rechner mit Mikroprozessoren üblich, nur in einer Schicht zu
programmieren. Größere Programmsysteme wurden später als Client/Server-
Systeme entwickelt. Genau genommen ist die Client/Server-Achitektur ein
zweischichtiges Modell. Es besteht aus dem Client (im wesentlichen zur Präsen-
tation) und dem Server (im wesentlichen Datenbank und Anwendungslogik).
Grundlagen zu den Enterprise JavaBeans und der J2EE-Architektur 17

2.2.1. Datenschicht

Die Datenschicht wird auch oft als Backend bezeichnet. Das Backend enthält
die Datenbank. Dort sind Daten physikalisch gespeichert. Das Backend kennt
das relationale Datenmodell, weiß aber außer Ablegen und Auffinden nichts
mit den Daten anzufangen.

2.2.2. Präsentationsschicht

Sie wird im Gegensatz zum Backend als Frontend bezeichnet. Während die
Datenschicht den Anwendern verborgen bleibt, zeigt sich das Frontend den
Benutzern. Man bezeichnet diese Schicht auch gerne als User Interface (UI)
oder auch Graphical User Interface (GUI).

2.2.3. Logikschicht

Dies ist die Schicht in der Mitte. Sie wird gern als Geschäftslogik, Business
Layer, Middleware, Middle Tier, Application Logic oder als Business Logic
bezeichnet. Hier ist die Programmintelligenz vereint, d.h. hier findet die Ver-
arbeitung statt.

2.3. Allgemeine Anforderungen an Business Objekte

Wie alle Objekte haben auch Business Objekte einen Zustand und ein Verhal-
ten. Untersucht man Business Objekte für verschiedene Anwendungen, stellt
man fest, dass sie in ihrem Verhalten gemeinsame Muster haben:

• einen Zustand (state) festhalten


Man unterscheidet persistent state und conversational state. Ein Zustand
ist persistent, wenn er unabhängig von dem Zugriff eines Clients festge-
halten wird. Meist wird er in eine Datenbank geschrieben.
Grundlagen zu den Enterprise JavaBeans und der J2EE-Architektur 18

• auf gemeinsame Daten zugreifen

• an Transaktionen teilnehmen
Eine Transaktion ist eine Gruppe von Verarbeitungsschritten, die unbe-
dingt als Einheit (atomar) abgeschlossen werden muss. Führt einer der
Verarbeitungsschritte nicht zum Erfolg, müssen alle Schritte rückgängig
gemacht werden. Dies wird auch oft als ’roll back’ bezeichnet. Eine er-
folgreiche Transaktion wird dagegen mit einem ’commit’ abgeschlossen.

• eine große Anzahl von Clients bedienen

• für Clients hochverfügbar sein

• in verteilten Systemen nutzbar sein

• wiederverwendbar sein

Um den Entwickler von diesen komplexen, aber immer wiederkehrenden glei-


chen Anforderungen zu entlasten, ist es sinnvoll, dass diese Aufgaben nicht
vom Business Objekt selbst implementiert werden, sondern von der serversei-
tigen Infrastruktur bereitgehalten werden. Und genau dies ist der Sinn der
J2EE-Architektur. Allgemeine Anforderungen von Business Objekten werden
vom Container geregelt.

Zum Verständnis ein kleines Beispiel aus dem Leben: Normalerweise ist es
nicht sinnvoll, daß sich die Eltern selbst überwiegend um die Ausbildung ihrer
Kinder kümmern. Es handelt sich um eine Aufgabe, die bei allen Kindern etwa
gleich abläuft. Die Eltern geben die Kontrolle über die Ausbildung zu einem
erheblichen Teil an das Schulsystem (Framework) ab. Im Schulsystem werden
Schulen (Container) mit Methoden und Objekten zur Verfügung gestellt, die
die oben erwähnten Muster unterstützen:

• Zustände festhalten (Zeugnisse)


Grundlagen zu den Enterprise JavaBeans und der J2EE-Architektur 19

• auf gemeinsame Daten zugreifen (ein Klassenzimmer teilen)

• an Transaktionen teilnehmen (versetzt werden (commit“) oder sitzen-


bleiben ( rollback“)

• eine große Anzahl von Clients bedienen können (viele Schüler)

Die Eltern geben die Kontrolle teilweise an den Container ab. Die Ausbildung
der Kinder wird hauptsächlich von der Schule geregelt (container managed)
und die Eltern können sich anderen Aufgaben im Leben zuwenden . . .

2.4. J2EE Technologie

Die Java 2 Plattform Enterprise Edition (J2EE) ist eine Menge von Middlewa-
re Services, die das Entwickeln von serverseitigen Applikationen vereinfacht.
J2EE baut auf der Java 2 Standard Edition (J2SE) auf. Die Erläuterungen in
dieser Arbeit beziehen sich auf die J2EE Version 1.3, in der EJB 2.0 unterstützt
wird. Die folgenden Abschnitte zeigen die enthaltenen Services.

2.4.1. Enterprise JavaBeans (EJB)

EJB definiert, wie serverseitige Komponenten geschrieben werden. In der EJB


Version 2.0 unterscheidet man zwischen 3 Typen. Die einzelnen Typen sind im
Abschnitt 3.1 näher erläutert.

2.4.2. Java Remote Method Invocation (RMI) und RMI-IIOP

RMI erlaubt es, auf Objekte anderer Java Virtual Machines (JVM) zuzugrei-
fen, als wären sie in der eigenen JVM. Dieser Dienst wird im Kapitel 6 be-
schrieben.
Grundlagen zu den Enterprise JavaBeans und der J2EE-Architektur 20

2.4.3. Java Naming and Directory Interface (JNDI)

JNDI erlaubt den Zugriff auf Namensdienste wie DNS (Domain Name System)
oder LDAP (Lightweight Directory Access Protocol). Dieser Dienst erlaubt es,
Objekte unter einem eindeutigen Namen in eine Datenbank abzulegen. Andere
Objekte können sich dann mit Hilfe des eindeutigen Namen eine Referenz zu
dem Objekt, das in der Datenbank abgelegt wurde, holen.

2.4.4. Java Database Connectivity (JDBC)

JDBC stellt eine Schnittstelle da, um auf relationale Datenbanken zuzugreifen.

2.4.5. Java Transaction API (JTA), Java Transaction Services (JTS)

JTA und JTS gestatten es, Komponenten mit Transaktionsmechanismen zu


versorgen.

2.4.6. Java Message Services (JMS)

JMS unterstützt mit verschiedenen Messaging-Systemen asynchrone Kommu-


nikation.

2.4.7. JavaMail

JavaMail erlaubt es, direkt aus Java Programmen Emails zu senden und zu
empfangen.

2.4.8. Java IDL

IDL ist eine CORBA-Schnittstelle für Java. CORBA ist eine Spezifikation
ähnlich wie J2EE, erlaubt jedoch nicht nur, zwischen Java Objekten zu
Grundlagen zu den Enterprise JavaBeans und der J2EE-Architektur 21

kommunizieren, sondern zwischen Objekten beliebiger Programmiersprachen.


Weitere Informationen zum Thema CORBA findet man unter [1].

Im Rahmen dieser Arbeit können nur einige dieser Pakete detailliert bespro-
chen werden. Zu den einzelnen Diensten findet man ausreichend Dokumenta-
tion auf der Homepage von Sun Microsystems [2].
Entwickeln von Enterprise JavaBeans 22

3. Entwickeln von Enterprise JavaBeans

Enterprise JavaBeans sind keine eigenständigen Applikationen. Sie sind Kom-


ponenten, die auf einem Applikations-Server abgelegt werden. Sie stellen mehr
oder weniger einen Dienst (Service) bereit, der von einem Client genutzt
werden kann. Ein Client kann eine andere EJB, ein Servlet, JSP oder ei-
genständige Java Applikation sein. Es ist sogar erlaubt, mit Objekten anderer
Programmiersprachen auf EJBs zuzugreifen. Die Kommunikation zwischen
einem Client und der EJB geschieht niemals direkt. Wie im vorigen Kapitel
beschrieben, ist ein Container für eine EJB zuständig. Damit ein solcher Con-
tainer eine EJB verwaltet, muss die EJB bestimmte Methoden implementieren
und Schnittstellen bereitstellen. Abbildung 1 gibt einen Überblick, wie die
Kommunikation zwischen einem Client und der EJB abläuft.
Entwickeln von Enterprise JavaBeans 23

Abbildung 1: Kommunikation zwischen Client und J2EE-Server

Jeder J2EE-Server muss einen Name-Server bereitstellen, auf dem mit Hilfe
von JNDI zugegriffen wird. Ein Name-Server erlaubt es, Referenzen zu einem
Objekt unter einem Namen abzulegen und wiederzufinden. Nähere Informa-
tionen zu JNDI findet man unter [3]. Zuerst holt sich ein Client mit Hilfe
des Name-Servers eine Referenz auf das Home-Objekt. Im nächsten Schritt
wird eine Methode create(...) des Home-Objektes aufgerufen, die ein neu-
es EJB-Objekt erzeugt und eine Referenz zum Client zurückliefert. Mit Hilfe
dieses EJB-Objektes können nun Methoden der EJB aufgerufen werden. Um
diesen Mechanismus der Kommunikation zu gewährleisten, müssen Schnitt-
stellen entwickelt werden, die für den Container und den Client zur Verfügung
Entwickeln von Enterprise JavaBeans 24

stehen. Diese Schnittstellen sind das Home-Interface und das remote Interface.
Aus diesen beiden Schnittstellen und der eigentlichen EJB kann der Container
dann das Home-Objekt und das EJB-Objekt generieren. Wie man diese Ob-
jekte von Hand generiert, wird in Kapitel 7 erläutert.
Der folgende Quellcode zeigt ein typisches Home-Interface.

1 package ejb;
2
3 import java.rmi.*;
4 import javax.ejb.*;
5
6 /**
7 * Einfaches Home Interface
8 */
9 public interface ErsteEJBHome extends EJBHome{
10
11 public ErsteEJB create() throws RemoteException,
12 CreateException;
13
14 }

In dieser Schnittstelle werden alle Erzeuger-Methoden deklariert. Das ist ver-


gleichbar mit den Konstruktoren normaler Klassen. Die Schnittstelle muss die
Schnittstelle javax.ejb.EJBHome erben und alle create(...) Methoden müs-
sen eine RemoteException beziehungsweise CreateException werfen können.
Als nächstes folgt der Aufbau eines einfachen remote-Interfaces.

1 package ejb;
2
3 import java.rmi.*;
4 import javax.ejb.*;
5
6
7 /**
8 * Einfaches remote Interface
9 */
10 public interface ErsteEJB extends EJBObject{
11
12 public String hello() throws RemoteException;
13 }
Entwickeln von Enterprise JavaBeans 25

In dieser Schnittstelle werden alle Methoden deklariert, die später vom Client
aufgerufen werden können. Die Schnittstelle muss die javax.ejb.EJBObject
Schnittstelle erben, und alle Methoden müssen eine RemoteException werfen
können. Nachdem die beiden Schnittstellen entwickelt worden sind, wird die
eigentliche Bean erstellt. Dabei ist zu beachten das man in der EJB 2.0 Spe-
zifikation zwischen 3 verschiedenen Typen wählen kann. Diese Typen werden
in Abschnitt 3.1 behandelt.
Der folgende Quellcode zeigt eine einfache Session Bean.

1 package ejb;
2
3 import javax.ejb.*;
4
5
6 /**
7 * Einfache Session Bean
8 */
9 public class ErsteEJBBean implements SessionBean {
10
11 public void ejbCreate(){
12
13 }
14
15 public void ejbRemove(){
16
17 }
18
19 public void ejbActivate(){
20
21 }
22
23 public void ejbPassivate(){
24
25 }
26
27 public void setSessionContext(SessionContext ctx){
28
29 }
30
31 public String hello(){
32
Entwickeln von Enterprise JavaBeans 26

33 return "Hello World";


34 }
35 }

Dies ist die eigentliche Enterprise Java Bean. Für jede deklarierte Methode
create(...) muss die EJB-Klasse eine Methode ejbCreate(...) bereitstel-
len. Zudem muss jede Methode, die im remote Interface deklariert wurde, hier
implementiert werden. Nun kommt schnell die Frage auf, wieso die EJB-Klasse
nicht auch das Remote Interface implementiert. Diese Frage wird erst im Ka-
pitel 8 beantwortet. Für die Antwort sind Kenntnisse über RMI erforderlich.
Die anderen Methoden werden vom Container verlangt, um eine Session Bean
zu verwalten. Für jeden Typ von EJBs sind andere Methoden erforderlich, die
in Abschnitt 3.1 erläutert werden. Die 3 Typen von EJBs haben lediglich eine
gemeinsame Methode: Sie implementieren alle eine Methode setTYPContext
(TYP ctx). Diese Methode wird direkt nach dem Erzeugen und vor dem
Initialisieren der Bean aufgerufen und übergibt der Bean einen Kontext. Ein
Kontext enthält Informationen über den Container. Über ihn erhält man eine
Referenz zum EJB-Objekt, Sicherheitsinformationen, kann Transaktionen
abbrechen und noch vieles mehr. Weitere Informationen zu den einzelnen
Kontexttypen findet man unter [4].

Eine einfache EJB könnte wie im obigen Beispiel aussehen. Man könnte sie
nun einfach übersetzen und auf einen Applikations-Server heraufladen (de-
ploy). Bei diesem Vorgang wird der EJB ein Deployment Descriptor beigefügt
(package). Dies ist eine XML Datei, die dem J2EE Applikations-Server sagt,
wie er die Bean zu behandeln hat. Dort werden z.B. Sicherheitsfunktionen,
Transaktionen usw. festgelegt. Dies sind Aufgaben, die im Abschnitt 2.3
erwähnt wurden. Zudem kann es notwendig sein, dass der J2EE-Server zusätz-
lich einen herstellerspezifischen Descriptor fordert. Diese Descriptoren werden
im Allgemeinen nicht selbst geschrieben. Der Hersteller des J2EE-Servers
liefert meist ein Werkzeug zum Heraufladen der Applikationen (Deploytool),
Entwickeln von Enterprise JavaBeans 27

das es dem Benutzer ermöglicht, mit Hilfe eines Wizards die Descriptoren zu
erstellen. Informationen zum Deployment Descriptor findet man unter [5].

Ein EJB-Objekt verhält sich für den Client wie ein lokales Objekt. Im Quell-
code des Clients ist später nicht ersichtlich, dass sich das Objekt auf einem
anderen Rechner befindet. Wie dieser Mechanismus funktioniert, ist in den
Kapiteln 6 und 8 näher beschrieben. Diese Eigenschaft kostet Performance.
Oft kommt es vor, dass der Client lokal zur EJB liegt. In der EJB 2.0 Spezifi-
kation ist es zulässig, anstelle eines Home-Interfaces ein LocalHome-Interface
und anstelle eines remote-Interface ein Local-Interface zu nutzen. Dies bringt
erhebliche Performancevorteile mit sich, da die Verwaltung für die Kommu-
nikation zwischen beiden Rechnern wegfällt. Die beiden obigen Schnittstellen
werden in einer lokalen Umgebung folgendermaßen deklariert:

1 package ejb;
2
3 import java.rmi.*;
4 import javax.ejb.*;
5
6
7 /**
8 * Einfaches Local Interface
9 */
10 public interface ErsteEJBLocalHome extends EJBLocalHome{
11
12 public ErsteEJBLocal create() throws CreateException;
13 }

1 package ejb;
2
3 import java.rmi.*;
4 import javax.ejb.*;
5
6
7 /**
Entwickeln von Enterprise JavaBeans 28

8 * Einfaches Local-Home Interface


9 */
10 public interface ErsteEJBLocal extends EJBLocalObject{
11
12 public String hello();
13 }

Für die eigentliche EJB ändert sich nichts. Man wird im Allgemeinen alle vier
Schnittstellen erstellen. Damit erlaubt man, dass jedes Objekt auf die EJB
zugreifen kann, und gibt lokalen Objekten eine bessere Performance. Mittler-
weile gibt es Tools, die das Entwickeln von Enterprise JavaBeans erheblich
erleichtern. Das entwickeln konzentriert sich auf die eigentliche Bean. Die In-
formationen für die Schnittstellen liegen im Quellcode der EJB vor und werden
per Knopfdruck erstellt. Bei der Vergabe der Namen für die Komponenten ei-
ner EJB sollte man sich an Tabelle 1 halten, um den Überblick zu behalten.

Komponente Namen
Projektname der EJB Hello
Enterprise JavaBean HelloBean
Home-Interface HelloHome
Remote-Interface Hello
LocalHome-Interface HelloLocalHome
Local-Interface HelloLocal

Tabelle 1: Namensvergabe für Enterprise JavaBeans

3.1. Typen von Enterprise JavaBeans

Wie erwähnt, ist eine Enterprise JavaBean eine Softwarekomponente, die in


einem Container auf dem Applikations-Server ausgeführt wird. Je nach dem,
was die EJB leisten soll, entscheidet man sich für einen bestimmten Typ.
Grundsätzlich unterscheidet man zwischen:

• Session-Bean
Entwickeln von Enterprise JavaBeans 29

• Entity-Bean

• Message-Driven-Bean

Jeder der Typen besitzt bestimmte Eigenschaften und wird spezifiziert. In den
folgenden Abschnitten werden die Typen sowie deren Verwaltungsmethoden
beschrieben.

3.1.1. Session Beans

Session Beans werden dazu verwendet, Anwendungslogik auf dem Server aus-
zuführen. Sie sind für diese Aufgabe geeignet. Für jeden Client wird durch den
Container eine eigene Bean konstruiert. Nur dieser Client erhält Zugriff. Sessi-
on Beans sind nicht persistent, d.h. sobald die Kommunikation zwischen Client
und Server beendet ist, wird die Instanz durch den Container zerstört. Dies hat
den Nachteil, dass bei einer Verbindungsunterbrechung bisherige Informatio-
nen verloren gehen. Man unterteilt die Session Beans in zustandbehaftete
(stateful) und zustandlose (stateless) Beans.

Zustandlose Beans bekommen eine Anfrage und geben eine Antwort, d.h. sie
können sich den Status einer vorausgegangenen Anfrage nicht merken und mit
einem Blick auf die Vergangenheit reagieren. Sie werden bei einfachen Pro-
zessen verwendet, in denen keine Kommunikation zwischen Client und EJB
abläuft, z.B. beim Ausrechnen von Berechnungen. Der Client übergibt einer
Methode die Parameter und erhält daraufhin das Ergebnis. Genau genommen
heißt das, dass vor jedem Aufruf einer Methode die Klassenvariablen auf die
Standardwerte zurückgesetzt werden. Aus diesem Grund besitzt dieser Typ
auch die beste Performance. Der Container braucht nicht für jeden einzelnen
Client eine eigene Instanz anzulegen. Dies bedeutet, dass ein Objekt, das von
einem Client momentan nicht benutzt wird, von einem anderen Client genutzt
werden kann. Alle Instanzen dieses Typs sind gleich, da sie sich methodenüber-
greifend keinen Zustand merken.
Entwickeln von Enterprise JavaBeans 30

Bei zustandbehafteten Session Beans handelt es sich um kompliziertere Kon-


strukte. Ihr Zustand wird während der Konversation mit den Clients gespei-
chert. Das bedeutet, dass jeder Client eine eigene Bean erhält. Dieser Typ
von EJB wird z.B. verwendet, um einen Warenkorb in einem Internet-Shop
zu realisieren. Der Client sucht sich Produkte aus, die sich die Bean merken
muss. Da für jeden Client eine eigene Instanz angelegt wird, ist dieser Typ von
Bean langsamer als seine zustandlosen Kollegen. Damit der Container nicht in
Engpässe bei mehrfachen Clientanfragen gerät, werden Instanzen von zustand-
behafteten Session Beans kurzfristig auf Festplatte ausgelagert. Dies geschieht
ähnlich wie beim Swapping“ Mechanismus des Betriebssystems. Im Allgemei-

nen werden nicht alle Instanzen im Speicher gehalten. Der Container lagert
automatisch nicht genutzte Instanzen aus.

Um eine Session Bean zu erstellen, implementiert die EJB die Schnittstelle


javax.ejb.SessionBean. Tabelle 2 zeigt, welche Methoden eine Session Bean
im Einzelnen implementiert und was im Allgemeinen in diesen Methoden ge-
schieht. Der Typ, also ob zustandlose beziehungsweise zustandbehaftete Bean,
wird im Deployment Descriptor festgelegt.
Methode Beschreibung Typische Implementation Typische Implementation
(Stateful Session Bean) (Stateless Session Bean)
setSessionContext Wird aufgerufen, bevor die Der Kontext wird in einer Klas- Der Kontext wird in einer Klas-
(SessionContext Methode create() aufgerufen senvariablen gespeichert, um senvariablen gespeichert, um
ctx) wird, und übergibt der Sessi- später darauf zugreifen zu später darauf zugreifen zu
on Bean den Bean-spezifischen können. können.
Kontext.
ejbCreate...(...) Initialisiert die Session Bean Initialisierung der Session Initialisieren der Session Bean,
Entwickeln von Enterprise JavaBeans

Bean, z.B. das Setzen von z.B. Testen von Ressourcen. Da


Klassenvariablen mit Hilfe zustandlose Session Beans sich
der Parametern oder der keinen Zustand merken, wird
Aufbau einer Datenbankver- nur eine Methode ejbCreate()
bindung. Es können mehrere definiert.
ejbCreate...... definiert
werden mit unterschiedlichen
Parametern, mindestens eine
muss jedoch existieren.
auf nächster Seite fortgesetzt
31
Methode Beschreibung Typische Implementation Typische Implementation
(Stateful Session Bean) (Stateless Session Bean)
ejbPassivate() Vom Container aufgerufen, be- Gegebenenfalls Ressourcen Wird bei zustandlosen Beans
vor die Bean ausgelagert (swap- freigeben, z.B. eine Verbindung nicht aufgerufen, deshalb bleibt
ping) wird. zu einer Datenbank. der Inhalt leer.
ejbActivate() Wird direkt nach dem Wieder- Gegebenenfalls wiederanschaf- Wird bei zustandlosen Beans
einlagern vom Container aufge- fen von Ressourcen, z.B. ei- nicht aufgerufen, deshalb bleibt
rufen. ner Verbindung zu einer Daten- der Inhalt leer.
Entwickeln von Enterprise JavaBeans

bank.
ejbRemove() Wird aufgerufen, bevor die Be- Freigeben aller zugeteilten Res- Freigeben aller zugeteilten Res-
an entfernt wird. sourcen sourcen

Tabelle 2: Geforderte Methoden einer Session Bean


32
Entwickeln von Enterprise JavaBeans 33

3.1.2. Entity Beans

Entity Beans sind für den Umgang mit permanenten Daten in einem Programm
geeignet, da sie Persistenz gewährleisten. Bei diesen Beans besteht nicht für
jeden Client eine eigene Instanz, sondern für alle Clients eine gemeinsame, auf
die jedoch ein gleichzeitiger Datenzugriff möglich ist. Hinter Entity Beans steht
meist direkt eine Datenbank, d.h. es handelt sich um langlebige Komponen-
ten, die auch Serverabstürze überleben. Eine Instanz einer Entity Bean enthält
z.B. einen Datensatz. Beim Löschen einer Entity Bean wird automatisch auch
der Inhalt aus der Datenbank gelöscht. Alle Zugriffe unterliegen der Transak-
tionsverwaltung, d.h. die Daten bleiben in einem konsistenten Zustand. Man
unterscheidet wieder zwischen zwei Arten, den Bean-Managed Persistent
(BMP) und den Container-Managed Persistent (CMP) Enterprise Ja-
vaBeans.

Bei den Bean-Managed EJBs bleibt es dem Entwickler überlassen, sich um


die Persistenz zu kümmern. Er ist dafür verantwortlich, eine Verbindung zu
einer Datenbank aufzubauen, Daten darin zu speichern und auch wieder zu
laden. Die Kommunikation mit der Datenbank könnte beispielsweise mittels
SQL über JDBC erfolgen. Dies erlaubt zwar eine höhere Flexibilität, wider-
spricht aber eigentlich dem Prinzip der Kapselung der Komponenten, da sich
der Komponentenentwickler wieder sowohl mit seinem Anwendungsproblem
als auch mit der Anbindung von Datenbanken auseinandersetzen muss. Die
Komponente ist abhängig von der gebundenen Datenbank.

Bei Container-Managed Entity Beans wird es dem Entwickler einfacher


gemacht. Er muss lediglich beschreiben, welche Daten er gespeichert haben
möchte, der Container übernimmt den Rest. Das bedeutet, dass der Container
die Verbindung zur Datenbank aufbaut, Daten speichert, sucht und von dort
lädt. Dies wird als automatische Persistenz bezeichnet. Ein weiterer Vorteil ist
die Unabhängigkeit der Bean von der Datenbank. Bei diesen Beans kann z.B.
Entwickeln von Enterprise JavaBeans 34

leicht von einer relationalen auf eine objektorientierte Datenbank umgestellt


werden. Die Komponenten müssen dafür nicht angepasst werden. Dies geht
deshalb so einfach, da die Spezifikation EJB 2.0 eine eigene Datenbanksprache
EJB-QL verwendet. Im Deployment-Descriptor wird festgelegt, welche Daten
vom Container verwaltet werden. Suchfunktionen oder ähnliches werden in
EJB-QL angegeben. Der Container setzt dann die EJB-QL Befehle in die da-
tenbankspezifische Sprache um. Mehr Informationen zu EJB-QL findet man
unter [6].

Das Konzept der Container Managed Entity Beans läßt sich nicht immer um-
setzen. Leistungsfähige relationale Datenbanken verfügen über eigene SQL-
Befehle außerhalb des Standards. Bis jetzt setzen die meisten Container EJB-
QL direkt in SQL 3.0 um. Viele Datenbanken unterstützen diesen Standard
jedoch noch nicht und können deshalb nicht mit diesem Konzept verwendet
werden. Die meisten J2EE-Server Hersteller erlauben es jedoch, die SQL Be-
fehle zu ändern bzw. zu optimieren. Die beste Performance wird man jedoch
mit Bean Managed EJBs bekommen.

Um eine Entity Bean zu erstellen, muss die EJB die Schnittstelle javax.ejb.
EntityBean implementieren. Tabelle 3 zeigt, welche Methoden eine Entity
Bean implementieren muss und was in diesen Methoden gemacht wird. Bei
Container-Managed Beans ist zu beachten, dass überhaupt keine Datenbank-
zugriffe direkt implementiert werden. Die entsprecheden Kommandos werden
später im Deployment Descriptor als EJB-QL- oder SQL-Kommandos angege-
ben, ebenfalls der genaue Typ, also Bean-Managed beziehungsweise Container-
Managed Entity Bean.
Methode Beschreibung Typische Implementation (Entity Bean)
setEntityContext Wird aufgerufen, wenn eine neue Instanz Der Kontext wird in einer Klassenvariablen
(EntityContext ctx) der EJB erzeugt wird; übergibt der Entity gespeichert, um später darauf zugreifen zu
Bean den Bean-spezifischen Kontext. können. Zudem werden Vorbereitungen für
die Bean getroffen, z.B. das Herstellen einer
Datenbankverbindung.
ejbFind...(...) Diese Methoden werden über das Ho- Suchen in der Datenbank nach Da-
me Objekt aufgerufen und suchen nach tensätzen; Zum Beispiel in einer rela-
Entwickeln von Enterprise JavaBeans

Entity Beans, die bestimmte Eigenschaf- tionalen Datenbank durch einen SQL-
ten erfüllen. Die Eigenschaften werden als Ausdruck wie SELECT * FROM ... WHERE
Parameter übergeben. Der Rückgabewert .... Werden Datensätze gefunden, wird
kann dabei eine einzelne Instanz oder meh- der Primärschlüssel zurückgeliefert. Der
rere (Collection) Instanzen einer Entity Be- Container wird dann dafür EJB-Objekte
an sein. Auf jeden Fall muss eine Methode erzeugen und dem Client die Referenzen
ejbFindByPrimaryKey(...) definiert wer- übergeben.
den.
auf nächster Seite fortgesetzt
35
Methode Beschreibung Typische Implementation (Entity Bean)
ejbHome...(...) Diese Methoden beziehen sich nicht auf be- Umsetzen der Aufgabe z.B durch
stimmte Instanzen einer Entity Bean. Es Ausführen einer SQL Anweisung.
sind globale Methoden, die über das Home-
Objekt aufgerufen werden, z.B das Zählen
der Datensätze in der Datenbank.
ejbCreate(...) Diese Methoden werden ebenfalls über das Überprüfen der Parameter auf Gültigkeit
Home Objekt aufgerufen. Mit Hilfe dieser und Eintragen der Daten in die Daten-
Entwickeln von Enterprise JavaBeans

Methoden werden neue Daten in die Da- bank; Zusätzlich werden die Parameter in
tenbank eingetragen, z.B. das Erzeugen ei- Klassenvariablen gespeichert. Als Rückga-
nes neuen Datensatzes. Als Rückgabewert bewert wird der Primärschlüssel des neu
wird der Primärschlüssel der neu angeleg- angelegten Datensatzes geliefert.
ten Daten verlangt. Wird es einem Client
nicht erlaubt, neue Daten in die Datenbank
einzutragen, werden diese Methoden nicht
implementiert.
auf nächster Seite fortgesetzt
36
Methode Beschreibung Typische Implementation (Entity Bean)
ejbPostCreate(...) Zu jeder Methode ejbCreate(...) wird Vervollständigen der Initialisation, wobei
eine Methode ejbPostCreate(...) mit das EJB-Objekt verlangt wird.
gleichen Parametern erstellt. Diese Me-
thode wird gleich nach der Methode
ejbCreate(...) aufgerufen.
ejbPassivate() Vom Container aufgerufen, bevor die Bean Ressourcen freigeben, z.B. eine Verbindung
ausgelagert wird (Swapping). zu einer Datenbank.
Entwickeln von Enterprise JavaBeans

ejbActivate() Wird direkt nach dem Wiedereinlagern Wiederanschaffen notwendiger Ressourcen,


vom Container aufgerufen. z.B. einer Verbindung zu einer Datenbank.
ejbLoad() Wird aufgerufen, um Daten speziell für die- Zuerst wird vom Kontext der
se Entity Bean-Instanz aus der Datenbank Primärschlüssel erfragt, daraufhin eine Da-
zu holen. Diese Methode wird z.B. direkt tenbankabfrage ausgeführt. Anschließend
nach der Methode ejbActivate() aufge- werden die Klassenvariablen mit Werten
rufen. gefüllt.
auf nächster Seite fortgesetzt
37
Methode Beschreibung Typische Implementation (Entity Bean)
ejbStore() Wird aufgerufen, um Daten speziell für die- Speichert alle entityspezifischen Daten in
se Entity Bean-Instanz in die Datenbank zu die Datenbank. Bei relationalen Datenban-
schreiben. Diese Methode wird z.B. direkt ken kann man dies mit Hilfe des SQL-
vor der Methode ejbPassivate() aufgeru- Befehls UPDATE.
fen.
ejbRemove() Wird aufgerufen, um Daten aus der Da- Daten aus der Datenbank löschen. Bei re-
tenbank zu löschen. Das EJB-Objekt wird lationalen Datenbanken z.B. mit Hilfe des
Entwickeln von Enterprise JavaBeans

ebenfalls gelöscht. SQL-Befehls DELETE.


unsetEntityContext() Wird aufgerufen, wenn eine Instanz der En- Freigabe aller verwendeten Ressourcen, wie
tity Bean entfernt werden soll. z.B. einer Datenbankverbindung.

Tabelle 3: Geforderte Methoden einer Entity Bean


38
Entwickeln von Enterprise JavaBeans 39

3.1.3. Message-Driven Beans

Eine weitere Eigenschaft der Spezifikation EJB 2.0 sind die Message-Driven-
Beans. Ruft ein Client Methoden einer Session oder Entity Bean auf, ist er
automatisch solange blockiert, bis er das Ergebnis der Methode erhält. Bei
Message-Driven Beans werden die Methodenaufrufe nicht zwingend synchron
durchgeführt. Dies ist möglich, da der Java Messaging Service (JMS) asyn-
chrone Aufrufe gestattet. Abbildung 2 zeigt den Aufbau.

Abbildung 2: Nachrichtenübermittlung via JMS

Bei diesem Typ von Enterprise JavaBeans funktioniert die Kommunikation


zwischen Client und EJB etwas anders. Ein Client kommuniziert nicht mehr
mit Hilfe eines Home- bzw. Remote Objektes mit der EJB, sondern mit Hil-
fe von Nachrichten (Messages). Der Client schickt Nachrichten in eine War-
teschlange (queue), diese Nachrichten werden zu einer Message-Driven Bean
geschickt. Es ist sogar erlaubt, dass mehrere Clients in eine Warteschlange
schreiben und mehrere Message-Driven Beans benachrichtigt werden. Mit Hil-
fe dieses Dienstes kann der Client, eine Aufgabe zu einer Message-Driven Bean
schicken und sofort weiterarbeiten. Der größte Nachteil dieser Architektur ist,
dass der Client nicht automatisch benachrichtigt wird, ob die gewünschte Ope-
ration überhaupt durchgeführt wurde beziehungsweise ob es zu einem Fehler
kam. Möchte man das tun, muss eine zweite Warteschlange erstellt werden, in
der die Message-Driven Bean eine Antwort zum Client schickt.

Um eine Warteschlange zu realisieren, wird ein JMS Server gebraucht. Alle


J2EE Server verfügen über einen solchen JMS Server, aber oft macht es auch
Sinn, einen eigenen aufzustellen. Es gibt JMS Server, die garantieren, dass
Entwickeln von Enterprise JavaBeans 40

eine Nachricht beim Empfänger ankommt. Dies wird dadurch realisiert, dass
die Nachrichten persistent auf dem JMS Server gespeichert werden. Die Nach-
richten werden automatisch zum Empfänger gesendet. Ist er zeitweise nicht
verfügbar oder kommt innerhalb einer bestimmten Zeit keine Bestätigung (ack-
nowledgment) des Empfangs zurück, wird die Nachricht erneut versandt. Fällt
der JMS Server selbst aus, führt dies lediglich zu einer Verzögerung der Nach-
richtenübermittlung, denn beim nächsten Start werden die zu verschickenden
Nachrichten automatisch geladen.

Da der Client nicht direkt die Message-Driven Bean anspricht, wird auch kein
Home- und Remote Interface für diesen Typ benötigt. Lediglich die EJB Klas-
se wird entwickelt. Der nachfolgende Code zeigt den Aufbau einer typischen
Message-Driven Bean.

1 package ejb;
2
3 import javax.jms.*;
4 import javax.ejb.*;
5
6
7 /**
8 * Einfaches Message-Driven Bean
9 */
10 public class LogBean implements MessageDrivenBean,
11 MessageListener {
12
13 public void ejbCreate() {
14
15 }
16
17 public void ejbRemove() throws EJBException {
18
19 }
20
21 public void setMessageDrivenContext(
22 MessageDrivenContext ctx) throws EJBException {
23 }
24
25
26 public void onMessage(Message msg) {
Entwickeln von Enterprise JavaBeans 41

27 if (msg instanceof TextMessage) {


28 TextMessage tm = (TextMessage) msg;
29 try{
30 String text = tm.getText();
31 System.err.println("Neue Nachricht erhalten : "
32 + text);
33 msg.acknowledge();
34 }catch(JMSException e){
35 e.printStackTrace();
36 }
37 }
38 }
39 }

Der Container braucht für Message-Driven Beans weniger Verwaltungsmetho-


den als für die Entity- beziehungsweise Session Beans. Das liegt daran, dass ein
Client nicht direkt mit einer Message-Driven Bean kommuniziert. Der Contai-
ner ruft die Methode onMessage auf, sobald eine Nachricht für die Message-
Driven Bean eingeht. Je nach Auslastung werden neue Instanzen der Message-
Driven Bean erzeugt und entfernt. Leider werden alle Nachrichten zur Methode
onMessage(Message msg) geschickt. Die Bean muss dann selbst überprüfen,
um welchen Typ Nachricht es sich handelt.

Um eine Message-Driven Bean zu erzeugen, implementiert die EJB die Schnitt-


stellen javax.ejb.MessageDrivenBean und javax.jms.MessageListener.
Tabelle 4 zeigt, welche Methoden eine Message-Driven Bean implementiert und
was in diesen Methoden geschieht. Auf welche Warteschlangen die Message-
Driven Bean hört, wird im Deployment Descriptor festgelegt.
Methode Beschreibung Typische Implementation (Message-Driven Be-
ans)
setMessageDrivenContext Wird aufgerufen, wenn eine neue In- Der Kontext wird in einer Klassenvariablen ge-
(MessageDrivenContext stanz der EJB erzeugt wird. Übergibt speichert, um später Informationen über das
ctx) der Message-Driven Bean den beanspe- EJB-Objekt erhalten zu können.
zifischen Kontext.
ejbCreate() Initialisiert die Session Bean Initialisierung der Message-Driven Bean, z.B.
das Setzen von Klassenvariablen oder der Auf-
Entwickeln von Enterprise JavaBeans

bau einer Datenbankverbindung. Dabei werden


nur die Referenzen gesetzt, die bei jedem Auf-
ruf von onMessage(Message) verwendet wer-
den.
onMessage(Message msg) Diese Methode wird vom Container Zunächst wird überprüft, um welchen Typ von
aufgerufen, wenn eine neue Nachricht Message es sich handelt. Dies geschieht über
eingegangen ist. den Operator instanceof. Anschließend wird
gecastet und es werden Operationen speziell für
diesen Typ von Nachricht durchgeführt.
auf nächster Seite fortgesetzt
42
Methode Beschreibung Typische Implementation (Message-Driven Be-
ans)
ejbRemove Wird aufgerufen, bevor die Bean ent- Freigeben aller zugeteilten Ressourcen.
fernt wird.

Tabelle 4: Geforderte Methoden einer Message-Driven Bean


Entwickeln von Enterprise JavaBeans
43
Entwickeln von Enterprise JavaBeans 44

Nach dieser Einführung in die Entwicklung von Enterprise JavaBeans wird im


Kapitel 5 gezeigt, wie eine Container-Managed EJB zusammen mit einer Sessi-
on Bean entwickelt wird. Bevor man EJBs entwickelt, sind einige Softwaremo-
dule zu installieren. Diese werden in Kapitel 4 erläutert. Mehr Informationen
zu Enterprise JavaBeans findet man unter [7, 8, 9].
Installieren und Einrichten der Software 45

4. Installieren und Einrichten der Software

4.1. Notwendige Software

Um Enterprise JavaBeans zu entwickeln, braucht man einige Softwarepakete,


die die geforderten Bibliotheken und Werkzeuge enthalten. Die folgende Liste
gibt einen Überblick der notwendigen Komponenten.

• Java 2 Plattform, Standard Edition (J2SE)

• Java 2 Plattform, Enterprise Edition (J2EE)

• Ein Applikations-Server

• Eine Datenbank

Dies sind die Komponenten, die mindestens notwendig sind, um EJBs zu ent-
wickeln. Die Java 2 Plattform Standard- und Enterprise Edition kann von der
SUN Microsystems Homepage [10] heruntergeladen werden. Die J2EE enthält
bereits einen Applikations-Server. Für Demonstrationszwecke reicht dieser Ap-
plikations-Server vollkommen aus, leider hat er nicht die beste Performance,
weshalb man bei professionellem Einsatz von Enterprise JavaBeans einen leis-
tungsfähigeren Applikations-Server erwerben sollte. Eine Liste von verfügbaren
J2EE-Servern findet man unter [11]. In dieser Diplomarbeit beziehen sich die
Erläuterungen auf den Applikations-Server von SUN. Eine Datenbank ist not-
wendig, um mit Entity Beans zu arbeiten. Zudem kann es hilfreich sein, eine
Entwicklungsumgebung zu nutzen. Diese erstellt automatisch die Schnittstel-
len für eine EJB und kontrolliert, ob alle geforderten Methoden implementiert
sind.
Installieren und Einrichten der Software 46

4.2. Installation der Software

Die beiden Software-Pakete von SUN lassen sich durch Anklicken der herun-
tergeladenen Dateien installieren. Der Wizard erklärt die Schritte. Um mit
dem Applikations-Server zu arbeiten, ist es notwendig zwei Umgebungsvaria-
blen im Betriebssystem zu setzen. Die Variable J2EE Home verweist auf den
Installationsordner der Java 2 Plattform, Enterprise Edition (J2EE), die Va-
riable JAVA HOME auf den Installationsordner der Java 2 Plattform, Standard
Edition (J2SE). Weitere Informationen über das Setzen von Umgebungsva-
riablen findet man im Handbuch des Betriebssystems. Um persistent Daten
zu speichern, wird eine Datenbank verlangt. Welche Datenbank gewählt wird,
bleibt dem Benutzer überlassen. In dieser Diplomarbeit wird davon ausgegan-
gen, dass eine relationale Datenbank vorliegt, die über SQL angesprochen wird.
Eine detaillierte Installationsanleitung der notwendigen Komponenten findet
man unter [12].
Entwicklung einer Container-Managed EJB am Beispiel einer
Adressdatenbank 47

5. Entwicklung einer Container-Managed EJB am


Beispiel einer Adressdatenbank

Es wird gezeigt, wie eine Container Managed Entity Bean im Zusammenhang


mit einer Session Bean entwickelt und auf einen Applikations-Server heraufge-
laden (deploy) wird. Der Vorteil von Container Managed Entity Beans ge-
genüber Bean Managed Entity Beans wurde im Abschnitt 3.1.2 detailliert
erläutert. Zudem wird ein Konzept vorgestellt, um Netzwerkressourcen zu spa-
ren und Clients nur eingeschränkten Zugriff auf die Entity Bean zu gewähren.
Als Beispiel dient eine Adressdatenbank.

Im Allgemeinen wird man eine Entity Bean in eine Session Bean einbet-
ten, um Netzwerkressourcen zu sparen. Enterprise JavaBeans sind entfern-
te Objekte2 . Dies wird detailliert in Kapitel 6 besprochen. Jeder Zugriff
auf eine Methode eines entfernten Objektes wird über das Netz getätigt.
Für eine Adressdatenbank, die aus 5 Attributen (Name, Vorname, Straße,
Postleitzahl und Ort) besteht, würde das Ausgeben einer Adresse auf der
Konsole mit dem Kommando System.out.println("Name :" + entityObj.
getName() +" Vorname :" + entitiyObj.getVorname ...) insgesamt fünf
Male auf das entfernte Objekt zugreifen. Dies bedeutet bei der Ausgabe von
n Datensätzen 5*n Zugriffe. Geschickter ist es, wenn mit Hilfe einer Session
Bean auf die Entity Bean zugegriffen wird. Der Client greift dann nicht direkt
auf die Entity Bean zu, sondern bekommt von der Session Bean eine Kopie der
Entity Bean. Die Ausgabe einer Adresse mit diesem Mechanismus wird keine
Netzwerkressourcen verbrauchen, da auf die lokale Kopie zugegriffen wird.

Um diesen Mechanismus umzusetzen, wird eine gesonderte Klasse entwickelt.


Diese Klasse hat die Attribute einer Entity Bean, die für einen Client zur
Verfügung steht. Änderungen an den Daten sind kompliziert und zeigen einen
Nachteil dieses Mechanismus auf. Hat der Client direkt Zugriff auf die Adress
2
In einigen Java Dokumentationen wird auch der englische Begriff remote object verwendet.
Entwicklung einer Container-Managed EJB am Beispiel einer
Adressdatenbank 48

Entity Bean, kann er den Namen einer Adresse direkt mit der Methode set...
ändern. Bei dem Mechanismus der lokalen Kopie hat die Session Bean solche
Methoden bereitzustellen. Aus diesem Grund kann man nicht grundsätzlich
sagen, dass eine Entity Bean in eine Session Bean eingebettet werden sollte.
Man wird diesem Mechanismus auf jeden Fall benutzen, wenn überwiegend
Daten gelesen werden.

In einer Adressdatenbank wird überwiegend gelesen. Deswegen wird in diesem


Beispiel eine Session Bean entwickelt. Um später das System zu testen, wird
zudem ein Client erstellt. Abbildungen 3 und 4 zeigen ein UML-Diagramm der
Komponenten.

<<Interface>> <<Interface>> <<Interface>>


javax.ejb.EJBLocalObject javax.ejb.EJBLocalHome javax.ejb.EntityBean
+remove(): void +remove(): void +setEntityContext(ctx:javax.ejb.EntityContext): void
+getEJBLocalHome(): javax.ejb.EJBLocalHome +unsetEntityContext(): void
+...() +ejbRemove(): void
+....()
<<package>>
address_cmp

<<Interface>>
AddressLocal
+getID(): String
+getName(): String
+getFirstName(): String
+getStreet(): String
+getPostcode(): String
+getLocation(): String
+setName(name:String): void
+setFirstName(firstName:String): void
+setStreet(street:String): void
+setPostcode(postcode:String): void
+setLocation(location:String): void

<<Interface>>
AddressLocalHome
+create(addressID:String,name:String,firstName:String,street:String,postcode:String,location:String): AddressLocal
+findByPrimaryKey(addressID:String): AddressLocal
+findByName(name:String): AddressLocal
+findAllAdresses(): java.util.Collection of AddressLocal

AddressBean
+getID(): String
+getName(): String
+getFirstName(): String
+getPostcode(): String
+getLocation(): String
+setID(name:String): void
+setName(name:String): void
+setFirstName(firstName:String): void
+setStreet(street:String): void
+setPostcode(postcode:String): void
+setLocation(location:String): void
+setEntityContext(ctx:javax.ejb.EntityContext): void
+unsetEntityContext(): void
+ejbRemove(): void
+ejbActivate(): void
+ejbPassivate(): void
+ejbLoad(): void
+ejbStore(): void
+ejbCreate(addressID:String,name:String,firstName:String,street:String,postcode:String,location:String): String
+ejbPostCreate(addressID:String,name:String,firstName:String,street:String,postcode:String,location:String): void

Abbildung 3: UML Diagramm der Komponenten von Address


Entwicklung einer Container-Managed EJB am Beispiel einer
Adressdatenbank 49

<<Interface>> <<Interface>> <<Interface>>


javax.ejb.EJBHome javax.ejb.EJBObject javax.ejb.SessionBean
+remove(): void +remove(): void +setSessionContext(ctx:javax.ejb.SessionContext): void
+...() +getEJBHome(): javax.ejb.EJBHome +ejbRemove(): void
+...() +....()
<<package>>
address

<<Interface>>
AddressCoverHome
+create(): AddressCover

<<Interface>>
AddressCover
+setName(addressID:String,name:String): void
+setFirstName(addressID:String,firstName:String): void
+setStreet(addressID:String,street:String): void
+setPostcode(addressID:String,postcode:String): void
+setLocation(addressID:String,location:String): void
+newAddress(addressID:String,name:String,firstName:String,street:String,postcode:String,location:String): void
+findByPrimaryKey(addressID:String): AddressClone
+findByName(name:String): AddressClone
+getAddressCount(): int

AddressCoverBean
+setSessionContext(ctx:javax.ejb.SessionContext): void
+ejbRemove(): void
+ejbActivate(): void
+ejbPassivate(): void
+ejbCreate(): void
+setName(addressID:String,name:String): void
+setFirstName(addressID:String,firstName:String): void
+setStreet(street:String,addressID:String): void
+setPostcode(postcode:String,addressID:String): void
+setLocation(location:String,addressID:String): void
+newAddress(addressID:String,name:String,firstName:String,street:String,postcode:String,location:String): String
+findByPrimaryKey(addressID:String): AddressClone
+findByName(name:String): java.util.Collection of AddressClone
+getAddressCount(): int

AddressClone
-addressID: name
-name: String
-firstName: String
-street: String
-postcode: String
-location: String
+AddressClone(addressId:String,name:String,firstName:String,street:String,postcode:String,location:String): Konstruktor
+getID(): String
+getName(): String
+getFirstName(): String
+getStreet(): String
+getPostcode(): String
+getLocation(): String

Abbildung 4: UML-Diagramm der Komponenten von AddressCover

Die Funktion und Bedeutung der Home- und Remote Schnittstellen wurde
bereits in Kapitel 3 detailliert erläutert. Die Funktion der Methoden ist der
Dokumentation der Klassen beziehungsweise Schnittstellen zu entnehmen.

5.1. Quellcodes

Die folgenden Quelltexte zeigen die Umsetzung der Adressdatenbank. Beson-


ders interessant ist der Quellcode der AddressBean. Die Methoden zur Verwal-
tung der Bean sind leer. Es werden nur abstrakte get... und set... Metho-
den und eine create() Methode definiert. Anhand der abstrakten Methoden
kann der Container später feststellen, welche Daten gespeichert werden sollen.
Entwicklung einer Container-Managed EJB am Beispiel einer
Adressdatenbank 50

Genau dies ist eine der großen Stärken von CMP EJBs. Im Anhang 3 A findet
man die AddressBean, die als Bean Managed Entity Bean realisiert ist. Al-
lein der Quellcode ist mehr als 6 mal so lang wie der Quellcode der Container
Managed Entity Bean. Daraus ist ersichtlich, dass Container Managed Beans
wesentlich schneller zu entwickeln sind.

5.1.1. Quellcode des Local Interfaces für die AddressBean

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: AddressLocal
9 * Date: 28.01.2003
10 * Version: 1.0
11 */
12
13 package address_cmp;
14
15 import javax.ejb.*;
16
17
18 /**
19 * Dies ist das local Interface für die AddressBean. Alle
20 * Methoden die hier deklariert sind, können später von einem
21 * lokalen Client aufgerufen werden. Diese Methoden beziehen
22 * sich auf eine spezielle Bean Instanz.
23 */
24 public interface AddressLocal extends EJBLocalObject{
25
26
27 /**
28 * Gibt den Primärschlüssel zurück
29 */
30 public String getID();
31
3
Der Anhang befindet sich in der digitalen Form (PDF Dokument) der Diplomarbeit und
kann unter [13] heruntergeladen werden.
Entwicklung einer Container-Managed EJB am Beispiel einer
Adressdatenbank 51

32
33 /**
34 * Gibt den Namen des Adressbesitzers zurück
35 * @return Name des Adressbesitzers
36 */
37 public String getName();
38
39
40 /**
41 * Gibt den Vornamen des Adressbesitzers zurück
42 * @return Vorname des Adressbesitzers
43 */
44 public String getFirstName();
45
46
47 /**
48 * Gibt die Strasse der Adresse zurück
49 * @return Strasse der Adresse
50 */
51 public String getStreet();
52
53
54 /**
55 * Gibt die Postleitzahl der Adresse zurück
56 * @return Postleitzahl der Adresse
57 */
58 public String getPostcode();
59
60
61 /**
62 * Gibt die Ort der Adresse zurück
63 * @return Ort der Adresse
64 */
65 public String getLocation();
66
67
68 /**
69 * Setzt den Namen des Adressbesitzers
70 * @param name Name des Adressbesitzers
71 */
72 public void setName(String name);
73
74
75 /**
76 * Setzt den Vornamen des Adressbesitzers
Entwicklung einer Container-Managed EJB am Beispiel einer
Adressdatenbank 52

77 * @param firstName Vorname des Adressbesitzers


78 */
79 public void setFirstName(String firstName);
80
81
82 /**
83 * Setzt die Strasse der Adresse
84 * @param street Strasse der Adresse
85 */
86 public void setStreet(String street);
87
88
89 /**
90 * Setzt die Postleitzahl der Adresse
91 * @param postcode Postleitzahl der Adresse
92 */
93 public void setPostcode(String postcode);
94
95
96 /**
97 * Setzt den Ort der Adresse
98 * @param location Ort der Adresse
99 */
100 public void setLocation(String location);
101 }

5.1.2. Quellcode des Local Home Interfaces für die AddressBean

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: AddressLocalHome
9 * Date: 28.01.2003
10 * Version: 1.0
11 */
12
13 package address_cmp;
14
Entwicklung einer Container-Managed EJB am Beispiel einer
Adressdatenbank 53

15 import javax.ejb.*;
16 import java.util.*;
17
18
19 /**
20 * Dies ist das local home Interface für die AddressBean.
21 * Alle Methoden, die hier deklariert sind, können später von
22 * einem lokalen Client aufgerufen werden. Diese Methoden
23 * beziehen sich nicht auf eine spezielle Bean Instanz.
24 *
25 */
26 public interface AddressLocalHome extends EJBLocalHome{
27
28
29 /**
30 * Erstellt eine neue Adresse
31 * @param addressID Der Primärschlüssel der Adresse. Jede
32 * Adresse benötigt eine eindeutige ID.
33 * @param name Der Name des Adressbesitzers
34 * @param firstName Der Vorname des Adressbesitzers
35 * @param street Die Straße der Adresse
36 * @param postcode Die Postleitzahl der Adresse
37 * @param location Der Ort der Adresse
38 *
39 * @return Das neu erzeugte EJB-Objekt
40 *
41 * @throws CreateException Wird geworfen wenn die adressID
42 * bereits in der Datenbank vorhanden ist.
43 */
44 public AddressLocal create(String addressID, String name,
45 String firstName, String street,
46 String postcode, String location)
47 throws CreateException;
48
49
50 /**
51 * Sucht nach einer Adresse die als ID den übergebenen
52 * Parameter adressID besitzt.
53 * @param adressID ID nach der gesucht werden soll.
54 *
55 * @return Das EJB-Objekt, das die addressID als Primär-
56 * schlüssel hat.
57 */
58 public AddressLocal findByPrimaryKey(String addressID)
59 throws FinderException;
Entwicklung einer Container-Managed EJB am Beispiel einer
Adressdatenbank 54

60
61
62 /**
63 * Sucht nach Adressen, die als Namen den übergebenen
64 * Parameter name besitzen.
65 * @param name Der Name nach dem gesucht werden soll.
66 *
67 * @return Eine Liste von EJB-Objekten die den Parameter
68 * name als Namen haben.
69 */
70 public Collection findByName(String name)
71 throws FinderException;
72
73
74 /**
75 * Liefert alle Datensätze der Adressen-Datenbank zurück
76 * @return Alle Datensätze in der Datenbank
77 */
78 public Collection findAllAdresses() throws FinderException;
79 }

5.1.3. Quellcode der AddressBean

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: AddressBean
9 * Date: 29.01.2003
10 * Version: 1.0
11 */
12 package address_cmp;
13
14 import javax.ejb.*;
15
16
17 /**
18 * Entity Bean zum Demonstrieren von Container-managed
19 * Persistence
Entwicklung einer Container-Managed EJB am Beispiel einer
Adressdatenbank 55

20 *
21 * Dies ist eine persistente Adresse. Sie besitzt einen
22 * Namen, einen Vornamen, eine Straße, eine Postleitzahl und
23 * einen Ort.
24 */
25 public abstract class AddressBean implements EntityBean {
26
27 protected EntityContext ctx;
28
29 /*
30 * Abstrakte get/set Methoden. Diese Methoden werden später
31 * vom Container implementiert.
32 */
33 public abstract String getID();
34 public abstract String getName();
35 public abstract String getFirstName();
36 public abstract String getStreet();
37 public abstract String getPostcode();
38 public abstract String getLocation();
39 public abstract void setID(String id);
40 public abstract void setName(String name);
41 public abstract void setFirstName(String fristName);
42 public abstract void setStreet(String street);
43 public abstract void setPostcode(String postcode);
44 public abstract void setLocation(String location);
45
46
47 /*
48 * Benötigte Methoden einer Entity Bean
49 */
50 public void setEntityContext(EntityContext ctx){
51
52 this.ctx = ctx;
53 }
54
55
56 public void unsetEntityContext(){
57
58 this.ctx = null;
59 }
60
61
62 public void ejbRemove(){
63
64 }
Entwicklung einer Container-Managed EJB am Beispiel einer
Adressdatenbank 56

65
66 public void ejbActivate(){
67
68 }
69
70
71 public void ejbPassivate(){
72
73 }
74
75
76 public void ejbLoad(){
77
78 }
79
80
81 public void ejbStore(){
82
83 }
84
85
86 public String ejbCreate(String addressID, String name,
87 String firstName, String street,
88 String postcode, String location)
89 throws CreateException{
90
91 setID(addressID);
92 setName(name);
93 setFirstName(firstName);
94 setStreet(street);
95 setPostcode(postcode);
96 setLocation(location);
97
98 return addressID;
99 }
100
101
102 public void ejbPostCreate(String addressID, String name,
103 String firstName, String street,
104 String postcode,String location){
105
106 }
107 }
Entwicklung einer Container-Managed EJB am Beispiel einer
Adressdatenbank 57

5.1.4. Quellcode des remote Interfaces für die AddressCoverBean

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: AddressCover
9 * Date: 29.01.2003
10 * Version: 1.0
11 */
12 package address;
13
14 import javax.ejb.*;
15 import java.rmi.*;
16 import java.util.*;
17
18
19 /**
20 * Dies ist das remote Interface für die AddressCoverBean.
21 * Alle Methoden, die hier deklariert sind, können später von
22 * einem remote Client aufgerufen werden.
23 */
24 public interface AddressCover extends EJBObject {
25
26
27 /**
28 * Setzt den Namen des Adressbesitzers mit dem Primär-
29 * schlüssel addressID
30 * @param name Name des Adressbesitzers
31 * @param addressId Primärschlüssel der zu ändernden
32 * Adresse
33 */
34 public void setName(String addressID, String name)
35 throws FinderException,
36 RemoteException;
37
38
39 /**
40 * Setzt den Vornamen des Adressbesitzers mit dem Primär-
41 * schlüssel addressID
42 * @param firstName Vorname des Adressbesitzers
43 * @param addressId Primärschlüssel der zu ändernden
Entwicklung einer Container-Managed EJB am Beispiel einer
Adressdatenbank 58

44 * Adresse
45 */
46 public void setFirstName(String addressID,String firstName)
47 throws FinderException,
48 RemoteException;
49
50
51 /**
52 * Setzt die Strasse der Adresse mit dem Primärschlüssel
53 * addressID
54 * @param street Strasse der Adresse
55 * @param addressId Primärschlüssel der zu ändernden
56 * Adresse
57 */
58 public void setStreet(String addressID, String street)
59 throws FinderException,
60 RemoteException;
61
62
63 /**
64 * Setzt die Postleitzahl der Adresse mit dem Primär-
65 * schlüssel addressID
66 * @param postcode Postleitzahl der Adresse
67 * @param addressId Primärschlüssel der zu ändernden
68 * Adresse
69 */
70 public void setPostcode(String addressID, String postcode)
71 throws FinderException,
72 RemoteException;
73
74
75 /**
76 * Setzt den Ort der Adresse mit dem Primärschlüssel
77 * addressID
78 * @param location Ort der Adresse
79 * @param addressId Primärschlüssel der zu ändernden
80 * Adresse
81 */
82 public void setLocation(String addressID, String location)
83 throws FinderException,
84 RemoteException;
85
86
87 /**
88 * Erstellt eine neue Adresse
Entwicklung einer Container-Managed EJB am Beispiel einer
Adressdatenbank 59

89 * @param addressID Der Primärschlüssel der Adresse. Jede


90 * Adresse benötigt eine eindeutige ID.
91 * @param name Der Name des Adressbesitzers
92 * @param firstName Der Vorname des Adressbesitzers
93 * @param street Die Straße der Adresse
94 * @param postcode Die Postleitzahl der Adresse
95 * @param location Der Ort der Adresse
96 *
97 * @return Das neu erzeugte EJB-Objekt
98 *
99 * @throws CreateException Wird geworfen wenn die adressID
100 * bereits in der Datenbank vorhanden ist.
101 */
102 public void newAddress(String addressID, String name,String
103 firstName, String street, String
104 postcode, String location)
105 throws CreateException,
106 RemoteException;
107
108
109 /**
110 * Sucht nach einer Adresse, die als ID den übergebenen
111 * Parameter adressID besitzt.
112 * @param adressID ID nach der gesucht werden soll.
113 *
114 * @return Das AddressClone Objekt, das die addressID als
115 * Primärschlüssel hat.
116 */
117 public AddressClone findByPrimaryKey(String addressID)
118 throws FinderException,
119 RemoteException;
120
121
122 /**
123 * Sucht nach Adressen, die als Namen den übergebenen
124 * Parameter name besitzen.
125 * @param name Der Name nach dem gesucht werden soll.
126 *
127 * @return Eine Liste von AddressClone Objketen die den
128 * Parameter name als Namen haben.
129 */
130 public Collection findByName(String name)
131 throws FinderException,
132 RemoteException;
133
Entwicklung einer Container-Managed EJB am Beispiel einer
Adressdatenbank 60

134
135 /**
136 * Liefert die Anzahl der Datensätze zurück
137 * @return Anzahl der Datensätze in der Datenbank
138 */
139 public int getAddressCount() throws RemoteException,
140 FinderException;
141 }

5.1.5. Quellcode des remote Home Interfaces für die AddressCoverBean

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: AddressCoverHome
9 * Date: 29.01.2003
10 * Version: 1.0
11 */
12 package address;
13
14 import javax.ejb.*;
15 import java.rmi.*;
16
17
18 /**
19 * Dies ist das home Interface für die AddressCoverBean. Alle
20 * Methoden, die hier deklariert sind, können später von
21 * einem remote Client aufgerufen werden.
22 *
23 */
24 public interface AddressCoverHome extends EJBHome {
25
26
27 public AddressCover create() throws CreateException,
28 RemoteException;
29 }
Entwicklung einer Container-Managed EJB am Beispiel einer
Adressdatenbank 61

5.1.6. Quellcode der AddressCoverBean

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: AddressCoverBean
9 * Date: 29.01.2003
10 * Version: 1.0
11 */
12 package address;
13
14 import javax.ejb.*;
15 import java.rmi.*;
16 import javax.naming.*;
17 import address_cmp.*;
18 import javax.rmi.*;
19 import java.util.*;
20
21
22 /**
23 * Session Bean als Hülle um eine Address Entity Bean
24 *
25 * Diese Bean ermöglicht es auf Address Entity Beans
26 * zuzugreifen. Als Rückgabewert erhält der Client eine Kopie
27 * (AddressClone) der Entity Bean.
28 */
29 public class AddressCoverBean implements SessionBean {
30
31 SessionContext ctx;
32 AddressLocalHome home;
33 AddressLocal addObj;
34
35
36 // Benötitge Methoden einer jeden Session Bean
37 public void setSessionContext(SessionContext ctx){
38
39 this.ctx = ctx;
40 }
41
42
43 public void ejbRemove(){
Entwicklung einer Container-Managed EJB am Beispiel einer
Adressdatenbank 62

44
45 }
46
47
48 public void ejbActivate(){
49
50 }
51
52
53 public void ejbPassivate(){
54
55 }
56
57
58 public void ejbCreate() throws CreateException{
59
60 try{
61 InitialContext addressCtx = new InitialContext();
62 Object obj = addressCtx.lookup
63 ("java:comp/env/ejb/AddressLocalHome");
64 home = (AddressLocalHome) PortableRemoteObject.
65 narrow(obj, AddressLocalHome.class);
66 }catch(Exception e){
67 throw new CreateException("Konnte JNDI Referenz "+
68 "(java:comp/env/ejb/AddressLocalHome) nicht finden");
69 }
70 }
71
72 // Business Methoden
73
74 public void setName(String addressID, String name)
75 throws FinderException{
76
77 addObj = home.findByPrimaryKey(addressID);
78 addObj.setName(name);
79 }
80
81
82 public void setFirstName(String addressID,String firstName)
83 throws FinderException{
84
85 addObj = home.findByPrimaryKey(addressID);
86 addObj.setFirstName(firstName);
87 }
88
Entwicklung einer Container-Managed EJB am Beispiel einer
Adressdatenbank 63

89
90 public void setStreet(String addressID, String street)
91 throws FinderException{
92
93 addObj = home.findByPrimaryKey(addressID);
94 addObj.setStreet(street);
95 }
96
97
98 public void setPostcode(String addressID, String postcode)
99 throws FinderException{
100
101 addObj = home.findByPrimaryKey(addressID);
102 addObj.setPostcode(postcode);
103 }
104
105
106 public void setLocation(String addressID, String location)
107 throws FinderException{
108
109 addObj = home.findByPrimaryKey(addressID);
110 addObj.setLocation(location);
111 }
112
113
114 public void newAddress(String addressID, String name,String
115 firstName, String street, String
116 postcode, String location)
117 throws CreateException{
118
119 home.create(addressID, name, firstName, street, postcode,
120 location);
121 }
122
123
124 public AddressClone findByPrimaryKey(String addressID)
125 throws FinderException{
126
127 addObj = home.findByPrimaryKey(addressID);
128 AddressClone tmp = new AddressClone(addObj.getID(),
129 addObj.getName(),
130 addObj.getFirstName(),
131 addObj.getStreet(),
132 addObj.getPostcode(),
133 addObj.getLocation());
Entwicklung einer Container-Managed EJB am Beispiel einer
Adressdatenbank 64

134 return tmp;


135 }
136
137
138 public Collection findByName(String name)
139 throws FinderException{
140
141 Collection coll = home.findByName(name);
142 Vector vec = new Vector();
143 Iterator iter = coll.iterator();
144 while(iter.hasNext()){
145 addObj = (AddressLocal) iter.next();
146 vec.add(new AddressClone(addObj.getID(),
147 addObj.getName(),
148 addObj.getFirstName(),
149 addObj.getStreet(),
150 addObj.getPostcode(),
151 addObj.getLocation()));
152 }
153
154 return (Collection) vec;
155 }
156
157
158 public int getAddressCount()throws FinderException{
159
160 return home.findAllAdresses().size();
161 }
162 }

5.1.7. Quellcode des Clients

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: Client
9 * Date: 29.01.2003
10 * Version: 1.0
Entwicklung einer Container-Managed EJB am Beispiel einer
Adressdatenbank 65

11 */
12
13 import address.*;
14 import javax.naming.*;
15 import javax.rmi.*;
16 import java.util.*;
17
18
19 /**
20 * Dies ist ein TestClient für die Addressen-Datenbank. Er
21 * greift über die AddressCoverBean auf die AddressBean zu.
22 * Die AddressCoverBean sorgt dafür, dass der Client keine
23 * Referenz auf die Entity Bean erhält, sondern eine Kopie.
24 */
25 public class Client {
26
27 public static void main(String[] args) {
28
29 AddressCoverHome home = null;
30 try{
31 Context ctx = new InitialContext();
32 home = (AddressCoverHome) PortableRemoteObject.narrow
33 (ctx.lookup("ejb/AddressCoverHome"),
34 AddressCoverHome.class);
35 AddressCover addressCover = home.create();
36 addressCover.newAddress(""+System.currentTimeMillis(),
37 "Meier","Rita","Taubenstrasse 5","26723", "Emden");
38 addressCover.newAddress(""+System.currentTimeMillis(),
39 "Meier","Jens","Taubenstrasse 5","26723", "Emden");
40 addressCover.newAddress(""+System.currentTimeMillis(),
41 "Müller","Maria","Am Berg","49661", "Cloppenburg");
42
43 System.out.println("Sucht nach Personen die den Namen"+
44 " Meier tragen");
45 Iterator i = addressCover.findByName("Meier").
46 iterator();
47 while(i.hasNext()){
48 AddressClone addr = (AddressClone) i.next();
49 System.out.println("ID :" +addr.getID());
50 System.out.println("Name :" +addr.getName());
51 System.out.println("Vorname :" +addr.getFirstName());
52 System.out.println("Strasse :" +addr.getStreet());
53 System.out.println("PLZ :" +addr.getPostcode());
54 System.out.println("Ort :" +addr.getLocation());
55 System.out.println("------------------------------");
Entwicklung einer Container-Managed EJB am Beispiel einer
Adressdatenbank 66

56 }
57 System.out.println("Es befinden sich " + addressCover.
58 getAddressCount()+ " Adressen in der Datenbank");
59 }catch(Exception e){
60 e.printStackTrace();
61 }
62 }
63 }

5.2. Erstellen des Delployment - Descriptors

Wie aus Kapitel 3 bekannt ist, können Enterprise JavaBeans auf einem belie-
bigen J2EE kompatiblen Applikations-Server geladen werden. Bevor die EJBs
auf einen Applikations-Server geladen werden kann, muss ein Paket erstellt
werden (package), in dem sich die notwendigen Klassen und Schnittstellen be-
finden. Zudem enthält das Paket Informationen darüber, wie der Applikations-
Server die EJBs zu verwalten hat. Dies wird über einen Deployment Descriptor,
der für jede EJB angelegt werden muss, realisiert. In einem Deployment Des-
criptor wird festgelegt, wer auf eine EJB zugreifen darf, welche Transaktionen
vorliegen, welcher SQL-Ausdruck für eine bestimmte Methode generiert wer-
den soll und noch vieles mehr. Es gibt mehrere Verfahren, einen Descriptor zu
erstellen. Man kann ihn selbst im Editor schreiben, in einer Entwicklungsum-
gebung von einem Wizard generieren lassen oder mit Hilfe eines Werkzeugs
des Applikations-Server generieren. Der Applikations-Server von Sun stellt ein
solches Werkzeug bereit. Die nachfolgenden Schritte erläutern, wie man mit
diesem Werkzeug einen Deployment - Descriptor für die Adressdatenbank ge-
neriert. Es wird davon ausgegangen, dass die folgende Baumstruktur im Pro-
jektverzeichnis vorliegt.
Entwicklung einer Container-Managed EJB am Beispiel einer
Adressdatenbank 67

| Client.class
+---address
| AddressClone.class
| AddressCover.class
| AddressCoverHome.class
| AddressCoverBean.class
+---address_cmp
AddressLocal.class
AddressLocalHome.class
AddressBean.class

Das Werkzeug trägt den Namen deploytool und befindet sich im Unterver-
zeichnis bin des J2EE Paketes. Zunächst muss eine Applikation erzeugt wer-
den. Dies geschieht, indem man File yNew yApplikation... wählt. Im
darauf folgenden Fenster wählt man browse und wechselt zum Projektver-
zeichnis der Adressdatenbank. Als Dateiname wird AdressDB gewählt. In die-
se Applikation werden die einzelnen Komponenten eingebettet. Zunächst wird
die Session Bean eingebettet. Dazu geht man auf File yNew yEnterprise
Bean.... Es wird ein neuer Dialog angezeigt. Auf der zweiten Seite des Dia-
logs werden die Dateien der EJB ausgewählt. Dazu wählt man die Schalt-
fläche edit rechts im Dialog und fügt die Dateien hinzu. Für die Sessi-
on Bean sind das die drei Dateien der Session Bean selbst (AddressCover,
AddressCoverHome, AddressCoverBean), die Schnittstellen der Entity Bean
(AddressLocal, AddressLocalHome) und die Klasse AddressClone. Auf der
nächsten Seite wird spezifiziert, um welchen Typ EJB es sich handelt und wie
die Schnittstellen heißen. Aus Abbildung 5 ist zu entnehmen, wie die Einstel-
lungen zu wählen sind.
Entwicklung einer Container-Managed EJB am Beispiel einer
Adressdatenbank 68

Abbildung 5: Einstellungen der Bean AddressCover

Die nachfolgenden Seiten des Wizards kann man überspringen. Als nächstes
wird die Entity Bean eingehängt. Dazu wählt man File yNew yEnterprise
Bean.... Die Entity Bean besteht nur aus den drei Dateien der Entity Bean
selbst (AddressLocal, AddressLocalHome, AddressBean). Auf der nächsten
Seite des Wizard sind die Einstellungen so zu wählen, wie Abbildung 6 es zeigt.
Entwicklung einer Container-Managed EJB am Beispiel einer
Adressdatenbank 69

Abbildung 6: Allgemeine Einstellungen der Bean Address

Wie erwähnt, müssen die Methoden einer CMP Enterprise Java Bean mit
Datenabfragebefehlen versorgt werden. Dies geschieht auf der nächsten Seite
des Wizards. Abbildung 7 zeigt, wie die Einstellungen zu wählen sind.
Entwicklung einer Container-Managed EJB am Beispiel einer
Adressdatenbank 70

Abbildung 7: Spezielle Einstellungen der Bean Address

Aus den abstrakten Methoden get... und set... stellt der Container fest,
welche Daten in der Datenbank gespeichert werden sollen. Die Attribute wer-
den manuell im Listenfeld gewählt. Damit der Container eine Adresse eindeu-
tig findet, wird ein Primärschlüssel gewählt. Bei der Adressdatenbank ist der
Primärschlüssel das Feld iD vom Typ String. Der Container selbst kann an-
hand dieser Informationen die allgemeinen Datenbankbefehle generieren. Der
Benutzer muss lediglich die find...(...) Datenbankabfragen selber imple-
mentieren. Dazu wählt er die Schaltfläche Finder/Select Methods... und
trägt die EJB-QL Befehle ein. Tabelle 5 zeigt die Befehle für die Adressdaten-
bank.

EJB-QL ist eine Erweiterung von SQL. Jeder, der SQL Befehle schreiben kann
und ein wenig objektorientiertes Programmieren versteht, wird auch ohne Do-
kumentation EJB-QL Befehle schreiben können. Es besitzt die gleichen Schlüs-
selwörter wie SQL und bietet zusätzlich die Alternative, auf nicht relationale
Entwicklung einer Container-Managed EJB am Beispiel einer
Adressdatenbank 71

Datenbanken zuzugreifen. Der Container generiert mit Hilfe des JDBC Trei-
bers die datenbankspezifischen Befehle. Der Vorteil dieses Mechanismus ist,
dass Unternehmen, die persistente EJBs erwerben, nicht an spezielle Daten-
banken gebunden sind. Mehr Informationen zu EJB-QL findet man unter [6].

Methode EJB-QL Befehl


findAllAddresses() SELECT OBJECT(a) FROM AddressBean AS a
findByName() SELECT OBJECT(a) FROM AddressBean AS a
WHERE a.name = ?1

Tabelle 5: EJB-QL Befehle für die Bean Address

Damit die Home-Objekte später gefunden werden, sind JNDI-Namen zu ver-


geben. Dazu wählt man EJB1 beziehungsweise EJB2 aus und trägt jeweils ejb/
AddressCoverHome beziehungsweise ejb/AddressLocalHome als JNDI-Namen
ein. Da die Session Bean intern die Entity Bean nutzt, muss sie zusätzlich noch
eine Referenz zur Adress Entity Bean haben. Abbildung 8 zeigt, wie die Ein-
stellungen zu wählen sind.
Entwicklung einer Container-Managed EJB am Beispiel einer
Adressdatenbank 72

Abbildung 8: EJB Referenzen der Bean AddressCover

Damit sind alle Schritte, um einen Deployment - Descriptor zu erstellen,


erläutert. Die einzelnen Descriptoren kann man sich mit einem Rechtsklick auf
die Bean ansehen. Um eine oder mehrere EJBs auf einen Applikations-Server
heraufzuladen, wird ein Archiv generiert. Dieses Archiv trägt die Dateiendung
ear und ist eine Erweiterung des Java Archives (JAR). Im Deployment Tool
kann man mit Hilfe des Befehles File ySave As... ein solches Archiv erstel-
len. Die ear-Datei kann nun an Dritte weitergegeben werden und auf einem
J2EE kompatiblen Applikations-Server abgelegt werden.

5.3. Enterprise JavaBeans auf einen Applikations-Server


heraufladen (deploy)

Im letzten Abschnitt wurde ein Archiv erstellt, das direkt auf einen Applika-
tions-Server geladen werden kann. Die folgenden Schritte erläutern, wie man
Entwicklung einer Container-Managed EJB am Beispiel einer
Adressdatenbank 73

dieses Archiv auf den Applikations-Server von Sun herauflädt. Bevor man die
Applikation herauflädt, sind noch ein paar Vorbereitungen zu treffen. Es wird
davon ausgegangen, dass bereits eine Datenbank installiert wurde und sich die
Tabelle AddressBeanTable darin befindet. Der folgende Ausdruck zeigt, wie
die Tabelle mit Hilfe eines SQL-Befehls erzeugt werden kann. Mehr Informa-
tionen zum Installieren einer Datenbank findet man unter [12, 14].

CREATE TABLE AddressBeanTable (


firstName VARCHAR(255),
iD VARCHAR(255),
location VARCHAR(255),
name VARCHAR(255),
postcode VARCHAR(255),
street VARCHAR(255),
PRIMARY KEY (iD)
)
Damit der Applikations-Server die Datenbank später findet und einer Ap-
plikation zuordnen kann, wird ein JNDI Name erzeugt, der auf die URL
der Datenbank verweist. Dies kann man mit Hilfe des Befehls j2eeadmin
-addJdbcDatasource <JNDI-Name> <URL>, der sich ebenfalls im Unterver-
zeichnis bin des J2EE Paketes befindet, tun. Mehr Informationen hierzu findet
man unter [14].

Nun kann der eigentliche Vorgang des Heraufladens beginnen. Dazu wird
erst einmal der J2EE Server gestartet. Dies geschieht mit dem Befehl
j2ee -verbose. Zum Heraufladen wird das bekannte Deploytool verwendet.
Zunächst erhält die AddressBean eine Referenz zur Datenbank. Dazu öff-
net man das generierte Archiv aus dem vorherigen Kapitel und wählt un-
ter AddressBean den Reiter Entity. Ein Klick auf Deployment Settings...
öffnet ein neues Fenster, indem die Einstellungen für die Datenbank vorge-
nommen werden. Da bereits eine Tabelle in der Datenbank erzeugt wurde,
werden die beiden Häkchen von create table on deploy und delete table
Entwicklung einer Container-Managed EJB am Beispiel einer
Adressdatenbank 74

on undeploy entfernt. Unter Database Settings... wird dann der Name der
Datenbank angegeben. Als JNDI Name wird der Name gewählt, der zusammen
mit der Datenbank-URL verwendet wurde. Falls Benutzername und Kennwort
erforderlich sind, um auf die Datenbank zuzugreifen, müssen diese Angaben
ebenfalls gemacht werden. Die Schaltfläche Generate Default SQL... gene-
riert dann automatisch die SQL Befehle. Mit Hilfe von Tools yDeploy...
werden Archiv und spezielle Einstellungen auf den Server geladen. Auf der
ersten Seite sollte unbedingt Return Client Jar angeklickt werden. Dieses
Jar-Archiv enthält später alle notwendigen Klassen und Schnittstellen für einen
Client, der auf diese Applikation zugreifen möchte. Die anderen Seiten können
wieder übersprungen werden.

5.4. Starten des Clients und Testen der Applikation

Um eigenständige Applikationen zu starten, die auf den J2EE Server von Sun
zugreifen, sollte man den Befehl runClient ebenfalls im Unterverzeichnis bin
des J2EE Paketes benutzen. Dieser Befehl fordert ein Jar-Archiv des Clients,
das wiederum mit Hilfe des Deploytools generiert werden kann. Dazu wählt
man File yApplikation Client und wählt auf der zweiten Seite die Datei
Client.class aus. Die anderen Seiten können übersprungen werden. Das Jar-
Archiv kann man generieren, indem man zuerst den Client auswählt und dann
auf File ySave AS... klickt. Mehr Informationen zu Jar-Dateien und wie
man diese auch auf der Konsole generiert, findet man unter [15].

Nun wird der Client gestartet. Es wird davon ausgegangen, dass der
Applikations-Server sich lokal befindet, die Client Jar-Datei den Namen client
.jar und die Jar-Datei mit den notwendigen Klassen und Schnittstellen den
Namen AddressDBClient.jar trägt. Folgende Befehlsfolge startet die Client-
Applikation.
Entwicklung einer Container-Managed EJB am Beispiel einer
Adressdatenbank 75

set VMARGS=-Dorg.omg.CORBA.ORBInitialHost=localhost
set APPCPATH=AddressDBClient.jar
runclient -client Client.jar -name TextClient -textauth
Als Benutzername und Passwort wird irgendetwas eingetragen, da keine Sicher-
heitsfunktionen für die Applikation festgelegt worden sind. Wenn alles richtig
gemacht wurde, erscheint die folgende Ausgabe.
Initiating login ...
Enter Username:irgendwas
Enter Password:irgendwas
Sucht nach Personen die den Namen Meier tragen
ID :1043940932164
Name :Meier
Vorname :Rita
Strasse :Taubenstrasse 5
PLZ :26723
Ort :Emden
--------------------------------------
ID :1043940932194
Name :Meier
Vorname :Jens
Strasse :Taubenstrasse 5
PLZ :26723
Ort :Emden
--------------------------------------
Es befinden sich 3 Adressen in der Datenbank
Grundlagen zu RMI 76

6. Grundlagen zu RMI

Dieses Kapitel befaßt sich mit RMI, jedoch nicht mit RMI IIOP, das in Enter-
prise JavaBeans Umgebungen verwandt wird. Beide Technologien sind bis auf
ein paar Unterschiede identisch. Die Unterschiede werden in Kapitel 8 erklärt.
Deshalb kann dieses Kapitel auch ohne Vorkenntnisse über Enterprise JavaBe-
ans (EJBs) gelesen werden. Falls bereits Vorkenntnisse über EJBs vorhanden
sind, wird dieses Kapitel zudem noch einen Einblick darüber geben, wie die
eigentliche Kommunikation zwischen einem Client und der EJB verläuft.

Java-RMI (Remote Method Invocation) ist Teil der Java-2-Plattform. Die-


ses Gerüst (Framework) erlaubt die Kommunikation zwischen Java-Objekten
auf verschiedenen virtuellen beziehungsweise physikalischen Maschinen. RMI-
Applikation werden üblicherweise in zwei Teilen entwickelt: für einen Client
und einen Server. Eine typische Server-Applikation erzeugt Objekte (sogenann-
te entfernte Objekte4 ) und erlaubt es Clients, diese Objekte aufzurufen. Es gibt
zwei typische Mechanismen, eine Referenz auf ein entferntes Objekt zu erhal-
ten.

via RMI-Registry(Name Server) Der Server registriert ein entferntes Objekt


in der RMI-Registry. Dabei wird ein eindeutiger Name angegeben, unter
dem das Objekt später wiedergefunden wird. Dieser Vorgang wird auch
als naming oder binding bezeichnet. Der Client nutzt später den eindeu-
tigen Namen, um eine Referenz auf das entfernte Objekt zu bekommen.
Diesen Vorgang nennt man lookup.

via Referenz Methoden können als Parameter oder als Rückgabewerte Re-
ferenzen auf entfernte Objekte übergeben bekommen beziehungsweise
zurückgeben.

4
In einigen Java Dokumentationen wird auch der englische Begriff remote object verwendet.
Grundlagen zu RMI 77

Die eigentliche Kommunikation übernimmt die RMI-Umgebung der Java Vir-


tual Machine (JVM). Der Programmierer kann Referenzen auf entfernte Ob-
jekte wie Referenzen auf lokale Objekte benutzen. Um ein Objekt in die RMI-
Registry zu binden beziehungsweise dort zu finden, werden Methoden der Klas-
se java.rmi.Naming aufgerufen. Somit erlaubt RMI es auf einfache Weise, eine
verteilte Applikation zu schreiben.

Abbildung 9 zeigt noch einmal, wie die Kommunikation via RMI-Registry


abläuft. Zuerst registriert der Server ein entferntes Objekt in der RMI-Registry.
Anschließend holt sich ein Client eine Referenz auf das entfernte Objekt. Be-
sitzt ein Client eine Referenz auf ein entferntes Objekt, kann er direkt Metho-
den auf dem Originalobjekt aufrufen. Die RMI-Registry wird also nicht weiter
genutzt. Sie ist ein Hilfsmittel, um ein Objekt zu finden. Ähnlich ist es im
wirklichen Leben. Wenn man die Telefonnummer einer Person haben möchte,
weiß man oft nur den Namen der Person. Mit Hilfe einer Telefonauskunft be-
kommt man die Telefonnummer. Für das eigentliche Telefonat braucht man
die Telefonauskunft nicht mehr. Sie ist nur ein Hilfsmittel.

Abbildung 9: Kommunikation via RMI-Registry

RMI bietet weitere Vorteile, wie beispielsweise das automatische Herunterladen


von Klassen oder der verteilte Garbage Collector. Mehr Informationen dazu
findet man unter [16].

6.1. Entwurf einer verteilten Applikation mit Hilfe von RMI

Bei der Entwicklung einer verteilte Applikation mit Hilfe von RMI wird
zunächst entschieden, welche Objekte später entfernten (remote) Clients
zugänglich sein sollen. Für jede dieser Klassen wird eine Schnittstelle (remote
Interface) implementiert. Diese Schnittstelle definiert die Methoden, die später
Grundlagen zu RMI 78

vom Client aufgerufen werden können. Ein remote Interface hat zwei typische
Merkmale:

• Ein remote Interface muss die Schnittstelle java.rmi.Remote erben. Je-


des Objekt, dass diese Schnittstelle implementiert, wird automatisch zu
einem entfernten Objekt.

• Jede Methode der Schnittstelle kann eine java.rmi.RemoteException


werfen. Um dies zu gewährleisten, deklariert jede Methode in ihrer
throws-Klausel diese Exception. Im Falle eines Fehlers (z.B. Netzwerk-
fehler) bekommt der Client automatisch eine solche Exception. Mehr
Informationen zu dieser Exception findet man unter [17].

Jede Klasse, deren Instanzen später von entfernten Clients aufgerufen werden
sollen, implementiert ein oder mehrere remote Interfaces. Diese Klasse kann
weitere Methoden definieren, diese sind jedoch nur lokal verfügbar. Nach dem
Übersetzen der Schnittstellen und Klassen werden Stubs und Skeletons gene-
riert. RMI benutzt einen Stub als Proxy auf der Client-Seite, um eine Referenz
auf das Originalobjekt zu simulieren. Der Skeleton befindet sich auf der Server-
Seite. Seit dem JDK 1.2 wird kein Skeleton verwendet, da sich das Protokoll
geändert hat. Die meisten Tools generieren zwar die Skeletons mit, aber die
Skeletons werden für den Betrieb der Applikation nicht eingesetzt. Klassen
und Schnittstellen werden mit dem Befehl javac kompiliert, Stubs und Ske-
letons mit dem Befehl rmic. Nach dem Übersetzen und Generieren kann die
Applikation verteilt ausgeführt werden. Im Allgemeinen wird zuerst die RMI-
Registry gestartet, dann der Server und zuletzt der Client. Abbildung 10 zeigt
den strukturellen Aufbau einer Kommunikation mit RMI. Weitere Informatio-
nen zu RMI sind unter [18, 16] zu finden. Im nächsten Kapitel wird ein kleines
RMI-Beispiel entwickelt und detailliert besprochen.
Grundlagen zu RMI 79

Abbildung 10: Kommunikation zwischen Client und Server via RMI


Entwicklung eines einfachen RMI Beispiels (Modifier) 80

7. Entwicklung eines einfachen RMI Beispiels


(Modifier)

In diesem Kapitel wird eine einfache RMI-Applikation entwickelt, die zeigen


soll, dass ein entfernter Client Daten in einem Objekt außerhalb seiner JVM
ändern kann. Die einzelnen Schritte werden detailliert erläutert, damit das
Beispiel ohne weiteres nachprogrammiert werden kann. Es ist lediglich ein JDK
ab der Version 1.2 erforderlich, um diese kleine Applikation zu entwickeln und
zu starten. Zunächst wird die Entwicklung in vier Teile aufgeteilt:

• Entwurf und Implementation der Klassen und Schnittstellen

• Kompilieren der Quelltexte und Erzeugen der Stubs

• Klassen und Schnittstellen auf die Rechner verteilen

• Start und Test der Applikation

7.1. Entwurf und Implementation der Klassen und


Schnittstellen

Der Entwurf und die Implementation der Komponenten ist der größte Teil der
Arbeit bei der Entwicklung verteilter RMI-Applikationen. Diese Aufgabe kann
weiter unterteilt werden in:

• Anfertigen einer Server-Applikation

– Entwurf eines remote Interfaces

– Implementierung des remote Interfaces

• Erstellen einer Client Applikation


Entwicklung eines einfachen RMI Beispiels (Modifier) 81

Abbildung 11 zeigt ein UML Diagramm (Unified Modeling Language)


bezüglich der zu entwickelnden Applikation.

<<Interface>> java.rmi.server.UnicastRemoteObject
java.rmi.Remote +.....
+...()
+clone(): java.lang.Object
+exportObject(): Remote
<<package>>
myRmi

<<Interface>>
IServer
+increase(): void
+decrease(): void

Modifier Server
+counter
+static main(args:String[]): void
-Server(): Konstruktor
+static main(args:String[]): void

Abbildung 11: UML Diagramm für das RMI-Beispiel

Der Modifier (Client in diesem Falle) und der Server sind beide eigenständi-
ge Applikationen und werden später auf zwei verschiedene Rechner verteilt.
Der Server wird als entferntes Objekt entworfen, der Client soll später die-
ses entfernte Objekt nutzen. Dies bedeutet: der Modifier hat Zugriff auf alle
Methoden, die in dem remote Interface IServer deklariert sind, in diesem Fal-
le also die Methoden increase() und decrease(). Um zu zeigen, dass ein
entfernter Client die Daten des entfernten Objekts ändern kann, wird bei je-
dem Aufruf der Methoden increase() und decrease() der aktuelle Wert der
Variablen int counter auf der Konsole ausgegeben.

7.1.1. Entwurf eines remote Interfaces

Das remote Interface definiert die Methoden, die von einem entfernten Client
aufgerufen werden. Der folgende Code zeigt den Quellcode des remote Inter-
faces IServer.
Entwicklung eines einfachen RMI Beispiels (Modifier) 82

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: IServer
9 * Date: 02.02.2003
10 * Version: 1.0
11 */
12
13 package myRmi;
14
15 import java.rmi.*;
16
17
18 /**
19 * Remote interface für das Server Objekt.
20 */
21 public interface IServer extends Remote {
22
23 /**
24 * Inkrementiert den Integer Wert des Variablen count;
25 */
26 public void increase() throws RemoteException;
27
28
29 /**
30 * Dekrementiert den Integer Wert des Variablen count;
31 */
32 public void decrease() throws RemoteException;
33 }

Beim Erben der Schnittstelle java.rmi.Remote wird die Schnittstelle auto-


matisch zu einem remote Interface. Jedes Objekt, dass diese Schnittstelle im-
plementiert, wird automatisch zum entfernten Objekt (remote Objekt). Da alle
Methoden in dieser Schnittstelle von außen her aufrufbar sind, müssen sie
alle eine java.rmi.RemoteException werfen können. Diese Exception wird
automatisch von der RMI Umgebung geworfen, wenn Fehler bei der Kommu-
nikation oder ähnlichem auftauchen. Der Client, der diese Methoden aufruft,
Entwicklung eines einfachen RMI Beispiels (Modifier) 83

muss diese Exception entsprechend abfangen. Als Parameter und Rückgabe-


typen der remote Interface Methoden dürfen nur entfernte Objekte, primitive
Typen und Objekte, die die Schnittstelle java.io.Serializable implemen-
tieren, verwendet werden. RMI nutzt den Objektserialisationsmechanismus,
um Objekte von einer JVM zu einer anderen JVM zu transportieren. Das be-
deutet, dass immer, wenn ein nicht entferntes Objekt“ als Parameter für eine

remote Methode benutzt wird, eine Kopie des Objektes erzeugt wird. Somit
unterstützt RMI die beiden Verfahren Parse-by-Referenz und Parse-by-Value
je nach dem, ob es sich um entferntes Objekt handelt oder nicht. Mehr Informa-
tionen über den Objektserialisationsmechanismus ist unter [19] nachzulesen.

7.1.2. Implementierung eines remote Interfaces

Jedes Objekt, das ein remote Interface implementiert, wird automatisch zu


einem entfernten Objekt. Die folgende Liste zeigt, was die Implementation der
Klasse Server mindestens leisten muss.

• Implementation eines remote Interfaces

• Definieren eines Konstruktors, der eine java.rmi.RemoteException


werfen kann

Da eine Referenz des Server später in die RMI-Registry eingetragen wird, wird
eine Methode main() deklariert. Die main() Methode sollte folgende Aufgaben
übernehmen.

5
• Falls notwendig, einen Security Manager bereitstellen

• Erzeugen eines entfernten Objekts

• Registrieren des entfernten Objekts in der RMI-Registry

5
Mehr Informationen zu Security Manager findet man unter [20, 16]
Entwicklung eines einfachen RMI Beispiels (Modifier) 84

Der folgende Code zeigt den Quellcode der Klasse Server. Es implementiert
das remote Interface IServer und bindet in der Methode main() ein entferntes
Objekt in die RMI-Registry.

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: Server
9 * Date: 02.02.2003
10 * Version: 1.0
11 */
12 package myRmi;
13
14 import java.rmi.*;
15 import java.rmi.server.UnicastRemoteObject;
16
17 /**
18 * Remote Objekt, das zur Demonstration dient. Ein Client auf
19 * einer anderen JVM kann Methoden dieser Klasse aufrufen.
20 * Aufrufbare Methoden sind im remote Interface (IServer)
21 * deklariert.
22 */
23 public class Server extends UnicastRemoteObject
24 implements IServer {
25
26 int counter;
27
28 /**
29 * Standard-Konstruktor
30 * Muss für jedes remote Objekt definiert werden
31 */
32 private Server() throws RemoteException {
33
34 }
35
36
37 public void increase() throws RemoteException {
38
39 counter++;
40 System.out.println("Variable inkrementiert :"+counter);
Entwicklung eines einfachen RMI Beispiels (Modifier) 85

41 }
42
43
44 public void decrease() throws RemoteException {
45
46 counter--;
47 System.out.println("Variable dekrementiert :"+counter);
48 }
49
50
51 /**
52 * Bindet ein Server Objekt in die RMI-Registry.
53 * @param args[0] Zu verwendende URL für das remote Objekt
54 */
55 public static void main(String[] args) {
56
57 try {
58 if (args.length > 0) {
59 Naming.rebind("rmi://"+ args[0] + "/Server",
60 new Server());
61 System.out.println("Server gestarted");
62 } else {
63 System.out.println("Geben Sie bitte die IP Adresse "+
64 "bzw. Hostname der rmi-Registry "+
65 "als Argument an ");
66 System.out.println("Zum Beispiel java Server 192.168."+
67 "0.5");
68 }
69 } catch (Exception exc) {
70 exc.printStackTrace();
71 }
72 }
73 }

Dem Quellcode ist zu entnehmen, dass der Server die Klasse java.rmi.
UnicastRemoteObject erbt. Da das Verhalten von entfernten Objekten an-
ders ist, als das von lokalen, sollten auch die Standard-Methoden ( equals(),
hashCode(), toString() ...) von java.lang.Objct neu definiert werden. Die
Klasse UnicastRemoteObject überschreibt diese Methoden. Außerdem besitzt
die Klasse weitere Konstruktoren und Methoden, die später für die Kom-
munikation zuständig sind. Ein entferntes Objekt muss aber nicht zwingend
Entwicklung eines einfachen RMI Beispiels (Modifier) 86

von dieser Klasse abgeleitet sein. Man muss nur dafür sorgen, dass die Stan-
dardmethoden auf eine remote-Umgebung angepaßt werden. Das Erben der
Klasse UnicastRemoteObject bietet einem einen einfachen Weg, diese Arbeit
nicht selbst programmieren zu müssen. Leider gibt es in Java keine Mehr-
fachvererbung, dass heißt, das, falls die Klasse auch eine andere Klasse erben
soll, man selbst die Methoden überschreiben muss. Weitere Informationen zu
UnicastRemoteObject findet man in der Java API unter [21].

Die Server-Klasse implementiert weitere Methoden, die nicht im remote In-


terface deklariert sind. Wie bereits beschrieben, können diese Methoden nicht
von einem Client aufgerufen werden. Jedes entfernte Objekt muss einen Stan-
dardkonstruktor deklarieren, der eine RemoteException werfen kann. Damit
später auf ein entferntes Objekt zugegriffen werden kann, muss es vorher ex-
portiert werden. Dies wird dadurch realisiert, dass das Objekt auf einem be-
stimmten Port des Systems mithört. Dieser Schritt wird beim Erzeugen der
Klasse von der RMI-Umgebung auomatisch erledigt. Desweiteren hat die Klas-
se Server eine Methode main(), in der ein entferntes Objekt erzeugt wird
und in die RMI-Registry eingetragen wird. Um Objekte in die RMI-Registry
einzutragen, benutzt man die Methode Naming.bind(name : String, obj :
Remote). Der erste Parameter ist dabei ein URL String, der den Ort, Port
der RMI-Registry und den Namen des Objektes spezifiziert. Die Syntax lautet
//host:port/objectname. Es müssen aber nicht alle Angaben gemacht wer-
den. Wird host oder port nicht angegeben, so wird automatisch localhost
beziehungsweise 1099 als Port gewählt. Es ist also lediglich der Name des
Objektes anzugeben. Wichtig ist, dass der Name eindeutig ist. Es ist nicht
erlaubt, mehrere Objekte unter dem gleichen Namen in der RMI-Registry ab-
zulegen. Ist bereits ein Objekt unter dem Namen in der RMI-Registry vor-
handen, so liefert die Methode bind(...) eine Exception. Mit der Methode
Naming.rebind(name : String, obj : Remote) kann man solche Excepti-
ons umgehen, denn diese Methode überschreibt gegebenenfalls die Referenz.
Mehr Informationen zu der Klasse Naming findet man unter [22].
Entwicklung eines einfachen RMI Beispiels (Modifier) 87

Leider können in der RMI-Registry nur entfernte Objekte, die sich lokal zur
RMI Registry befinden, abgelegt werden. Das Erfragen eines remote Objektes
kann jedoch von jedem beliebigen Client aus geschehen. Wenn man ein ent-
ferntes Objekt nicht lokal in einer RMI-Registry ablegen möchte, wählt man
einen anderen Naming and Directory Service. Der Zugriff erfolgt dann mit Hilfe
von JNDI. Dieses Werkzeug wird von den meisten J2EE Applikations-Servern
genutzt. Mehr Informationen findet man unter [3].

7.1.3. Eine Client Applikation erstellen

Der folgende Code zeigt den Quellcode der Klasse Modifier.

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: Modifier
9 * Date: 02.02.2003
10 * Version: 1.0
11 */
12
13 package myRmi;
14
15 import java.rmi.*;
16 import java.io.*;
17
18
19 /**
20 * Diese Klasse bietet die Möglichkeit, die Server-Anwendung
21 * zu testen. Sie holt sich eine Referenz auf das Server-
22 * Objekt und führt daraufhin eine Methode auf dieses Objekt
23 * durch. Damit diese eigenständige Anwendung erfolgreich
24 * ausgeführt werden kann, braucht sie als Argument den Ort
25 * der RMI-Registry des Server Objektes.
26 */
27 public class Modifier {
28
Entwicklung eines einfachen RMI Beispiels (Modifier) 88

29 public static void main(String[] args) {


30
31 try {
32 if (args.length > 0) {
33 IServer server = (IServer) Naming.lookup("rmi://"+
34 args[0] + "/Server");
35 System.out.println("Modifier gestarted");
36 System.out.println("+ für increase()\n- für "+
37 "decrease()\nexit zum beenden der Anwendung\n");
38
39 BufferedReader in = new BufferedReader(new
40 InputStreamReader(System.in));
41 String input;
42 while ((input = in.readLine()) != null &&
43 !input.equalsIgnoreCase("exit")) {
44 if (input.equals("+")) {
45 server.increase();
46 } else if (input.equals("-")) {
47 server.decrease();
48 } else {
49 System.out.println("+ für increase()\n- to "+
50 "decrease()\nexit zum Beenden der Anwendung\n");
51 }
52 }
53 } else {
54 System.out.println("Geben Sie bitte die IP Adresse "+
55 "bzw. Hostname der rmi-Registry "+
56 "als Argument an ");
57 System.out.println("Zum Beispiel java Server 192.168."+
58 "0.5");
59 }
60 } catch (Exception exc) {
61 exc.printStackTrace();
62 }
63 }
64 }

Die Client Applikation gibt dem Benutzer die Gelegenheit, den Wert der Varia-
blen count interaktiv zu verändern. Startet man den Client, wird zunächst eine
Referenz auf das entferntes Objekt geholt. Dazu wird die Methode Naming.
lookup(name : String) aufgerufen. Diese Klasse wurde auch zum Binden des
Objektes benutzt und der Parameter name hat die gleiche Syntax (//host:
Entwicklung eines einfachen RMI Beispiels (Modifier) 89

port/objectname) und Standardwerte wie die Methoden bind(...) bezie-


hungsweise rebind(...). Um ein entferntes Objekt aufzufinden, wird der selbe
Name angegeben wie beim Ablegen. Die Methode lookup(...) liefert darauf-
hin eine Referenz zurück, die durch einen Stub realisiert wird. Damit der Client
das entfernte Objekt findet, muss der Server und die RMI-Registry zuvor gest-
artet werden. Wenn der Client erfolgreich gestartet ist, können mit Hilfe der +
und - Tasten, gefolgt von Enter, die Methoden increase() und decrease()
auf dem Server aufgerufen werden. Im Quellcode selbst ist nicht direkt zu
erkennen, dass es sich um ein entferntes Objekt handelt. Der Programmierer
kann das Objekt so verwenden, als sei es lokal. Dies ist eine der großen Stärken
von RMI. Abbildung 12 zeigt die einzelnen Schritte der Kommunikation.

Modifier ooku p(name) RMI-Registry


2: Naming.l
(Client)
3:
inc 1: Naming.
rea
se( rebind(name)
) , d
ecr
eas
e( )

Server

Abbildung 12: Kommunikation zwischen Client und Server

7.2. Übersetzen der Quelltexte und Erzeugen der Stubs

Normalerweise werden nicht alle Komponenten einer verteilten Anwendung


von einem Programmierer erstellt. Im Allgemeinen werden die Komponen-
ten aufgeteilt. Zunächst werden Schnittstellen entwickelt. Die Schnittstellen
werden dann an die Programmierer vergeben und die Anwendung implemen-
tiert. In diesem Beispiel würde das bedeuten, dass als erstes die Schnittstelle
IServer entwickelt worden wäre. Anschließend hätte ein Programmierer die
Klasse Server, ein weiterer die Klasse Client entwickelt. Beide Klassen brau-
chen zum Übersetzen nur die Schnittstelle IServer. Handelt es sich um meh-
Entwicklung eines einfachen RMI Beispiels (Modifier) 90

rere Schnittstellen oder Klassen, die verteilt werden sollen, wird auch oft ein
JAR-Archiv erstellt. Mehr Informationen findet man unter [15].

Nach dem Erzeugen der Quelltexte für die benötigten Klassen und Schnittstel-
len werden sie mit dem Kommando javac übersetzt. Die Stubs und Skeletons
werden mit Hilfe des Kommandos rmic generiert. Diese beiden Befehle sind im
Software Development Kit (SDK) enthalten. Um RMI zu nutzen, sollte eine
Version ab 1.2 verwendet werden. Die folgenden Schritte zeigen, welche Befeh-
le für Windows/Win32 und UNIX/LINUX Betriebssysteme notwendig sind.
Dabei beziehen sich die Befehle darauf, dass sich der Quellcode unter c:\user
für Rechner mit Windows Betriebsystem und /home/user für Rechner mit
LINUX Betriebsystem befindet.

Übersetzen des remote Interfaces auf Windows Betriebsystemen


cd d:\user
javac myRmi\IServer.java
Übersetzen des remote Interfaces auf UNIX Betriebsystemen
cd /home/user
javac myRmi/IServer.java
Übersetzen des Servers und Erzeugen des Stubs auf Windows Be-
triebsystemen
cd d:\user
javac myRmi\Server.java
rmic myRmi.Server
Übersetzen des Servers und Erzeugen des Stubs auf UNIX Betrieb-
systemen
cd /home/user
javac myRmi/Server.java
rmic myRmi.Server
Übersetzen des Clients auf Windows Betriebsystemen
cd d:\user
javac myRmi\Modifier.java
Entwicklung eines einfachen RMI Beispiels (Modifier) 91

Übersetzen des Clients auf UNIX Betriebsystemen


cd /home/user
javac myRmi/Modifier.java
Nach dem Übersetzen der einzelnen Quellcodes sollten sich nun folgende Da-
teien im Verzeichnis c:\user\myRmi bzw. /home/user/myRmi befinden.
IServer.class
Server.class
Modifier.class
Server Stub.class
Server Skel.class

7.3. Klassen und Schnittstellen auf die Rechner verteilen

Bevor die Applikation gestartet werden kann, müssen die Dateien für Server
und Client auf diese Rechner verteilt werden. Das bedeutet: der Client braucht
die Stubs und remote Interfaces für die von ihm aufgerufen entfernten Objek-
te. Es ist sogar realisierbar, die Stubs während der Ausführung automatisch
herunterzuladen. Dies setzt aber voraus, dass ein Security Manager installiert
ist. Weitere Informationen dazu findet man unter [23].

In diesem Beispiel wird davon ausgegangen, dass Client und Server alle not-
wendigen Dateien besitzten. Der Client greift auf die Dateien Modifier.class,
Server Stub.class und IServer.class zu. Der Server besitzt die Dateien
Server.class, Server Stub.class und IServer Stub.class.

7.4. Start und Test der Applikation

Die folgenden Befehle gehen davon aus, dass sich die notwendigen Dateien
in der selben Verzeichnisstruktur befinden wie in Abschnitt 7.2. Zuerst wird
die RMI-Registry, dann der Server und zuletzt der Client gestartet. Die RMI-
Registry ist ebenfalls im SDK enthalten. Beim Start der RMI-Registry muss
Entwicklung eines einfachen RMI Beispiels (Modifier) 92

darauf geachtet werden, dass alle Klassen der zu registrierenden Objekte sich
im CLASSPATH befinden. Am einfachsten erreicht man dies, indem man die
RMI-Registry im Verzeichnis der Klassen startet.
Starten der RMI-Registry und des Servers auf Windows-
Betriebsystemen
cd d:\user
start rmiregistry
java myRmi.Server 192.168.0.5
Starten der RMI-Registry und des Servers auf UNIX-
Betriebsystemen
cd /home/user
rmiregistry &
java myRmi.Server 192.168.0.5
Der Server erhält als Argument die IP-Adresse des Rechners, auf dem sich die
RMI-Registry befindet. Da die RMI-Registry es nur erlaubt, lokale Objekte
abzulegen, muss sich eine RMI-Registry auf dem Rechner des Servers befinden.
Daraus folgt, dass im Allgemeinen immer localhost angegeben werden kann,
statt der IP-Adresse des Rechners.

Starten des Clients auf Windows-Betriebsystemen


cd d:\user
java myRmi.Modifier 192.168.0.5
Starten des Clients auf Windows-Betriebsystemen
cd /home/user/
java myRmi.Modifier 192.168.0.5
Der Client bekommt ebenfalls eine IP-Adresse als Argument. Es ist die IP-
Adresse der RMI-Registry, unter der er das entfernte Objekt findet.

Testen der Applikation

Jedesmal, wenn auf dem Server die Methode increase() beziehungsweise


decrease aufgerufen werden, erscheint auf der Konsole eine Ausgabe. Ruft
Entwicklung eines einfachen RMI Beispiels (Modifier) 93

der Client zum Beispiel vier Male die Methode increase() auf, sollten die
Konsolen des Servers und des Clients folgendermaßen aussehen:

java myRmi.Modifier localhost

Modifier gestarted
+ für increase()
- für decrease()
exit zum Beenden der Anwendung

+
+
+
+

Abbildung 13: Ausgabe auf dem Client

java myRmi.Server localhost

Server gestarted
Variable inkrementiert :1
Variable inkrementiert :2
Variable inkrementiert :3
Variable inkrementiert :4

Abbildung 14: Ausgabe auf dem Server


Zusammenspiel zwischen RMI und Enterprise JavaBeans 94

8. Zusammenspiel zwischen RMI und Enterprise


JavaBeans

Wie den vorherigen Kapiteln zu entnehmen ist, nutzt ein J2EE Server eine
besondere Art von RMI, um auf entfernte Objekte6 zuzugreifen. Es handelt
sich um RMI IIOP (Remote Method Invocation over the Internet Inter-ORB
Protocol). Diese spezielle Version von RMI wurde entwickelt, um auf CORBA-
kompatible Objekte zuzugreifen. Um dies zu gewährleisten, musste das Proto-
koll von RMI geändert werden. RMI IIOP nutzt das Protokoll IIOP von COR-
BA. Leider brachte diese Änderung auch Nachteile, denn unter RMI IIOP gibt
es keinen verteilten Garbage Collector, und es gibt nicht die Option des au-
tomatischen Herunterladens von Klassen. Zudem gibt es noch ein paar kleine
Unterschiede in der Handhabung. Unter RMI kann ein entferntes Objekt di-
rekt in den entsprechenden Typ gecastet werden. Dies ist deshalb erlaubt, weil
die Klassen automatisch über das Netz übertragen werden. Unter RMI IIOP
ist diese Eigenschaft leider nicht vorhanden. Hier muss ein sauberer cast ge-
macht werden. Dies geschieht mit Hilfe der Methode PortableRemoteObject.
narrow(...) [24]. Leider muss die Klasse beziehungsweise Schnittstelle im
CLASSPATH liegen. Der Aufbau zwischen RMI und RMI IIOP ist ansonsten
gleich. Die Kommunikation wird mit Hilfe von Stubs und Skeletons, wie in
Kapitel 6 beschrieben, realisiert. Abbildung 15 zeigt den strukturellen Aufbau
einer Kommunikation unter RMI IIOP. Mehr Informationen zu RMI IIOP sind
unter [25] nachzulesen.
6
In einigen Java Dokumentationen wird auch der englische Begriff remote object verwendet.
Zusammenspiel zwischen RMI und Enterprise JavaBeans 95

Abbildung 15: Kommunikation zwischen Client und Server via RMI IIOP

Wie dem Beispiel des vorherigen Kapitels zu entnehmen ist, kann man ohne
J2EE Server entfernte Objekte erzeugen und auf sie zugreifen. Wenn man sich
die Klassenhierarchie ansieht, stellt man fest, dass das remote Interface einer
Enterprise Java Bean nichts anderes als das remote Interface eines entfernten
Objekts bei RMI ist. Entwickelt man selbst entfernte Objekte, dass heißt ohne
J2EE Server, implementiert man dieses Interface direkt und damit sind Instan-
zen der Klasse automatisch entfernte Objekte. Nun könnte man annehmen,
dass es bei Enterprise JavaBeans ähnlich ist. Eine Frage, die relativ schnell
aufkommt, ist: Weshalb implementiert die eigentliche Bean Klasse nicht das
remote Interface? Damit wäre es viel einfacher, zu prüfen, ob man alle Business
Methoden implementiert hat. Aber dafür gibt es einen guten Grund. Falls die
Bean selbst das remote Interface implementiert, wäre sie automatisch ein ent-
ferntes Objekt, was wiederum zur Folge hätte, dass sie mit dem Schlüsselwort
this anderen Objekten eine Referenz auf sich selber geben könnte. Dies wäre
eine Sicherheitslücke, denn der Aufruf einer Methode soll ausschließlich über
das EJB-Objekt verkehren, dass dann die eigentliche Methode der Bean auf-
ruft. Entwickelt man also eine EJB, ist nicht das Bean Objekt remote sondern
Zusammenspiel zwischen RMI und Enterprise JavaBeans 96

das EJB-Objekt. Möchte eine Bean einem anderen Objekt eine Referenz zu
sich selber übergeben, sollte sie eine Referenz des EJB-Objektes übergeben.
Diese Referenz kann eine EJB über den entsprechenden Kontext erhalten.

Für einfache verteilte Anwendungen lohnt sich meist der Aufwand einer En-
terprise Java Bean nicht. Aber es sprechen auch andere Gründe dafür, sich
weitere Gedanken über RMI zu machen. Die EJB-Spezifikation [26] schreibt
einige Einschränkungen für Enterprise JavaBeans vor. Die wichtigsten sind in
Abschnitt 8.1 erläutert.

8.1. Einschränkungen von Enterprise JavaBeans

Bei der Entwicklung von Enterprise JavaBeans wurden aufgrund von Sicher-
heitsrichtlinien einige Einschränkungen gemacht. Diese Einschränkungen sind
notwendig, damit die Dienste und Sicherheitsfunktionen eines Applikations-
Servers nicht umgangen werden. Die nachfolgende Auflistung zeigt die wich-
tigsten Einschränkungen von Enterprise JavaBeans.

• Das Schlüsselwort static ist nur für Lesevariablen (Konstanten) er-


laubt. Eine Benutzung von static fordert automatisch das Schlüsselwort
final.

• Es ist nicht erlaubt, einen neuen Thread zu starten, zu synchronisieren


oder Prioritäten zu ändern.

• Das Nutzen von Klassen aus dem Paket java.awt ist nicht erlaubt. Das
heißt also auch, dass keine Klassen des Paketes swing genutzt werden
können, da sie Unterklassen des Paketes java.awt sind.

• Klassen beziehungsweise Unterklassen aus dem Paket java.io sind nur


eingeschränkt nutzbar. Dies hat zur Folge, dass es nicht erlaubt ist, Da-
teien zu lesen beziehungsweise zu schreiben. Viele J2EE-Server erlau-
ben es zwar, zum Beispiel via System.out.println(), auf die Konsole
Zusammenspiel zwischen RMI und Enterprise JavaBeans 97

zu schreiben, aber diese Funktionen sind abhängig vom Hersteller des


J2EE Applikations-Servers. Um auf Daten zuzugreifen, sollte ein Res-
source Manager benutzt werden, zum Beispiel JDBC [27, 28, 29].

• Eine EJB darf keine Sockets überwachen. Das heißt, eine EJB darf nicht
als Server agieren, sondern nur als Client, der über ein Socket eine Ver-
bindung nach draußen aufbaut.

• Es ist nicht erlaubt, Klassen aus der API Reflection zu nutzen.

• Enterprise JavaBeans dürfen keine nativen Bibliotheken benutzen.

Tabelle 6: Einschränkungen von Enterprise JavaBeans

Es hat den Anschein, als gelten diese Einschränkungen nur für die Enterprise
JavaBeans, doch genau genommen gelten sie für fast alle Klassen, die innerhalb
des Containers benutzt werden. Das bedeutet, dass Objekte, unabhängig da-
von, ob sie als Parameter vom Client geparst oder direkt von der Bean erzeugt
wurden, den gleichen Einschränkungen unterliegen. Sollte eine Einschränkung
verletzt werden, wird vom Container eine Fehlermeldung ausgegeben und der
Vorgang angehalten.

Anwendungen, die diese Einschränkungen verletzen, können also nicht mit Hil-
fe des EJB-Frameworks gelöst werden. Oft sind es aber nur Teile der Anwen-
dung, die gegen die Spezifikation von EJBs verstoßen. Mit Hilfe von entfernten
Objekten, die außerhalb des Containers liegen kann man solche Probleme lösen.
In einer der genannten Einschränkungen ist beschrieben, dass eine EJB nur als
Client und nicht als Server agieren darf. Oft sieht man, dass eine EJB sich mit
Hilfe von JNDI eine Referenz auf eine andere EJB holt. Genauso könnte sie
sich eine Referenz auf ein eigenständiges entferntes Objekt holen. Die im Kapi-
tel 7 vorgestellte RMI-Registry stellt einen recht einfachen Namensdienst dar,
Java Objekte abzulegen (bind) und wiederzufinden (lookup).
Erläutern des Zusammenspiels von RMI und EJB am Beispiel eines
asynchronen Dateilesers 98

9. Erläutern des Zusammenspiels von RMI und


EJB am Beispiel eines asynchronen Dateilesers

Wie bereits in Abschnitt 8.1 erwähnt, gibt es einige Einschränkungen bei der
Entwicklung von Enterprise JavaBeans, um Sicherheit zu erlangen. Natürlich
haben diese Einschränkungen ihre Berechtigung. Trotzdem kann es vorkom-
men, dass bestimmte Einschränkungen nicht mit der zur entwickelnden An-
wendung in Einklang zu bringen sind. Welche Einschränkungen es gibt und wie
man diese mit Hilfe von RMI umgehen kann wurde in Abschnitt 8.1 erläutert.
In diesem Kapitel soll eine Anwendung entwickelt werden, die aus einem EJB-
und RMI-Teil besteht. Die Mechanismen die EJBs untersagt sind werden in
den RMI-Teil ausgelagert. Der Client kommuniziert mit der EJB und bekommt
nicht mit, dass im Hintergrund ein RMI Prozess läuft.

9.1. Aufgabenstellung

In diesem Kapitel wird eine Enterprise JavaBean entwickelt, die sich eine Re-
ferenz zu einem RMI entfernten Objekt holt. Das entfernte Objekt stellt als
Beispiel eine Methode bereit, um asynchron eine Datei einzulesen. Ein Client,
der diese Methode aufruft, ist nicht solange blockiert, bis die Datei vollständig
eingelesen worden ist, sondern er stößt nur den Prozess des Einlesens an. Später
kann er dann mit Hilfe einer anderen Methode den Inhalt der Datei erfragen.
Natürlich ist dieses Beispiel nur bedingt praxisrelevant, aber es zeigt deut-
lich, dass Einschränkungen einer EJB bei Bedarf umgangen werden können.
Nur der Teil, der den Einschränkungen nicht genügt, wird in ein RMI entfern-
ten Objekt ausgelagert. In diesem Beispiel sind das der Dateizugriff und der
zusätzliche Thread zum einlesen der Datei.

Mit diesem Konzept nutzt man alle Vorteile der EJBs und den Vorteil der ei-
genständigen RMI entfernten Objekte. Solche eigenständigen entfernte Objekte
Erläutern des Zusammenspiels von RMI und EJB am Beispiel eines
asynchronen Dateilesers 99

dürfen alles, was sie mit normalen anderen Objekten auch machen können. Die
einzige Einschränkung liegt darin, dass nur bestimmte Argumente- und Rück-
gabetypen zulässig sind. Die Typen wurden in Kapitel 6 beschrieben. Diese
Einschränkung dürfte zu keinem Problem führen, da es sich um Typen han-
delt, die nur lokal von Bedeutung sind (z.B. Filedescriptoren).

9.2. Umsetzen der Aufgabenstellung

Die Aufgabe läßt sich in drei Aufgaben unterteilen. Es wird ein eigenständiges
entferntes Objekt entwickelt, eine EJB, die dieses entfernte Objekt benutzt und
ein Client, um das System zu testen.

Abbildung 16 zeigt ein UML Diagramm der zu entwickelnden Klassen und


Schnittstellen für das RMI entfernte Objekt.
<<Interface>> java.rmi.server.UnicastRemoteObject <<Interface>>
java.rmi.Remote +.....
java.lang.Runnable
+...() +run(): void
+clone(): java.lang.Object
+exportObject(): Remote

<<package>>
myRmi

<<Interface>>
IRmiApp
+startReading(fileName:String): void
+getText(): String

RmiApp
-text: String = noch nicht eingelesen
-fileName: String
+RmiApp(): Konstruktor
+startReading(fileName:String): void
+getText(): String
-setText(text:String): void
+run(): void
+main(String[]:args): static void

Abbildung 16: UML Diagramm des RMI entfernten Objektes

Wie dem Diagramm 16 zu entnehmen, werden die Klasse RmiApp und Schnitt-
stelle IRmiApp entwickelt. Die anderen Schnittstellen gehören zur Klassenbi-
bliothek der Java 2 Plattform. Nähere Informationen zu diesen Klassen bezie-
hungsweise Schnittstellen findet man in der Java API [30]. Das nachfolgende
Erläutern des Zusammenspiels von RMI und EJB am Beispiel eines
asynchronen Dateilesers 100

UML-Diagramm 17 enthält die notwendigen Klassen und Schnittstellen für die


Enterprise JavaBean.
<<Interface>> <<Interface>> <<Interface>>
javax.ejb.EJBHome javax.ejb.EJBObject javax.ejb.SessionBean
+remove(): void +remove(): void +setSessionContext(ctx:javax.ejb.SessionContext): void
+...() +getEJBHome(): javax.ejb.EJBHome +ejbRemove(): void
+...() +....()
<<package>>
myEjb

<<Interface>> ReadFileBean
ReadFileHome +remoteObj: IRmiApp
+create(): ReadFile +ctx: javax.ejb.SessionContext
+ejbCreate(): void
+ejbActivate(): void
<<Interface>> +ejbPassivate(): void
ReadFile +ejbRemove(): void
+setSessionContext(ctx:javax.ejb.SessionContext): void
+readF(fileName:String,lookup:String): void +readF(fileName:String,lookup:String): void
+getDataContent(): String +getDataContent(): String

Abbildung 17: UML Diagramm der Enterprise JavaBean

Die Enterprise JavaBean wird als Session Bean realisiert. Es müssen das remo-
te Interface ReadFile, das Home Interface ReadFileHome und die eigentliche
Bean ReadFileBean entwickelt werden. In dem UML Diagramm sind nur die
direkt implementierten beziehungsweise vererbten Schnittstellen angegeben.
Jeder dieser Schnittstellen hat weitere Schnittstellen in seiner Klassenhierar-
chie. Mehr Informationen zu diesen Schnittstellen gibt es in der Java API [30].

Zudem wird noch ein Client entwickelt, der später die Enterprise JavaBean
aufruft. Da dieser Client nur über eine Methode main() verfügt, wird kein
gesondertes UML-Diagramm dafür erstellt.

9.3. Quellcodes

Die folgenden Quellcodes zeigen die konkrete Umsetzung der Aufgabenstel-


lung. Der vollständige Quellcode kann unter [31] heruntergeladen werden.
Nähere Informationen zu den einzelnen Klassen, Schnittstellen und Methoden
sind aus dem Quellcode zu entnehmen.
Erläutern des Zusammenspiels von RMI und EJB am Beispiel eines
asynchronen Dateilesers 101

9.3.1. Quellcode des remote Interface IRmiApp

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: IRmiApp
9 * Date: 04.02.2003
10 * Version: 1.0
11 */
12
13 package myRmi;
14
15 import java.rmi.*;
16
17
18 /**
19 * Remote interface für das RmiApp Objekt.
20 */
21 public interface IRmiApp extends Remote {
22
23
24 /**
25 * Startet einen neuen Thread, der eine Textdatei einliest.
26 * Der Name der Textdatei wird als Parameter übergeben. Die
27 * Methode, die diese Methode aufgerufen hat, kann also
28 * gleich weiterarbeiten. Wenn sie denn Inhalt der Datei
29 * erfragen möchte, muss sie die Methode getText()
30 * aufrufen.
31 * @param fileName Der Dateiname der zu einlesenden Datei.
32 */
33 public void startReading(String fileName)
34 throws RemoteException;
35
36
37 /**
38 * Liefert den Inhalt der Textdatei zurück. Solange die
39 * Datei nicht vollständig gelsen worden ist, liefert diese
40 * Methode "noch nicht eingelesen" zurück. Ist die Datei
41 * auf dem System nicht verfügbar liefert diese Methode
42 * "Datei nicht gefunden" zurück.
43 * @return Liefert den Inhalt der Textdatei zurück.
Erläutern des Zusammenspiels von RMI und EJB am Beispiel eines
asynchronen Dateilesers 102

44 */
45 public String getText() throws RemoteException;
46 }

9.3.2. Quellcode des eigenständigen entfernten Objekts RmiApp

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: RmiApp
9 * Date: 04.02.2003
10 * Version: 1.0
11 */
12
13 package myRmi;
14
15 import java.rmi.*;
16 import java.io.*;
17 import java.rmi.server.UnicastRemoteObject;
18
19 /**
20 * RmiApp dient dazu, eine TextDatei asynchron einzulesen.
21 * Ein Client ruft die Methode startReading(String) auf.
22 * Dabei wird ein neuer Thread erzeugt, der für das Einlesen
23 * der Datei zuständig ist. Der Client muss nicht extra
24 * warten bis die Datei eingelesen worden ist. Er kann andere
25 * Operationen durchführen und zu einem späteren Zeitpunkt
26 * die getText() Methode aufrufen, die ihm den Inhalt der
27 * Textdatei zurückliefert. Diese Klasse soll als Beispiel
28 * für eine Enterprise Java Bean Umgebung dienen, in der es
29 * nicht möglich ist einen neuen Thread zu erzeugen
30 * beziehungsweise auf das Dateisystem zuzugreifen. Eine EJB
31 * kann sich eine Referenz auf ein remote Object holen und
32 * dann damit arbeiten.
33 */
34 public class RmiApp extends UnicastRemoteObject
35 implements IRmiApp, Runnable{
Erläutern des Zusammenspiels von RMI und EJB am Beispiel eines
asynchronen Dateilesers 103

36
37 /**
38 * Inhalt der Textdatei
39 */
40 private String text="noch nich eingelesen";
41
42
43 /**
44 * Dateiname der einzulesenden Textdatei
45 */
46 private String fileName="";
47
48
49 /**
50 * Rmi fordert für jedes remote Object einen Standard-
51 * konstruktor der eine RemoteException wirft. Deshalb
52 * muss der Standardkonstruktor überschrieben werden.
53 */
54 public RmiApp() throws RemoteException{
55
56 }
57
58
59 /**
60 * Startet einen neuen Thread, der eine Textdatei einliest.
61 * Der Name der Textdatei wird als Parameter übergeben. Der
62 * Client der diese Methode aufgerufen hat, kann gleich
63 * weiterarbeiten. Wenn Sie denn Inhalt der Datei erfragen
64 * möchte, muss sie die Methode getText() aufrufen.
65 * @param fileName Der Dateiname der einzulesenden Datei.
66 */
67 public void startReading(String fileName)
68 throws RemoteException {
69
70 this.fileName = fileName;
71 setText("noch nicht eingelesen");
72 Thread t = new Thread(this);
73 t.start();
74 }
75
76
77 /**
78 * Setzt den Inhalt der Textdatei fest.
79 * @param text Der Inhalt der Textdatei.
80 */
Erläutern des Zusammenspiels von RMI und EJB am Beispiel eines
asynchronen Dateilesers 104

81 private void setText(String text){


82
83 this.text = text;
84 }
85
86
87 /**
88 * Liefert den Inhalt der Textdatei zurück. Solange die
89 * Datei nicht vollständig gelesen worden ist, liefert
90 * diese Methode "noch nicht eingelesen" zurück. Ist die
91 * Datei auf dem System nicht verfügbar liefert diese
92 * Methode "Datei nicht gefunden" zurück.
93 * @return Liefert den Inhalt der Textdatei zurück.
94 */
95 public String getText() throws RemoteException{
96
97 return text;
98 }
99
100
101 /**
102 * Methode, die automatisch von der Java Virtual Machine
103 * aufgerufen wird, wenn die Methode start() der Klasse
104 * Thread aufgerufen wird. Diese Methode öffnet die
105 * Textdatei und liest ihren Inhalt. Wenn der Inhalt
106 * vollständig gelesen wurde, liefert die Methode getText()
107 * den Inhalt.
108 */
109 public void run(){
110 try{
111 /*
112 * etwas Zeit vergehen lassen, damit der Client beim
113 * Aufruf von getText() sieht, dass es sich um einen
114 * Thread im Hintergrund handelt.
115 */
116 Thread.sleep(130);
117 }catch(InterruptedException e){
118 e.printStackTrace();
119 }
120 try{
121 FileReader input = new FileReader(fileName);
122 StringBuffer inhalt = new StringBuffer();
123 int gelesen;
124 boolean ende = false;
125
Erläutern des Zusammenspiels von RMI und EJB am Beispiel eines
asynchronen Dateilesers 105

126 while(!ende){
127 gelesen = input.read();
128 if (gelesen == -1){
129 ende = true;
130 }else{
131 inhalt.append( (char) gelesen);
132 }
133 }
134 setText(inhalt.toString());
135 }catch(IOException e){
136 setText("Datei nich gefunden");
137 }
138 }
139
140
141 /**
142 * Erzeugt ein neues RmiApp Objekt und legt es in der RMI-
143 * Registry ab. RMI erlaubt es nur auf einer lokalen RMI-
144 * Registry ein Objekt abzulegen. Clients können mit Hilfe
145 * eines lookups eine Referenz auf dieses Objekt bekommen
146 * und dann die im remote Interface deklarierten Methoden
147 * aufrufen.
148 */
149 public static void main (String[] args){
150 if (args.length ==1) {
151 try{
152 Naming.rebind("rmi://"+ args[0] + "/RmiApp" ,
153 new RmiApp());
154 System.out.println("RmiApp started");
155 }catch(Exception e){
156 e.printStackTrace();
157 }
158 }else{
159 System.out.println("Geben Sie bitte die IP Adresse "+
160 "bzw. Hostname der rmi-Registry "+
161 "als Argument an");
162 System.out.println("Zum Beispiel java myApp.rmiApp 1"+
163 "92.168.0.5");
164 }
165 }
166 }
Erläutern des Zusammenspiels von RMI und EJB am Beispiel eines
asynchronen Dateilesers 106

9.3.3. Quellcode des remote Interface ReadFile

Nicht alle Einschränkungen von Enterprise JavaBeans sind in der Spezifikati-


on zu lesen. Leider beziehen sich einige Einschränkungen auch auf den J2EE
Server. Entwickelt man normale Klassen und bezeichnet man eine Methode
mit dem gleichen Namen wie die Klasse selbst, so wird diese Methode zum
Konstruktor. Hat eine solche Methode aber einen Rückgabetypen, gilt sie als
ganz normale Methode. Als Java Entwickler würde man annehmen, dass dieses
Verhalten bei Enterprise JavaBeans auch so ist, aber dem ist nicht so. Würde
man die readF Methode umbenennen in ReadFile beziehungsweise readFile,
bekäme der Client, der diese Methode aufruft, eine ganz merkwürdige Fehler-
meldung. Was genau diesen Fehler auslöst, kann nicht so einfach gesagt werden.
Es kann am J2EE Applikations-Server oder an der CORBA-Kompatibilität lie-
gen. Wenn man dieses Problem nicht kennt, kann man viele Stunden damit
verbringen, nähere Informationen zu dieser Fehlermeldung zu bekommen.
Erläutern des Zusammenspiels von RMI und EJB am Beispiel eines
asynchronen Dateilesers 107

java -classpath %j2ee_home%\lib\j2ee.jar;d:\Diplomarbeit\Rmi_Ejb\


RmiEJBClient.jar;. EjbClient
java.rmi.RemoteException: CORBA BAD_OPERATION 0 No;
nested exception is:
org.omg.CORBA.BAD_OPERATION: vmcid: 0x0 minor code: 0
completed: No
at com.sun.corba.ee.internal.iiop.ShutdownUtilDelegate.
mapSystemException(ShutdownUtilDelegate.java:137)
at javax.rmi.CORBA.Util.mapSystemException(Util.java:65)
at myEjb._ReadFile_Stub.readFile(Unknown Source)
at EjbClient.main(EjbClient.java:37)
Caused by: org.omg.CORBA.BAD_OPERATION: vmcid: 0x0
minor code: 0 completed: No
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(
Native Method)

at sun.reflect.NativeConstructorAccessorImpl.newInstance(
NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(
DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:274)
at java.lang.Class.newInstance0(Class.java:296)
at java.lang.Class.newInstance(Class.java:249)
at com.sun.corba.ee.internal.iiop.messages.ReplyMessage_1_2.
getSystemException(ReplyMessage_1_2.java:93)
at com.sun.corba.ee.internal.iiop.ClientResponseImpl.
getSystemException(ClientResponseImpl.java:108)
at com.sun.corba.ee.internal.POA.GenericPOAClientSC.invoke(
GenericPOAClientSC.java:132)
at org.omg.CORBA.portable.ObjectImpl._invoke(ObjectImpl.java:457)
at myEjb._ReadFile_Stub.readFile(Unknown Source)
... 1 more

Abbildung 18: Fehlermeldung bei ReadFile.readFile()

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: ReadFile
9 * Date: 04.02.2003
10 * Version: 1.0
11 */
Erläutern des Zusammenspiels von RMI und EJB am Beispiel eines
asynchronen Dateilesers 108

12
13 package myEjb;
14
15 import javax.ejb.EJBObject;
16 import java.rmi.*;
17
18
19 /**
20 * Remote interface der ReadFileBean EJB.
21 */
22 public interface ReadFile extends EJBObject {
23
24
25 /**
26 * Besorgt sich eine Referenz auf das RmiApp Objekt und
27 * ruft die startReading() Methode des Objektes auf.
28 * @param fileName Dateiname der zu öffnenden Textdatei
29 * @param URL die auf das remote Objekt verweist.
30 */
31 public void readF(String fileName,String lookup)
32 throws RemoteException;
33
34
35 /**
36 * Ruft die getText() Methode des remote Objektes auf und
37 * liefert dessen Rückgabewert zurück.
38 * @return Inhalt der Textdatei.
39 */
40 public String getDataContent() throws RemoteException;
41 }

9.3.4. Quellcode des remote Home Interfaces ReadFileHome

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: ReadFileHome
Erläutern des Zusammenspiels von RMI und EJB am Beispiel eines
asynchronen Dateilesers 109

9 * Date: 04.02.2003
10 * Version: 1.0
11 */
12
13 package myEjb;
14
15 import javax.ejb.EJBHome;
16 import java.rmi.RemoteException;
17 import javax.ejb.CreateException;
18
19
20 /**
21 * Home interface der ReadFileBean EJB.
22 */
23 public interface ReadFileHome extends EJBHome {
24
25 ReadFile create() throws RemoteException, CreateException;
26 }

9.3.5. Quellcode der Session Bean ReadFileBean

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: myEjb
9 * Date: 04.02.2003
10 * Version: 1.0
11 */
12
13 package myEjb;
14
15 import javax.ejb.SessionBean;
16 import javax.ejb.SessionContext;
17 import java.rmi.*;
18 import myRmi.*;
19
20
Erläutern des Zusammenspiels von RMI und EJB am Beispiel eines
asynchronen Dateilesers 110

21 /**
22 * Diese stateless Session Bean greift über ein remote Objekt
23 * auf eine Datei zu. Dieses remote Objekt benutzt zum Lesen
24 * der Datei einen eigenen Thread. Dieses Beispiel soll
25 * zeigen, dass man die Einschränkungen von EJB’s, die ihre
26 * Gründe haben, auch umgehen kann.
27 */
28 public class ReadFileBean implements SessionBean {
29
30 SessionContext ctx;
31
32 /**
33 * Platzhalter für das RmiApp remote Objekt
34 */
35 IRmiApp remoteObj;
36
37 // Benötigete Methoden jeder EJB
38
39 public void ejbCreate() {
40
41 }
42
43
44 public void ejbActivate() {
45
46 }
47
48
49 public void ejbPassivate() {
50
51 }
52
53
54 public void ejbRemove() {
55
56 }
57
58
59 public void setSessionContext(SessionContext ctx) {
60
61 this.ctx=ctx;
62 }
63
64 // business Methoden
65
Erläutern des Zusammenspiels von RMI und EJB am Beispiel eines
asynchronen Dateilesers 111

66 /**
67 * Besorgt sich eine Referenz auf das RmiApp Objekt und
68 * ruft die startReading() Methode des Objektes auf.
69 * @param fileName Dateiname der zu öffnenden Textdatei
70 * @param URL die auf das remote Objekt verweist.
71 */
72 public void readF(String fileName,String lookup){
73
74 try{
75 remoteObj=(IRmiApp) Naming.lookup("rmi://"+lookup+
76 "/RmiApp");
77 remoteObj.startReading(fileName);
78 }catch(Exception e ){
79 e.printStackTrace();
80 }
81 }
82
83
84 /**
85 * Ruft die getText() Methode des remote Objektes auf und
86 * liefert dessen Rückgabewert zurück.
87 * @return Inhalt der Textdatei.
88 */
89 public String getDataContent(){
90
91 if (remoteObj!=null){
92 try{
93 return remoteObj.getText();
94 }catch(Exception e){
95 return "Konnte remote Objekt nicht finden";
96 }
97 }else{
98 return "RemoteObjekt konnte nicht gefunden werden";
99 }
100 }
101 }

9.3.6. Quellcode des Clients EjbClient zum Testen des Systems

1 /*
2 *
Erläutern des Zusammenspiels von RMI und EJB am Beispiel eines
asynchronen Dateilesers 112

3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven


4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: EjbClient
9 * Date: 22.01.2003
10 * Version: 1.0
11 */
12
13 import myEjb.*;
14 import javax.rmi.*;
15 import javax.naming.*;
16
17
18 /**
19 * Diese Klasse dienst als Testanwendung für die
20 * ReadFileBean. Sie holt sich eine Referenz auf das Home
21 * Objekt, erzeugt danach ein EJB Objekt und führt daraufhin
22 * eine asynchrone Leseoperation durch. Dieser Client merkt
23 * nicht, dass die EJB sich ein weiteres remote Objekt zur
24 * Hilfe nimmt um dieses Feature zu gewähleisten. Damit diese
25 * eigenständige Anwendung erfolgreich ausgeführt werden kann
26 * braucht sie als Argument den Ort der rmi-Registry, den
27 * Dateinamen der Textdatei und den JNDI Namen des Home
28 * Interfaces.
29 */
30 public class EjbClient {
31
32 public static void main(String[] args){
33
34 if (args.length ==3) {
35 try{
36 Context ctx = new InitialContext();
37 Object obj = ctx.lookup(args[2]);
38 ReadFileHome home = (ReadFileHome)
39 PortableRemoteObject.narrow(obj,ReadFileHome.class);
40 ReadFile ejbObject = home.create();
41 ejbObject.readF(args[1],args[0]);
42 for (int i =1 ; i<=100 ; i++){
43 String text = ejbObject.getDataContent();
44 System.out.println("getDataContent() := " +i+ " :"+
45 text);
46 }
47 }catch(Exception e){
Erläutern des Zusammenspiels von RMI und EJB am Beispiel eines
asynchronen Dateilesers 113

48 e.printStackTrace();
49 }
50 }else{
51 System.out.println("Geben Sie bitte die IP Adresse "+
52 "bzw. Hostnamen der rmi-Registry, "+
53 "den Dateinamen \nder Textdatei und"+
54 " den JNDI Namen der ReadFile EJB"+
55 " als Argument an.");
56 System.out.println("Zum Beispiel java EjbClient 192.1"+
57 "68.0.5 test.txt ejb/ReadFileHome");
58 }
59 }
60 }

9.4. Vorbereitungen zum Start der Anwendung

Nach dem Übersetzen der Klassen und Schnittstellen sollte folgende Baum-
struktur im Projektverzeichnis vorliegen.
| EjbClient.class
+---myEjb
| ReadFileBean.class
| ReadFileHome.class
| ReadFile.class
+---myRmi
IRmiApp.class
RmiApp.class

In den vorherigen Kapiteln ist bereits deutlich gemacht worden, wie man eine
Enterprise JavaBean auf einen Applikations-Server lädt. Desweiteren ist aus
Abschnitt 7.2 hervorgegangen, wie man Stubs und Skeletons für eigenständige
entfernte Objekte generiert. Bei der Enterprise JavaBean ist darauf zu ach-
Erläutern des Zusammenspiels von RMI und EJB am Beispiel eines
asynchronen Dateilesers 114

ten, dass sie Zugriff auf das remote Interface IRmiApp und auf den Stub
RmiApp Stub hat. Ein fertiges ear-Archiv liegt unter [31]. Auch diesmal ist
darauf zu achten, dass beim Heraufladen (deploy) der Applikation auf den
Applikations Server ein Client Archiv erzeugt wird. Im Folgenden wird davon
ausgegangen, dass dieses Archiv den Dateinamen RmiEJBClient.jar trägt. In
diesem Archiv befinden sich neben den Schnittstellen der EJB auch die Stubs,
die für die Verbindung zur EJB später notwendig sind. Um den Client später
mit dem Befehl runClient zu starten, muss ebenfalls ein Archiv dafür gene-
riert werden. Wie ein solches Archiv generiert wird wurde in Abschnitt 5.4
bereits erläutert .

9.5. Testen der Beispielanwendung

Sind alle Vorbereitungen getroffen, kann man beginnen, das System zu testen.
Die Dateien werden zunächst auf die verschiedenen Rechner kopiert. Dieses
Beispiel besteht aus drei eigenständigen Teilprogrammen, und es ist vorstell-
bar, dass jedes dieser Teilprogramme auf einem anderen Rechner läuft. Tabelle
7 zeigt eine Verteilung, auf die sich die folgenden Befehle beziehen.

Teil IP Adresse
EJB 192.168.0.5
Client 192.168.0.6
entferntes Objekt 192.168.0.7

Tabelle 7: Verteilung der Beipielanwendung

Wie eine EJB auf einem Applikations-Server geladen wird, wurde im Abschnitt
5.4 erläutert. Deshalb folgen nur die Befehle für das RMI entfernte Objekt und
den Client. Dabei ist darauf zu achten, dass zuerst das entfernte Objekt gest-
artet wird. Die Befehle werden hier nur für Windows Betriebsysteme angeben.
Vergleichbare Befehle für Linux sind im Kapitel 7 erläutert.
Start des entfernten Objektes
Erläutern des Zusammenspiels von RMI und EJB am Beispiel eines
asynchronen Dateilesers 115

cd d:\user\ejb_rmi\
start rmiregistry
java myRmi.RmiApp 192.168.0.7
Start des Clients zum Testen des Systems
Die Befehle gehen davon aus, dass die EJB unter dem Namen
ejb/ReadFileHome auf dem Applikations-Server abgelegt ist und das Ar-
chiv für den Client den Namen RmiEjbClient.jar trägt.
cd d:\user\ejb_rmi\
set VMARGS=-Dorg.omg.CORBA.ORBInitialHost=192.168.0.5
set APPCPATH=RmiEJBClient.jar
runClient -client ClientApp.jar -textauth 192.168.0.6 hello.
txt ejb/ReadFileHome
Wenn sich die Datei d:\user\ejb_rmi\hello.txt auf dem Rechner des ent-
fernten Objektes befindet, wird diese auf dem Client ausgegeben. Ist der Inhalt
der Textdatei Hello World“, so wird die Ausgabe etwa wie in Abbildung 19

aussehen, je nach Geschwindigkeit der Rechner und des Netzwerkes.
....
getDataContent() := 13 :noch nicht eingelesen
getDataContent() := 14 :noch nicht eingelesen
getDataContent() := 15 :noch nicht eingelesen
getDataContent() := 16 :noch nicht eingelesen
getDataContent() := 17 :Hello World
getDataContent() := 18 :Hello World
getDataContent() := 19 :Hello World
....
Abbildung 19: Ausgabe des Clients

Abbildung 20 zeigt detailliert die einzelnen Schritte der Kommunikation.


Erläutern des Zusammenspiels von RMI und EJB am Beispiel eines
asynchronen Dateilesers 116

Abbildung 20: Kommunikationsablauf des Beispiels


Hochverfügbarkeit von verteilten Systemen 117

10. Hochverfügbarkeit von verteilten Systemen

Bei der Entwicklung verteilter Systeme stellt sich oft die Frage, wie sicher das
System ist. Man kann die einzelnen Komponenten mit sehr viel Sorgfalt entwi-
ckeln und darauf achten, dass die Argumente aller Methoden überprüft werden.
Leider kann die beste Softwareentwicklung gegen Hardwarefehler nichts tun.
In einer verteilten Applikation stellen die einzelnen Komponenten nur Dienste
(Services) dar. Ein Client greift auf einen solchen Dienst zu und erwartet ein
Ergebnis. Aus dem Kapitel 3 dürfte klar hervorgegangen sein, dass ein Cli-
ent fest an einen Applikations-Server gebunden ist. Fällt die Komponente des
Applikations-Server aus, kommt auch automatisch der Client zum Stillstand.
Dies würde dazu führen, dass ein Ausfall einer Komponente die verteilte Ap-
plikation zum Stillstand beziehungsweise Ausfall bringt.

10.1. Konzepte zur Hochverfügbarkeit

In diesem Kapitel sollen verschiedene Konzepte vorgestellt werden, um die


einzelnen Komponenten hochverfügbar zu bekommen. Zu jedem der Konzep-
te gibt es Vor- und Nachteile, die ebenfalls in den zugehörigen Abschnitten
angegeben werden.
Die folgende Auflistung zeigt die verschiedenen Varianten, wo diese Logik im-
plementiert werden könnte. Die Grundidee aller Konzepte ist, dass die Dienste
auf mehreren Rechnern angeboten werden.

• via JNDI

• via Container

• via Home Stub

• via Remote Stub

• via intelligentem Client


Hochverfügbarkeit von verteilten Systemen 118

10.1.1. Mehr Sicherheit mit Hilfe von JNDI

Bei diesem Konzept wird die Logik im JNDI Server untergebracht. Bevor ein
Client mit einer EJB arbeiten kann, braucht es ein Home Objekt. Dieses Home-
Objekt wird im Allgemeinen mit Hilfe eines JNDI-Name-Servers besorgt. Man
kann also dafür sorgen, dass nicht nur ein Objekt an einen JNDI-Namen ge-
bunden wird, sondern eine ganze Liste. Bei einer Anfrage könnte der JNDI
Server dann ein verfügbares Objekt zurückliefern. Der Vorteil dieses Konzep-
tes ist, dass es, einmal entwickelt, für jede EJB funktionieren würde. Der JNDI
Server befindet sich aber meist auf dem Applikations-Server und wäre bei ei-
nem Hardwareausfall ebenfalls nicht verfügbar. Auch wenn man den JNDI
Server auf einen eigenen Rechner installieren würde, wäre das Problem nicht
gelöst. Der Client ist dann zwar nicht mehr an einen Applikations-Server fest
gebunden, aber nun an einem JNDI-Server. Fällt dieser aus, fällt der Client
ebenfalls aus. Zudem kann mit diesem Konzept dem Client nur zum Zeitpunkt
des Lookup ein verfügbarer Applikations-Server zugewiesen werden. Bei einem
späteren Ausfall der Komponente würde die Applikation vor dem gleichen Pro-
blem stehen. Abbildung 21 zeigt noch einmal die Struktur dieses Konzeptes.

Abbildung 21: Hochverfügbarkeit via JNDI


Hochverfügbarkeit von verteilten Systemen 119

10.1.2. Mehr Sicherheit mit Hilfe des Containers

Ein Container überwacht bereits die Enterprise JavaBean und könnte bei ei-
nem Ausfall automatisch eine neue Instanz erzeugen beziehungsweise die An-
frage zu einem anderen Applikations-Server weiterleiten. Der Vorteil ist, dass
während der gesamten Kommunikation zwischen Client und EJB ein Wech-
sel von einer EJB Instanz zu einer anderen Instanz unterstützt werden kann.
Dies ist am einfachsten mit zustandlosen Session Beans realisierbar, da sie
sich keine Zustände merken. Bei den anderen Typen müssen die Instanzen an-
gepasst werden. Leider hilft dieses Konzept aber auch bei einem Ausfall des
Applikations-Servers nicht. Abbildung 22 zeigt dieses Konzept im Detail.

Abbildung 22: Hochverfügbarkeit via Container

10.1.3. Mehr Sicherheit durch den Home Stub

Der Home Stub ist das erste entfernte Objekt7 , mit dem ein Client arbei-
tet. Man könnte den Stub so intelligent machen, dass er beim Aufruf einer
7
In einigen Java Dokumentationen wird auch der englische Begriff remote object verwendet.
Hochverfügbarkeit von verteilten Systemen 120

create(...) Methode automatisch ein verfügbares Home Objekt anspricht.


Der Vorteil bei diesem Konzept ist, dass ein Ausfall des Applikations-Servers
dem Client nichts anhaben kann. Leider braucht der Client jedoch erst ein-
mal ein Home Stub. Dieser Stub wird von einem Applikations-Server geliefert
und ist nur so lange gültig wie das Home Objekt auf dem Server existiert.
Dieses Konzept funktioniert also nur gut, wenn der Lebenszyklus eines Ho-
me Objektes sehr lang ist und der Applikations-Server hochverfügbar ist. Ein
weiterer Nachteil ist, dass ein Absturz einer Komponente wieder zu einem Pro-
blem führt. Der Home Stub wird nur für die Methoden des Home Objektes
gebraucht. Businessmethoden sind im entfernten Objekt deklariert. Kommt
es also zu einem Absturz der Komponenten, während eine Businessmethode
bearbeitet wird, muss der Client selbst die java.rmi.RemoteException abfan-
gen und gegebenenfalls ein neues EJB-Objekt mit dem Home Stub erzeugen.
Abbildung 23 zeigt noch einmal die Struktur dieses Konzeptes.

Abbildung 23: Hochverfügbarkeit via Home Stub


Hochverfügbarkeit von verteilten Systemen 121

10.1.4. Mehr Sicherheit durch den Remote Stub

Anders als beim vorherigen Konzept kann auch die Logik im remote Stub
implementiert werden. Dadurch umgeht man zwar das Abfangen der entspre-
chenden java.rmi.RemoteException, da dies im remote Stub automatisch
abgefangen werden kann, kommt aber wieder zu dem Problem, dass ein remo-
te Stub nur so lange gültig ist, wie das entfernte Objekt existiert. Ein weiterer
Nachteil ist, dass, bevor ein remote Stub erzeugt werden kann, ein Home Stub
existieren muss. Der Home Stub kann zwar nachher vergessen werden, da alle
Informationen im remote Stub liegen, zunächst muss jedoch mit Hilfe eines
Name Servers ein Home Stub erzeugt werden. Ist der Name Server am An-
fang selbst nicht verfügbar, steht das System. Dieses Konzept funktioniert also
ebenfalls nur, wenn der Lebenszyklus eines entfernten Objektes sehr lang ist
und zudem der zugehörige Applikations-Server hochverfügbar ist. Abbildung
24 zeigt die Struktur dieses Konzeptes.

Abbildung 24: Hochverfügbarkeit via Remote Stub


Hochverfügbarkeit von verteilten Systemen 122

10.1.5. Mehr Sicherheit durch einen intelligenten Client

Bei diesem Konzept wird die vollständige Logik im Client implementiert.


Der Client kennt alle verfügbaren Applikations-Server und wählt bei Be-
darf einen anderen. Da der Client an keinen Applikations-Server gebun-
den ist, ist es unerheblich welcher Applikations-Server nicht verfügbar ist.
Die jeweiligen Stubs können alle nachproduziert werden. Leider besitzt aber
auch dieses Konzept einen Nachteil. Jeder Client muss die entsprechenden
java.rmi.RemoteException nun selbst abfangen. Zudem muss die Liste der
verfügbaren Applikations-Server auf dem Client verfügbar und relativ aktuell
sein. Abbildung 25 zeigt den prinzipiellen Aufbau dieses Konzeptes.

Abbildung 25: Hochverfügbarkeit via intellegentem Client


Hochverfügbarkeit von verteilten Systemen 123

10.2. Fazit

Aus den vorgestellten Konzepten ist bereits deutlich geworden, mit welchen
Probleme zu rechnen ist. Je näher die Logik am Server arbeitet, desto mehr
Probleme können auftreten. Ein Container kann zwar eine EJB perfekt über-
wachen und die Last entsprechend verteilen, aber bei einem Hardwareabsturz
ist auch der Container nicht verfügbar.

Es wäre wünschenswert, wenn das System solange funktioniert, wie es


verfügbare Applikations-Server gibt, die die notwendigen Dienste bereitstellen.
Diesen Wunsch kann nur das Konzept des intelligenten Clients erfüllen. Der
Nachteil dieses Konzeptes ist der höhere Implementationsaufwand. Der Client
bezieht sich immer auf eine bestimmte Komponente der verteilten Applikation.

Die ersten vier Konzepte können also zwar nicht alle Probleme beseitigen, ha-
ben aber den Vorteil, dass sie nicht an eine bestimmte Komponente gebunden
sind. Die Stubs könnten vom Applikations-Server so generiert werden, dass
sie alle Informationen des verteilten Systems enthalten. Die beste Lösung be-
steht jedoch darin, eine Mischung aus allen Konzepten zu finden. Leider ist
es aber schwierig, die Implementationen am Container und am JNDI Server
vorzunehmen, da man im Allgemeinen keinen Zugriff auf den Quellcode des
Applikations-Servers hat.
Framework für intelligente Clients 124

11. Framework für intelligente Clients

In diesem Kapitel soll das Konzept des intelligenten Clients implementiert


werden. Zunächst ist es notwendig, dass ein Client immer eine gültige Liste
der verfügbaren Applikations-Server hat. Da das Konzept im Allgemeinen Fall
verwendet werden soll, wird ein Framework aufgebaut, dass die Applikations-
Server überwacht und den Status mit Hilfe von EJBs in einer Datenbank ab-
legt. Clients können dann eine Anfrage an eine EJB stellen, um einen verfügba-
ren Applikations-Server zu bekommen. Abbildung 26 zeigt einen Überblick
über die zu entwickelnden Komponenten.
Framework für intelligente Clients 125

Abbildung 26: Framework des Intelligenten Clients

Dieses Framework sieht vor, dass der Client beliebig intelligent sein kann. Die
Applikations-Server werden von einem Beobachter (Observer) überwacht. Tre-
ten Ausfälle auf, wird der Verwaltungs-Server (Administration Server) benach-
richtigt. Der Verwaltungs-Server hat Informationen über alle Applikations-
Server, Beobachter und über alle angebotenen Dienste. Mit Hilfe eines
Verwaltungs-Clients können diese Daten angepaßt werden. Treten Änderungen
im Datenstamm auf, wird der Beobachter informiert. Es ist sogar erlaubt, meh-
rere Beobachter und Verwaltungs-Server einzusetzen. Der Verwaltungs-Server
kann dann die zu überwachenden Dienste und Applikations-Server auf die Be-
Framework für intelligente Clients 126

obachter aufteilen oder mehrere Beobachter mit den gleichen Daten versorgen,
um gegen einen Ausfall eines Beobachters gewappnet zu sein. Ein Beobachter,
der eine Veränderung des Zustands von Applikations-Server oder Diensten be-
merkt, informiert alle Interessenten (Verwaltungs-Server). Dies wird durch das
Entwurfsmuster des Hörers (Listener) realisiert. Jeder Interessent meldet beim
Beobachter Interesse an bestimmten Diensten beziehungsweise Applikations-
Servern an. Dadurch können bei Zustandveränderungen von Diensten und
Applikations-Server gleich mehrere Verwaltungs-Server benachrichtigt werden.
Mehr Informationen zum Entwurfsmuster des Hörers (Listener) beziehungswei-
se wie man solche in verteilten Anwendungen implementiert, findet man unter
[32, 33].

Ein Client, der nun einen Dienst nutzen möchte, kann auf den Verwaltungs-
Server zugreifen, um einen verfügbaren Applikations-Server zu bekommen. So-
lange die Verwaltungs-Server ihre Arbeit verrichten, wird dieses System funk-
tionieren. Bei einem verteilten System kann man davon ausgehen, dass nicht
ständig neue Dienste angeboten werden. Aus diesem Grund wäre es sinn-
voll, zu den benötigten Diensten eine Liste von Applikations-Servern lokal
zu speichern. Dies würde Netzwerkressourcen sparen, und bei einem Ausfall
der Verwaltungs-Server würde das System immer noch funktionieren. Um die
Daten lokal zu speichern, gibt es verschiedene Varianten. Eine eigenständige
Applikation könnte sie in einer Datei speichern. Da es für EJBs nicht erlaubt
ist, auf das Dateisystem zuzugreifen, könnte man die Daten auch in eine lokale
Datenbank schreiben. Über einen Daemon könnten die lokalen Daten dann in
geeigneten Zeiträumen mit den Daten eines Verwaltungs-Server aktualisiert
werden. Um den Client von dieser Arbeit zu befreien, könnte man eine all-
gemeine Komponente entwickeln, die in definierten Zeitabständen die lokalen
Daten aktualisiert. Mit diesem Framework bleibt es dann dem Client überlas-
sen, ob er eine lokale Kopie der Daten erzeugt, um eine maximale Sicherheit
gegen Abstürze zu bekommen, oder über das Netzwerk die Anfragen bei einem
Verwaltungs-Server stellt.
Framework für intelligente Clients 127

Die Entwicklung dieses Frameworks kann nun unterteilt werden in die Kom-
ponenten :

• Beobachter

• Verwaltungs-Server

• Verwaltungs-Client

11.1. Der Beobachter

Die Aufgabe eines Beobachters ist es, die Applikations-Server zu überwachen.


Er erhält über einen Verwaltungs-Server die zu überwachenden Applikations-
Server und Dienste. Kommen weitere hinzu oder werden sie mit Hilfe des Ver-
waltungs - Clients entfernt, wird der Beobachter automatisch benachrichtigt.
Um dem System mehr Flexibilität zu geben, werden nicht nur die Applikation-
Server und Dienste auf Verfügbarkeit überwacht, sondern auch die Geschwin-
digkeit des Applikations-Servers. Dazu wird eine Benchmark-Komponente für
Applikations-Server entwickelt. Diese Komponente wird einem Applikations-
Server optional gestartet. Unterstützt ein Applikations-Server die Benchmark-
Komponente, können für die einzelnen Dienste minimale Benchmark-Werte
vorausgesetzt werden. Unterschreitet der Applikations-Server den Wert, so gilt
der Dienst automatisch als nicht mehr verfügbar. Dieses Konzept sorgt dafür,
dass die Aufgaben der Clients besser auf die verschiedenen Applikations-Server
verteilt werden (Lastverteilung). Abbildungen 27 , 28 und 29 zeigen ein UML-
Diagramm über die zu entwickelnden Klassen und Schnittstellen des Beobach-
ters und der Benchmark-Komponente.
<<Interface>> java.rmi.server.UnicastRemoteObject java.util.EventObject
java.rmi.Remote +..... #transient source: Object
+clone(): java.lang.Object +EventObject(source:Object): Konstruktor
+exportObject(): Remote +getSource(): Object
+...() +toString(): String
<<package>>
observer

BenchServer.Benchmark <<Interface>> <<Interface>> <<Interface>> <<Interface>>


IBenchMachine IObserver IAppServerListener IServiceListener
+instructions(): void
siehe Abbildung 28
BenchServer +getAverageSpeed(): double +notify(e:AppServerEvent): void +notify(e:ServiceEvent): void
+getCurrentSpeed(): double +...(): * 1 * 1
-task: java.uti.TimerTask +getHostName(): String
-BenchmarkCount: long +getMinSpeed(): double
-interval: int +getMaxSpeed(): double
-machine: BenchMachine
-machineRegHostname: String
-machineRegName: String 1
-machineRegPort: int Observer
-showException: boolean siehe Abbildung 29
1 1
-verbose: boolean
+BenchServer(): Konstruktor BenchMachine 1
+init(): void -averageSpeed: double
+main(args:String[]): void -cache: double[]
-logException(Exception:exc): void +...()
-cacheSum: double
-currentSpeed: double 1 1
1
-hostName: String
-maxSpeed: double AppServerEvent
Framework für intelligente Clients

-measureCount: long +DISABLED: int = 2


-minspeed: double +ENABLED: int = 1
+BenchMachine(hostName:String,cacheSize:int): Konstruktor -appServerName: String
-setAverageSpeed(averageSpeed:double): void -source: Object
setCurrentSpeed(currentSpeed:double): void -type: int
-setMaxSpeed(maxSpeed:double): void +AppServerEvent(source:Object,name:String,type:int): Konstruktor
-setMinSpeed(minSpeed:double): void +getApplikationServerName(): String
1 +getSource(): Object
+getType(): int
+toString(): String
ObserverBenchMachine
+host: String 1
+regName: String ServiceEvent
+regPort: String +DISABLED: int = 2
-checking: boolean +ENABLED: int = 1
-lastSpeed: double +OVERLOAD: int = 3
-machine: IBenchMachine -appServerName: String
+ObserverBenchMachine(hostname:String,portnumber:String,regName:String): Konstruktor 1 -serviceName: String
+getBenchMachine(): IBenchMachine -source: Object
+getLastSpeed(): double -type: int
+isChecking(): boolean +ServiceEvent(source:Object,appSName:String,sName:String,type:int): Konstruktor
setBenchMachine(machine:IBenchMachine): void +getApplikationServerName(): String
setChecking(value:boolean): void +getServiceName(): String
setLastSpeed(lastSpeed:double): void +getSource(): Object
0,1 +getType(): int
+toString(): String

1 * *
ObserverAppServer ObserverService
-name: String siehe Abbildung 29
-observerBenchMachine: ObserverBenchMachine 1
+ObserverAppServer(name:String): Konstruktor 1
+ObserverAppServer(name:String,hostname:String,portnumber:String,regName:String): Konstruktor ServiceType
+getName(): String
+getObserverBenchMachine(): ObserverBenchMachine +...() +RMI: int = 1
+setName(name:String): void +EJB_SUN: int = 2
+setObserverBenchMachine(hostname:String,portnumber:String,regName:String): void +EJB_JBOSS: int = 3
+equals(obj:Object): boolean -type: int
128

+hashCode(): int +ServiceType(type:int): Konstruktor


+toString(): String +getType(): int

Abbildung 27: Komponenten des Beobachters (Observers)


<<Interface>>
IObserver
+addAppServerListener(appServerName:String,l:IAppServerListener): void
+addServiceListener(appServerName:String,serviceName:String,l:IServiceListener): void
+getAllAppServer(): java.util.HashMap(String, ObserverAppServer)
+getAppServerForService(service:String): Collection(ObserverAppServer)
+getAvailableAppServer(): HashMap(String, ObserverAppServer)
+getAvailableAppServerForService(service:String): Collection(ObserverAppServer)
+getBenchMachine(appServerName:String): IBenchMachine
+getCheckSpeedInterval(): long
+getLookupServiceInterval(): long
+getMaxBenchLookupTime(): long
+getMaxCheckSpeedTime(): long
+getMaxServiceLookupTime(): long
+getNotAvailableAppServer(): HashMap(String, ObserverAppServer)
+getObserverAppServer(appServerName:String): ObserverAppServer
Framework für intelligente Clients

+getReconnectBenchInterval(): long
+getServices(): HashMap(String,ObserverService)
+getServices(appServerName:String): Collection(ObserverService)
+isAvailable(appServerName:String): boolean
+isAvailable(appS:ObserverAppServer): boolean
+isRegistered(appServerName:String): boolean
+registerAppServer(appServerName:String): void
+registerAppServer(appServerName:String,hostname:String,port:String,regName:String): void
+registerAppServerService(appServerName:String,serviceName:String,minSpeed:double,lookupHost:String,lookupPort:int,lookupName:String): void
+removeHostMachineListener(appServerName:String,l:IAppServerListener): void
+removeServiceListener(appServerName:String,serviceName:String,l:IServiceListener): void
+setCheckSpeedInterval(checkSpeedInterval:long): void
+setLookupServiceInterval(serviceInterval:long): void
+setMaxCheckSpeedTime(maxCheckSpeedTime:long): void
+setMaxReconnectLookupTime(maxLookupTime:long): void
+setMaxServiceLookupTime(maxLookupTime:long): void
+setReconnectBenchInterval(interv:long): void
+unregisterAppServer(appServerName:String): void
+unregisterAppServerService(serviceName:String,appServerName:String): void

Abbildung 28: Komponenten des Beobachters (Observers) 2


129
Framework für intelligente Clients 130

Observer
-allAppServer: java.util.HashMap(String, ObserverAppServer)
-appServerListeners: java.util.HashMap(String, IAppServerListener)
-availableAppServer: java.util.HashMap(String, ObserverAppServer)
-checkSpeedInterval: long
-checkSpeedTimer: java.util.Timer
-checkSpeedTimerTask: java.util.TimerTask
-lookupServiceInterval: long
-lookupServiceTimer: java.util.Timer
-lookupServiceTimerTask: java.util.TimerTask
-mapAppServerServices: java.util.HashMap(String, java.util.HashList)
-mapServiceAppServer: java.util.HashMap(String, java.util.HashList)
-maxBenchLookupTime: long
-maxCheckSpeedTime: long
-maxServiceLookupTime: long
-notAvailableAppserver: java.util.HashMap(String, ObserverAppServer)
-observerName: String
-reconnectBenchInterval: long
-reconnectBenchTimer: java.util.Timer
-reconnectBenchTimerTask: java.util.TimerTask
-serviceListeners: java.util.HashMap(String, IServiceListener)
-services: java.util.HashMap(String, IObserverService)
-showException: boolean
-verbose: boolean
+observer(): Konstruktor
+main(args:String[]): void
-checkAppServerSpeed(appS:ObserverAppServer): double
-disableAppServer(appS:ObserverAppServer): void
-enableAppServer(appS:ObserverAppServer,bm:IBenchMachine): void
-fireAppServerEvent(appS:ObserverAppServer,type:int): void
-fireServiceEvent(appS:ObserverAppServer,service:ObserverService,type:int): void
-getAppServer(appServerName:String): ObserverAppServer
-help(): void
-init(): void
-log(t:Throwable): void
-log(s:String): void
-lookupAppServer(appS:ObserverAppServer): boolean
-lookupBench(appS:ObserverAppServer): boolean
-lookupEJB(appServerName:String,service:ObserverService): boolean
-lookupRMI(appserverName:String,service:ObserverService): boolean
-lookupService(appServerName:String,service:ObserverService): boolean
-mainInitProerties(): void
-setCheckSpeedTimer(): void
-setLookupServiceTimer(): void
-setReconnectBenchTimer(): void

ObserverService
-available: boolean
-checking: boolean
-lookupHost: String
-lookupName: String
-lookupPort: int
-minSpeed: double
-name: String
-overload: boolean
-type: ServiceType
+ObserverService(name:String,type:int,minSpeed:double,host:String,port:int,lookupName:String): Konstruktor
+getLookupHost(): String
+getLookupName(): String
+getLookupPort(): String
+getMinSpeed(): double
+getName(): String
+getServiceType(): ServiceType
+isAvailable(): boolean
+isChecking(): boolean
+isOverload(): boolean
+setLookupHost(lookupHost:String): void
+setLookupName(lookupName:String): void
+setLookupPort(lookupPort:int): void
+setName(name:String): void
setAvailable(value:boolean): void
setChecking(value:boolean): void
setOverload(value:boolean): void
+equals(obj:Object): boolean
+hashCode(): int
+toString(): String

Abbildung 29: Komponenten des Beobachters (Observers) 3

Aus dem UML-Diagramm geht hervor, dass Objekte der beiden Klas-
sen BenchMachine und Observer als RMI entfernte Objekte8 realisiert
sind. Wie bereits erwähnt, kann ein Applikations-Server die Benchmark-
8
In einigen Java Dokumentationen wird auch der englische Begriff remote object verwendet.
Framework für intelligente Clients 131

Komponente unterstützen. Diese Komponente überwacht die Geschwindigkeit


des Applikations-Servers und stellt sie dem Beobachter zur Verfügung. Die
Benchmark-Komponente ist in den Klassen BenchServer und BenchMachine
realisiert. Die Klasse BenchServer besitzt eine innere Klasse Benchmark,
die für die eigentliche Geschwindigkeitsmessung zuständig ist. Die aktuel-
le, sowie die minimale, maximale und die durchschnittliche Geschwindig-
keit werden in einem Objekt BenchMachine abgelegt. Jeder Applikations-
Server, der die Benchmark Komponente unterstützt, besitzt ein solches
BenchMachine Objekt. Der Beobachter kann dann mit Hilfe des remote In-
terfaces IBenchMachine auf das Objekt zugreifen. Aus dem Klassendiagramm
der Schnittstelle IBenchMachine geht hervor, dass ein Beobachter nur Lese-
rechte (get...) hat. Damit ein Beobachter eine Referenz zu dem Objekt erhält,
legt der BenchServer das Objekt BenchMachine in einer RMI-Registry ab.

Das Herz des Beobachters ist die Klasse Observer. In dieser Klasse ist die
gesamte Logik zum Überwachen der Applikations-Server und Dienste sowie
das Benachrichtigen der Interessenten implementiert. Entfernte Clients können
über die IObserver Schnittstelle einen Beobachter steuern. Dazu gehört das
Registrieren und Entfernen von Diensten und Applikations-Server sowie das
Anmelden von Interessenten. Des weiteren bietet die Schnittstelle auch Me-
thoden, um den Status von Diensten und Applikations-Servern abzufragen.
Applikations-Server können über die Methoden registerAppServer(...)
registriert werden. Der Unterschied besteht darin, dass ein Applikations-
Server einen Benchmark entweder unterstützt oder eben nicht. Falls er einen
Benchmark unterstützt, müssen der Rechnername, die Adresse sowie der
Name, unter dem das BenchMachine Objekt abgelegt ist, angegeben wer-
den. Der Beobachter legt für jeden registrierten Applikations-Server ein Ob-
jekt ObserverAppServer an. Dabei ist zu beachten, dass der Name des
Applikations-Servers eindeutig sein muss. Am einfachsten erreicht man dies,
indem man den Rechnernamen des Applikations-Servers wählt. Unterstützt
der Applikations-Server die Benchmark-Komponente, wird zudem ein Ob-
Framework für intelligente Clients 132

jekt ObserverBenchMachine erzeugt, das eine Referenz auf das entfernte


BenchMachine enthält.

Dienste werden mit der Methode registerAppServerService(...) regis-


triert. Als Parameter wird der Name des Dienstes, der Name des Applikations-
Servers, die minimale Geschwindigkeit, die für das Ausführen des Dienstes
benötigt wird, sowie die Parameter zum Auffinden des Dienstes und der Typ
des Dienstes angegeben. Der Typ definiert, um welche Art von Dienst es
sich handelt. Dabei kann es sich um eine EJB handeln, die mit Hilfe eines
JNDI-Lookups genutzt wird, oder um einen RMI Dienst, der mit Hilfe einer
RMI-Registry lokalisiert wird. Der Observer erzeugt für jeden Dienst eines
Applikations-Servers ein ObserverService Objekt mit einer Referenz zu ei-
nem ServiceType Objekt, das den Typ spezifiziert. Ein Dienst wird über den
Namen eindeutig spezifiziert.

Interesse an Zustandänderungen der Applikations-Server und Dienste kann


mit Hilfe der Methoden addAppServerListener(...) beziehungsweise
addServiceListener(...) angemeldet werden. Mit Hilfe der Parameter
wird der Applikations-Server beziehungsweise Dienst spezifiziert sowie das
Objekt angegeben, das gegebenenfalls benachrichtigt werden soll. Damit der
Beobachter diese Objekte benachrichtigen kann, müssen sie die Schnittstelle
IAppServerListener beziehungsweise IServiceListener implementieren.
Der Beobachter ruft bei Benachrichtigungen die Methode notify(...)
auf. Als Parameter wird dem Interessenten das jeweilige Ereignis geliefert.
Bei Interessenten an Applikations-Servern ist das ein Objekt der Klasse
AppServerEvent und bei Interessenten an Diensten ein Objekt der Klasse
ServiceEvent. Mit diesem Konzept kann ein Objekt Interesse an mehreren
Applikations-Servern und Diensten anmelden. Über den Parameter erhält
das Objekt die entsprechenden Informationen des Applikations-Servers oder
Dienstes.

Aus dem Klassendiagramm 27 geht weiterhin hervor, dass die Klassen


Framework für intelligente Clients 133

BenchServer und Observer eigenständige Applikationen sind. Objekte der


Klasse BenchServer befinden sich auf dem Applikations-Server und führen
den Benchmark durch. Die einzelnen Methoden und Attribute der Benchmark-
Komponente sind in den entsprechenden Klassen und Schnittstellen beschrie-
ben. Zudem verlangt der BenchServer eine Konfigurationsdatei, die fest-
legt, wie oft ein Benchmark ausgeführt wird, wie der Durchschnitt berech-
net wird, sowie die Parameter zum Ablegen des Objektes BenchMachine in
der entsprechenden RMI-Registry. Im Anhang B.4 ist eine Konfigurationsda-
tei BenchServer.properties mit den Standardwerten zu sehen. Objekte der
Klasse Observer bilden den eigentlichen Beobachter. Ein Beobachter verwen-
det ebenfalls eine Konfigurationsdatei, die festlegt, in welchen Abständen die
Geschwindigkeit und Verfügbarkeit der Applikations-Server sowie der Dienste
geprüft wird. Die Parameter, die für das Ablegen des Observer Objektes in der
RMI-Registry zuständig sind, sind ebenfalls in der Konfigurationsdatei spezi-
fiziert. Im Anhang B.15 ist eine Konfigurationsdatei Observer.properties
mit den Standardwerten für den Beobachter zu sehen. In den nachfolgenden
Sequenzdiagrammen ist die Bedeutung der Parameter aus der Konfigurations-
datei zu sehen. Abbildung 30 zeigt ein Sequenzdiagramm über den Start des
Beobachters.
<<static>>
Observer
Start
main(String[] args)
<<create>> Observer

init()

setCeckSpeedTimer()

<<create>>
checkSpeedTimer.Observer
Framework für intelligente Clients

setReconnectBenchTimer()

<<create>> reconnectBenchTimer.Observer

setLookupServiceTimer()

<<create>> lookupServiceTimer.Observer

Abbildung 30: Initialisierung des Beobachters (Observers)


134
Framework für intelligente Clients 135

Bei der Initialisierung des Beobachters werden drei separate Threads gestartet.
Diese Threads arbeiten voneinander unabhängig und sind für die Geschwin-
digkeitsprüfung der Applikations-Server (checkSpeedTimer), die Verfügbar-
keit der BenchMachine-Objekte (reconnectBenchTimer) und die Verfügbar-
keit der Dienste (lookupServiceTimer) verantwortlich. Für jede Messung be-
ziehungsweise Prüfung wird von dem jeweiligen Thread ein weiterer Thread
(Prüfthread) erzeugt, der den eigentlichen Vorgang übernimmt. Dies ist not-
wendig, um nicht in Performance-Engpässe zu geraten. Ruft ein Thread eine
Methode eines entfernten Objektes auf, ist er solange blockiert, bis die entfern-
te Methode einen Rückgabewert liefert. Dies ist für die meisten Anwendungen
nicht weiter tragisch, da sie mit den Daten weiterarbeiten und dann dem Be-
nutzer ein Ergebnis liefern. Der Beobachter soll allerdings ständig Auskunft
über die Methoden der Schnittstelle IObserver geben können. Zudem kann
auch eine Exception auftreten, wenn auf entfernte Objekte zugegriffen wird.
Kann beispielsweise eine RMI-Registry nicht gefunden werden, braucht der
Aufruf der Methode Naming.lookup(...) mehr als 2 Minuten. Für diese Zeit
ist der Thread, der diese Methode aufgerufen hat, blockiert. Aus diesem Grund
wird jeweils ein neuer Prüfthread gestartet, der von dem anderen überwacht
wird. Der Aufbau der einzelnen Threads sowie die zu spezifizierenden Zeiten in
der Konfigurationsdatei sind in den Sequenzdiagrammen in den Abbildungen
31, 32 und 33 zu sehen.
Framework für intelligente Clients 136

checkSpeedTimer.Observer

checkSpeedTimerTask.Observer

checkAppServerSpeed(AppServer1)

<<create>> checkThread.checkSpeedTimerTask.Observer

BenchMachine

MAX_CHECK_SPEED_TIME getAverageSpeed()
C
H Fall 1:
E Fall 1: Status=rechtzeitig
C Fall 2:
K Fall 2 : Status=ueberfaellig
_
S
P
E checkAppServerSpeed(AppServer2)
E
D
_ <<create>> checkThread.checkSpeedTimerTask.Observer
I
N
T ...
E
R
V
A
L .
.
.

checkSpeedTimerTask.Observer

checkAppServerSpeed(AppServer1)

<<create>> checkThread.checkSpeedTimerTask.Observer

...

.
.
.

Abbildung 31: Aufbau des Threads checkSpeedTimer

Die Parameter CHECK SPEED INTERVAL und MAX CHECK SPEED TIME sind für
die Geschwindigkeitsprüfung zu spezifizieren. Bei dieser Prüfung wird für alle
verfügbaren BenchMachine-Objekte die Geschwindigkeit erfragt. Der Parame-
ter CHECK SPEED INTERVAL legt fest, in welchen Abständen die Geschwindig-
keit erfragt wird. Der Parameter MAX CHECK SPEED TIME spezifiziert die Zeit,
die der Methodenaufruf getAverageSpeed() des jeweiligen BenchMachine-
Framework für intelligente Clients 137

Objektes benötigen darf. Liefert die Methode in diesem Zeitrahmen kein Er-
gebnis, wird davon ausgegangen, dass das BenchMachine-Objekt nicht mehr
zur Verfügung steht. Tritt dieser Fall ein, wird der Applikations-Server des
BenchMachine-Objektes in die Liste der nicht verfügbaren BenchMachine-
Objekte verschoben. Wählt man also diese Zeit zu niedrig, führt dies da-
zu, dass alle Applikations-Server der BenchMachine Objekte in die nicht zur
Verfügung stehende Liste verschoben werden. Für das Wiederaufnehmen ei-
nes Applikations-Servers in die zur Verfügung stehende Liste ist der Thread
reconnectBenchTimer verantwortlich. Für ein lokales Netz ist ein Wert ab
200 Millisekunden, je nach Geschwindigkeit des Netzes und der Rechner, zu
empfehlen. Der Wert für das Intervall sollte nicht zu niedrig gewählt werden.
Es hat keinen Sinn häufiger eine Anfrage zu stellen, als die eigentliche Messung
durchgeführt wird. Zu hohe Werte führen dazu, dass eine vernünftige Lasten-
verteilung verhindert wird, da die Messwerte zu alt sind. Werte zwischen 5000
und 60000 Millisekunden sind zu empfehlen. Aber egal wie niedrig die einzelnen
Werte gewählt werden, kommt es nie zu der Situation, dass zwei Prüfthreads
gleichzeitig auf ein BenchMachine-Objekt zugreifen.
Framework für intelligente Clients 138

reconnectBenchTimer.Observer

reconnectBenchTimerTask.Observer

lookupAppServer(AppServer1)

<<create>> checkThread.reconnectBenchTimerTask.Observer

RMI-Registry
R
E Naming.lookup(AppServer1)
C MAX_BENCH_LOOKUP_TIME
O Fall 1:
N Fall 1: Status=rechtzeitig
N Fall 2:
E Fall 2 : Status=ueberfaellig
C
T
_
B lookupAppServer(AppServer2)
E
N
C <<create>> checkThread.reconnectBenchTimerTask.Observer
H
_
I
N ...
T .
E .
R .
V
A
L
reconnectBenchTimerTask.Observer

lookupAppServer(AppServer1)

<<create>> checkThread.reconnectBenchTimerTask.Observer

...

.
.
.

Abbildung 32: Aufbau des Threads reconnectBenchTimer

Der Thread reconnectBenchTimer sorgt dafür, dass Applikations-Server, des-


sen BenchMachine-Objekt die nicht verfügbar war und jetzt verfügbar ist,
wieder in die Liste der verfügbaren Applikations-Server verschoben werden.
Bei dieser Prüfung wird die Liste der nicht verfügbaren Applikations-Server
durchlaufen und jeweils ein Lookup durchgeführt. War der Lookup erfolg-
reich, wird das BenchMachine-Objekt in das ObserverBenchMachine Ob-
jekt des Applikations-Servers übertragen und der entsprechende Applikations-
Framework für intelligente Clients 139

Server in die Liste der verfügbaren BenchMachine-Objekte verschoben. Dieser


Thread sorgt somit dafür, dass der Thread zur Geschwindigkeitsüberprüfung
nur verfügbare BenchMachine Objekte überprüft. Mit diesem Konzept der bei-
den eigenständigen Threads ist es möglich, maximale Performance zu gewähr-
leisten. Der Zugriff auf BenchServer Objekte, die nicht mehr zur Verfügung
stehen, kann in einem viel größeren Intervall überprüft werden. Kommt es
zum Beispiel zu einem Absturz des Applikations-Servers, kann es vorkom-
men, dass das BenchMachine-Objekt tagelang nicht mehr verfügbar ist. So-
mit ist ein Wert ab 15000 Millisekunden für den RECONNECT Bench Interval
völlig ausreichend. Der Parameter MAX BENCH LOOKUP TIME spezifiziert die
Zeit, die ein Lookup benötigen darf. Ist dieser Wert zu niedrig gewählt, wer-
den Applikations-Server nie wieder in die Liste der verfügbaren Applikations-
Server eingetragen. Für ein lokales Netz ist ein Wert ab 400 Millisekunden zu
wählen. Auch bei diesem Thread ist sichergestellt, dass egal wie kurz die Zeiten
gewählt werden, nie zwei Prüfthreads gleichzeitig einen Lookup auf dasselbe
BenchMachine Objekt durchführen.
Framework für intelligente Clients 140

lookupServiceTimer.Observer

lookupServiceTimerTask.Observer

lookupService(AppServer1, Service1)

<<create>> checkThread.lookupServiceTimerTask.Observer
RMI-Registry
L oder
O JNDI
O
K lookup(AppServer1, Service1)
MAX_SERVICE_LOOKUP_TIME
U
P Fall 1:
_ Fall 1: Status=rechtzeitig
S Fall 2:
E Fall 2 : Status=ueberfaellig
R
V
I
C
E lookupService(AppServer2, Service3)
_
I <<create>>
N checkThread.lookupServiceTimerTask.Observer
T
E
R ...
V .
A .
L .

lookupServiceTimerTask.Observer

lookupService(AppServer1, Service1)

<<create>> checkThread.lookupServiceTimerTask.Observer

...
.
.
.

Abbildung 33: Aufbau des Threads lookupServiceTimer

Der Thread lookupServiceTimer überprüft die Verfügbarkeit der Dienste.


Alle angemeldeten Dienste der Applikations-Server werden überprüft. Je nach
dem, ob der Lookup erfolgreich war oder nicht, wird das Attribut available
des Objektes ObserverService gesetzt oder zurückgesetzt. Der Parameter
LOOKUP SERVICE INTERVAL legt den zeitlichen Abstand zwischen den Prüfun-
gen fest. Hier sind Werte ab 5000 Millisekunden sinnvoll. Der Parameter
MAX SERVICE LOOKUP TIME legt fest, wieviel Zeit der Lookup benötigen darf.
Da ein Lookup mit Hilfe von JNDI im Allgemeinen länger dauert als mit Hilfe
Framework für intelligente Clients 141

einer RMI-Registry, sollte hier ein Wert ab 600 Millisekunden gewählt wer-
den. Bei diesem Thread ist ebenfalls gewährleistet, dass nie zwei Prüfthreads
gleichzeitig einen Lookup auf einen Dienst durchführen.

Die Parameter der einzelnen Threads könnnen auch über das remote Inter-
face IObserver verändert werden. Damit kann der Beobachter ferngesteuert
werden.

Wie bereits erwähnt, werden Zustandänderungen von Applikations-Server und


Diensten über Listener den Interessenten mitgeteilt. Damit der Interessent
weiß, um welchen Applikations-Server beziehungsweise Dienst es sich han-
delt, wird als Parameter ein Ereignisobjekt (Event Object) geliefert. In diesem
Objekt befinden sich Informationen über den jeweiligen Applikations-Server
beziehungsweise Dienst, den Sender (also welcher Beobachter die Nachricht
verschickt) und die Art der Zustandänderung. Bei Applikations-Servern un-
terscheidet man zwischen zwei verschiedenen Zuständen (ENABLED, DISAB-
LED). Je nach dem, ob das BenchMachine-Objekt des Applikations-Servers
verfügbar (ENABLED) oder nicht verfügbar (DISABLED) ist, werden die In-
teressenten bei einem Wechsel von einem Zustand zum anderen benachrich-
tigt. Bei den Diensten wird zwischen drei verschiedenen Zuständen (ENAB-
LED, DISABLED, OVERLOAD) unterschieden. Die Bedeutung ist ähnlich
wie bei dem Applikations-Server. Ein Dienst ist verfügbar (ENABLED), wenn
der Lookup erfolgreich war und nicht verfügbar (DISABLED), wenn der Loo-
kup fehlschlug. Der dritte Zustand (OVERLOAD) tritt ein, wenn der Dienst
zwar verfügbar ist, der entsprechende Applikations-Server aber nicht die mi-
nimale Geschwindigkeit bereitstellt. Unterstützt ein Applikations-Server die
Benchmark-Komponente nicht, können nur die beiden Zustände ENABLED
und DISABLED für diese Dienste auftreten.

Da die Listener meist entfernte Rechner benachrichtigen, wird für jeden Lis-
tener ein eigener Thread gestartet, um die Nachricht zu senden. Zum einen
bringt dies Vorteile in der Performance, da mehrere Listener gleichzeitig arbei-
Framework für intelligente Clients 142

ten können, führt aber auch zu dem Problem, dass die Reihenfolge nicht mehr
gesichert werden kann. Ändert sich der Zustand eines Applikations-Servers be-
ziehungsweise Dienstes schnell, kann das dazu führen, dass die Nachrichten auf
der Empfängerseite des Listeners in anderer Reihenfolge erscheinen. Um die-
ses Ereignis zu vermeiden, wird dafür gesorgt, dass nie ein Listener gleichzeitig
mehrere verschiedene Zustände eines Objektes versenden kann. Kommt es also
zu dieser Problematik, führt dies automatisch dazu, dass die Zustandsände-
rungen sequentiell verschickt werden (Synchronisation).

11.1.1. Quellcode des Beobachters

Der Quellcode des Beobachters ist im Anhang B zu finden. Der vollständige


Quellcode kann auch unter [34] heruntergeladen werden. Die einzelnen Metho-
den, Klassen und Schnittstellen sind im Quellcode dokumentiert und werden
hier nicht erläutert.

11.1.2. Starten des Observers und des BenchServers

Die Schritte zum Übersetzen und Erzeugen der Stubs wurden in Abschnitt
7.2 bereits detailliert erläutert. Der Quellcode sowie die erzeugten Klassen
und generierten Stubs kann auch unter [34] heruntergeladen werden. Die nach-
folgenden Befehle gehen davon aus, dass sich die folgende Baumstruktur im
Arbeitsverzeichnis befindet.
Framework für intelligente Clients 143

| BenchServer.properties
| Observer.properties
+---observer
IBenchMachine.class
BenchMachine.class
BenchServer.class
BenchServer$1.class
BenchServer$Benchmark.class
BenchMachine_Stub.class
BenchMachine_Skel.class
ObserverAppServer.class
ObserverService.class
ServiceType.class
IObserver.class
ObserverBenchMachine.class
IAppServerListener.class
AppServerEvent.class
IServiceListener.class
ServiceEvent.class
Observer.class
Observer$1.class
Observer$2.class
Observer$3.class
Observer$4.class
Observer$5.class
Observer$6.class
Observer$7.class
Observer$8.class
Observer_Stub.class
Observer_Skel.class
Framework für intelligente Clients 144

Für den Beobachter beziehungsweise für die Benchmark-Komponente sind je-


weils nur ein Teil der Dateien notwendig. Die Benchmark-Komponente ge-
braucht die Konfigurationsdatei BenchServer.properties und die ersten
sechs Dateien aus dem Unterverzeichnis observer. Der Beobachter hingegen
gebraucht die Konfigurationsdatei Observer.properties und die anderen Da-
teien aus dem Unterverzeichnis observer. Zudem gebraucht der Beobachter
noch die Datei BenchMachine Stub.class, um eine Methode des entfernten
Objektes aufzurufen.
Bevor man den Benchmark beziehungsweise Beobachter erstmalig startet, wird
die Konfigurationsdatei der jeweiligen Anwendung angepasst. Dazu zählen
vor allem die Parameter, die für das Ablegen des Objektes in der RMI-
Registry verantwortlich sind. Weitere Informationen sind in den Beispielen
BenchServer.properties und BenchServer.properties im Anhang B.4 und
B.15 zu finden. Anschließend können Beobachter und Benchmark gestartet
werden. Abweichend von Abschnitt 7.4 ist es nicht notwendig, selbst eine RMI-
Registry zu starten. Die Anwendungen starten gegebenenfalls eine eigene RMI-
Registry, falls noch keine auf dem angegebenen Port arbeitet. Die folgenden
Befehle beziehen sich auf Windows Betriebssysteme. Die vergleichbaren Befeh-
le für Linux-Betriebssysteme sind im Abschnitt 7.4 erläutert.

Starten einer Benchmark-Anwendung


cd d:\user
java observer.BenchServer
Starten einer Beobachter-Anwendung
Damit der Observer die notwendigen JNDI Treiber findet, wird die Bibliothek
J2EE in den CLASSPATH eingetragen.
cd d:\user
set CLASSPATH=%J2EE_HOME%\lib\j2ee.jar;.
java observer.Observer
Je nach Einstellung der Parameter sollte die Konsole ein Ergebnis wie in Ab-
bildung 34 beziehungsweise 35 anzeigen. Der Benchmark dient lediglich zum
Framework für intelligente Clients 145

Messen der Geschwindigkeiten und bietet keine Interaktionen für den Benut-
zer. Ist der Parameter VERBOSE des Benchmark gesetzt, so gibt dieser die ge-
messenen Geschwindigkeiten auf der Konsole aus. Der Beobachter hingegen
bietet dem Benutzer Befehle, mit denen er den Zustand von Diensten und
Applikations-Server erfragen kann. Mit Hilfe des Befehls help werden die Be-
fehle aufgelistet.

Neue RMI-Registry auf Port 1098 gestartet


----------------------------------
Der Rechner: hope0
wurde unter dem Namen benchObj
in der folgenden RMI-Registry registriert.
Rechner: localhost
Port : 1098
----------------------------------
Die Geschwindigkeit wird alle 1000 Millisekunden gemessen.
----------------------------------
BenchServer erfolgreich initialisiert.
Akt: 35714285 Max: 35714285 Min: 35714285 Durchschnitt: 35714285
Akt: 41666666 Max: 41666666 Min: 35714285 Durchschnitt: 36904761
Akt: 41666666 Max: 41666666 Min: 35714285 Durchschnitt: 38095237
Akt: 41666666 Max: 41666666 Min: 35714285 Durchschnitt: 39285713

Abbildung 34: Ausgabe nach dem Start des BenchServers


Framework für intelligente Clients 146

Neue RMI-Registry auf Port 1099 gestartet


--------------------------------
Der Observer wurde unter dem
Namen : ObserverObj
in der folgenden RMI-Registry registriert.
Rechner: hope0
Port : 1099
--------------------------------
checkSpeedInterval : 5000
lookupServiceInterval : 10000
reconnectBenchInterval : 12000
maxCheckSpeedTime : 200
maxServiceLookupTime: 600
maxBenchLookupTime : 400
showException : false
verbose : false
--------------------------------
Observer erfolgreich initialisiert
--------------------------------
Der Befehl <help> zeigt Optionen und Interaktionen !

Abbildung 35: Ausgabe nach dem Start des Beobachters

11.2. Der Verwaltungs-Server

Die Aufgabe des Verwaltungs-Servers ist es, Daten des verteilten Systems
persistent zu halten. Zu den Daten zählen neben den Applikations-Servern
und deren angebotenen Dienste auch deren aktueller Status und Typ. Zu-
dem wird zu jedem Applikations-Server ein Administrator gespeichert. Dieser
Administrator kann per E-Mail benachrichtigt werden, falls Dienste oder die
Benchmark-Komponente (wenn unterstützt) ihren Status wechseln. Damit der
Verwaltungs-Server die zu überwachenden Objekte verteilen kann, werden al-
le Beobachter gespeichert. Abbildung 36 zeigt ein Entity Relationship Model
(ER-Model) über die zu speichernden Daten. Nach der Normalisierung bis zur
dritten Normalform ergibt sich das vollständige Data Dictionary. Dieses Dic-
tionary zeigt Tabelle 8. Es kann nun einfach in eine relationale Datenbank
übertragen werden. Zu jeder Relation wird eine Tabelle in der Datenbank er-
zeugt. Ein SQL Skript ist in Tabelle 9 zu sehen, dass die notwendigen Tabellen
Framework für intelligente Clients 147

erzeugt. Um dieses Skript auszuführen, ist eine Datenbank, die SQL2 (SQL 92)
unterstützt, erforderlich. Dieser Standard wird von fast jeder relationalen Da-
tenbank unterstützt und erlaubt es, die Logik für referentielle Integrität in die
Datenbank auszulagern. Wie der Name bereits andeutet, bezieht sich Refe-
rentielle Integrität auf die umgesetzten Assoziationen zwischen den Tabellen.
Da in der relationalen Welt Assoziationen durch Fremdschlüsselattribute gelöst
werden, muss dafür gesorgt werden, dass Änderungen des Primärschlüssels des
Primärdatensatzes nicht zu ungültigen abhängigen Datensätzen führen. Es gibt
zwei Szenarios, die enstehen können: Der Wert des Primärschlüssels ändert sich
bzw. der Datensatz wird gelöscht. In beiden Fällen muss dafür gesorgt wer-
den, dass die abhängigen Datensätze sich in einem gültigen Zustand befinden.
Mit Hilfe von SQL2 kann man beschreiben was in diesen Fällen zu tun ist.
Änderungen des Primärschlüssels führen normalerweise automatisch zu einer
Änderung des Fremdschlüssels der abhängigen Datensätze. Beim Löschen eines
Datensatzes macht es meist auch Sinn, alle abhängigen Datensätze ebenfalls zu
löschen. Um diese Arbeit nicht selbst zu programmieren, kann man dies gleich
beim Erzeugen der Tabelle festlegen. Die Ereignisse, die beim Löschen eines
Datensatzes eintreten, können direkt aus den Assoziationen des ER-Modells
abgelesen werden. Überall dort, wo eine Kardinalität von 1 oder 1..n festge-
legt ist, führt dies automatisch dazu, dass die abhängigen Datensätze ebenfalls
gelöscht werden. Bei einer Kardinalität 0 oder 0..n kann das Fremdschlüsse-
lattribut der abhängigen Datensätze auch auf null gesetzt werden. Mehr In-
formationen zu referentieller Integrität kann unter [35] gefunden werden.
ID Name EMail ID Interpretation

1 1
Administrator AppServerStatus
Name Host
0..n
0..n
ApplicationServer
ID Host Port LookupName
0..n
1

Benchmark
0,1
Framework für intelligente Clients

0..n
ID Host Port LookupName ...

0..n
0..n Observer
0..n Name Description 0..n

0..n
R_AppServerService Service
0..n
ID Interpretation ID Interpretation

1 1
ServiceStatus ServiceType
ID MinSpeed Host Port LookupName

Abbildung 36: Entity Relationship Model des Verwaltungs-Servers


148
Framework für intelligente Clients 149

Relation : Administrator

Attribute Datentyp null erlaubt Prüfung


-------------------------------------------------------------
ID (Primärschlüssel) Long Nein
Name String(256) Nein
EMail String(256) Nein

Relation : AppServerStatus

Attribute Datentyp null erlaubt Prüfung


-------------------------------------------------------------
ID (Primärschlüssel) Integer Nein
Interpretation String(256) Nein

Relation : Benchmark

Attribute Datentyp null erlaubt Prüfung


-------------------------------------------------------------
ID (Primärschlüssel) Long Nein
Host String(256) Nein
Port String(256) Nein
LookupName String(256) Nein

Relation : ApplicationServer

Attribute Datentyp null erlaubt Prüfung


-------------------------------------------------------------
Name (Primärschlüssel) String(125) Nein
Host String(256) Nein
Framework für intelligente Clients 150

Administrator Long Nein


AppServerStatus Integer Nein
Benchmark Long Ja

Relation : Service

Attribute Datentyp null erlaubt Prüfung


-------------------------------------------------------------
Name (Primärschlüssel) String(125) Nein
Description String(2000) Ja

Relation : ServiceStatus

Attribute Datentyp null erlaubt Prüfung


-------------------------------------------------------------
ID (Primärschlüssel) Integer Nein
Interpretation String(256) Nein

Relation : ServiceType

Attribute Datentyp null erlaubt Prüfung


-------------------------------------------------------------
ID (Primärschlüssel) Integer Nein
Interpretation String(256) Nein

Relation : Observer

Attribute Datentyp null erlaubt Prüfung


-------------------------------------------------------------
ID (Primärschlüssel) Long Nein
Host String(256) Nein
Framework für intelligente Clients 151

Port String(256) Nein


LookupName String(256) Nein
CheckSpeedInterval Long Nein
LookupServiceInterval Long Nein
ReconnectBenchInterval Long Nein
MaxCheckSpeedTime Long Nein
MaxServiceLookupTime Long Nein
MaxBenchLookupTime Long Nein
RemoteControl Integer Nein >-2, <1

Relation : R_AppServerService

Attribute Datentyp null erlaubt Prüfung


-------------------------------------------------------------
ID (Primärschlüssel) Long Nein
ApplicationServer String(125) Nein
Service String(125) Nein
ServiceStatus Integer Nein
ServiceType Integer Nein
MinSpeed Double Nein
Host String(256) Nein
Port String(256) Nein
LookupName String(256) Nein

Relation : R_BenchmarkObserver

Attribute Datentyp null erlaubt Prüfung


-------------------------------------------------------------
ID (Primärschlüssel) Long Nein
Framework für intelligente Clients 152

Benchmark Long Nein


Observer Long Nein

Relation : R_AppServerServiceObserver

Attribute Datentyp null erlaubt Prüfung


-------------------------------------------------------------
ID (Primärschlüssel) Long Nein
AppServerService Long Nein
Observer Long Nein

Tabelle 8: Das vollständiges Data Dictionary des Verwaltungs-Servers

CREATE TABLE Administrator


(
ID NUMERIC(17,0) NOT NULL,
Name VARCHAR(256) NOT NULL,
EMail VARCHAR(256) NOT NULL,
PRIMARY KEY (ID)
);

CREATE TABLE AppServerStatus


(
ID INTEGER NOT NULL,
Interpretation VARCHAR(256) NOT NULL,
PRIMARY KEY (ID)
);

CREATE TABLE Benchmark


(
ID NUMERIC(17,0) NOT NULL,
Host VARCHAR(256) NOT NULL,
Framework für intelligente Clients 153

Port VARCHAR(256) NOT NULL,


LookupName VARCHAR(256) NOT NULL,
PRIMARY KEY (ID)
);

CREATE TABLE ApplicationServer


(
Name VARCHAR(125) NOT NULL,
Host VARCHAR(256) NOT NULL,
Administrator NUMERIC(17,0) NOT NULL REFERENCES
Administrator ON UPDATE CASCADE
ON DELETE CASCADE,
AppServerStatus INTEGER NOT NULL REFERENCES
AppServerStatus ON UPDATE CASCADE
ON DELETE CASCADE,
Benchmark NUMERIC(17,0) REFERENCES Benchmark
ON UPDATE CASCADE
ON DELETE CASCADE,
PRIMARY KEY (Name)
);

CREATE TABLE Service


(
Name VARCHAR(125) NOT NULL,
Description VARCHAR(2000),
PRIMARY KEY (Name)
);

CREATE TABLE ServiceStatus


(
Framework für intelligente Clients 154

ID INTEGER NOT NULL,


Interpretation VARCHAR(256) NOT NULL,
PRIMARY KEY (ID)
);

CREATE TABLE ServiceType


(
ID INTEGER NOT NULL,
Interpretation VARCHAR(256) NOT NULL,
PRIMARY KEY (ID)
);

CREATE TABLE Observer


(
ID NUMERIC(17,0) NOT NULL,
Host VARCHAR(256) NOT NULL,
Port VARCHAR(256) NOT NULL,
LookupName VARCHAR(256) NOT NULL,
CheckSpeedInterval NUMERIC(17,0) NOT NULL,
LookupServiceInterval NUMERIC(17,0) NOT NULL,
ReconnectBenchInterval NUMERIC(17,0) NOT NULL,
MaxCheckSpeedTime NUMERIC(17,0) NOT NULL,
MaxServiceLookupTime NUMERIC(17,0) NOT NULL,
MaxBenchLookupTime NUMERIC(17,0) NOT NULL,
RemoteControl INTEGER NOT NULL,
CHECK (RemoteControl < 1),
CHECK (RemoteControl > -2),
PRIMARY KEY (ID)
);
Framework für intelligente Clients 155

CREATE TABLE R_AppServerService


(
ID NUMERIC(17,0) NOT NULL,
ApplicationServer VARCHAR(125) NOT NULL REFERENCES
ApplicationServer ON UPDATE CASCADE
ON DELETE CASCADE,
Service VARCHAR(125) NOT NULL REFERENCES
Service ON UPDATE CASCADE
ON DELETE CASCADE,
ServiceStatus INTEGER NOT NULL REFERENCES
ServiceStatus ON UPDATE CASCADE
ON DELETE CASCADE,
ServiceType INTEGER NOT NULL REFERENCES
ServiceType ON UPDATE CASCADE
ON DELETE CASCADE,
MINSpeed DOUBLE PRECISION NOT NULL,
Host VARCHAR(256) NOT NULL,
Port VARCHAR(256) NOT NULL,
LookupName VARCHAR(256) NOT NULL,
PRIMARY KEY (ID),
UNIQUE(ApplicationServer,Service)
);

CREATE TABLE R_BenchmarkObserver


(
ID NUMERIC(17,0) NOT NULL,
Benchmark NUMERIC(17,0) NOT NULL REFERENCES
Benchmark ON UPDATE CASCADE
Framework für intelligente Clients 156

ON DELETE CASCADE,
Observer NUMERIC(17,0) NOT NULL REFERENCES
Observer ON UPDATE CASCADE
ON DELETE CASCADE,
PRIMARY KEY (ID),
UNIQUE(Benchmark,Observer)
);

CREATE TABLE R_AppServerServiceObserver


(
ID NUMERIC(17,0) NOT NULL,
AppServerService NUMERIC(17,0) NOT NULL REFERENCES
R_AppServerService ON UPDATE CASCADE
ON DELETE CASCADE,
Observer NUMERIC(17,0) NOT NULL REFERENCES
Observer ON UPDATE CASCADE
ON DELETE CASCADE,
PRIMARY KEY (ID),
UNIQUE(AppServerService,Observer)
);

CREATE VIEW V_AppServerService (ID, MinSpeed, Host, Port,


LookupName,ApplicationServer,Service,Status,
Servicetype) AS
SELECT DISTINCT R_AppServerService.ID,
R_AppServerService.MinSpeed,
R_AppServerService.Host,
R_AppServerService.Port,
R_AppServerService.LookupName,
Framework für intelligente Clients 157

R_AppServerService.ApplicationServer,
R_AppServerService.Service,
ServiceStatus.Interpretation,
ServiceType.Interpretation
FROM R_AppServerService, ServiceStatus, ServiceType
WHERE R_AppServerService.ServiceStatus = ServiceStatus.ID and
R_AppServerService.ServiceType = ServiceType.ID;

CREATE VIEW V_AppServer (Name, Host, Administrator, EMail,


Status,BHost,BPort,BLookupName) AS
SELECT DISTINCT ApplicationServer.Name,
ApplicationServer.Host,
Administrator.Name,
Administrator.EMail,
AppServerStatus.Interpretation,
Benchmark.Host,
Benchmark.Port,
Benchmark.LookupName
FROM Administrator, AppServerStatus, ApplicationServer LEFT
JOIN Benchmark
ON ApplicationServer.Benchmark = Benchmark.ID
WHERE ApplicationServer.Administrator = Administrator.ID and
ApplicationServer.AppServerStatus = AppServerStatus.ID;

CREATE VIEW V_BenchmarkObserver (BenchmarkHost, BenchmarkPort


,BenchmarkLookup, ObserverHost) AS
SELECT DISTINCT Benchmark.Host,
Benchmark.Port,
Benchmark.LookupName,
Framework für intelligente Clients 158

Observer.Host
FROM Observer, Benchmark, R_BenchmarkObserver
WHERE R_BenchmarkObserver.Observer = Observer.ID and
R_BenchmarkObserver.Benchmark = Benchmark.ID;

CREATE VIEW V_AppServerServiceObserver (ID, MinSpeed, Host,


Port, LookupName, ApplicationServer, Service, Status,
Servicetype, Observer) AS
SELECT DISTINCT R_AppServerService.ID,
R_AppServerService.MinSpeed,
R_AppServerService.Host,
R_AppServerService.Port,
R_AppServerService.LookupName,
R_AppServerService.ApplicationServer,
R_AppServerService.Service,
ServiceStatus.Interpretation,
ServiceType.Interpretation,
Observer.Host
FROM R_AppServerService, ServiceStatus, ServiceType,
R_AppServerServiceObserver, Observer
WHERE R_AppServerService.ServiceStatus = ServiceStatus.ID and
R_AppServerService.ServiceType = ServiceType.ID and
R_AppServerServiceObserver.AppServerService =
R_AppServerService.ID and
R_AppServerServiceObserver.Observer = Observer.id;

CREATE USER serveradmin WITH PASSWORD ’failsafe’;


GRANT ALL PRIVILEGES ON Administrator TO SERVERADMIN;
GRANT ALL PRIVILEGES ON ApplicationServer TO SERVERADMIN;
Framework für intelligente Clients 159

GRANT ALL PRIVILEGES ON AppServerStatus TO SERVERADMIN;


GRANT ALL PRIVILEGES ON Benchmark TO SERVERADMIN;
GRANT ALL PRIVILEGES ON Observer TO SERVERADMIN;
GRANT ALL PRIVILEGES ON R_AppServerService TO SERVERADMIN;
GRANT ALL PRIVILEGES ON R_AppServerServiceObserver TO
SERVERADMIN;
GRANT ALL PRIVILEGES ON R_BenchmarkObserver TO SERVERADMIN;
GRANT ALL PRIVILEGES ON Service TO SERVERADMIN;
GRANT ALL PRIVILEGES ON ServiceStatus TO SERVERADMIN;
GRANT ALL PRIVILEGES ON ServiceType TO SERVERADMIN;

Tabelle 9: SQL-Befehle zum Erzeugen der Datenbank Verwaltungs-Server

Um die Daten in der Datenbank zu verwalten, muss der Verwaltungs-Server


Schnittstellen für den Beobachter, Client und Verwaltungs-Client bereitstel-
len. Da diese drei Komponenten zu jedem beliebigen Zeitpunkt auf den
Verwaltungs-Server zugreifen können, muss dafür gesorgt werden, dass der
Verwaltungs-Server mehrere Clients gleichzeitig bedienen kann. Es ist zu
berücksichtigen, dass die Daten in einem konsistenten Zustand bleiben.

Für diese Aufgaben bietet sich das Framework der EJBs an. Entity Be-
ans unterstützen es, Objekte in einer Datenbank zu halten; der Container
kümmert sich darum, dass viele Clients gleichzeitig bedient werden. Mit Hil-
fe von Transaktionen wird dafür gesorgt, dass sich die Daten stets in einem
konsistenten Zustand befinden. Abbildung 37 zeigt einen Überblick über die
zu entwickelnden Teilkomponenten des Verwaltungs-Servers. Zu jeder Tabelle
der Datenbank wird eine Entity Bean entwickelt. Diese Beans sind im Pa-
ket serverAdmin.entity zusammengefasst. Damit die verschiedenen Clients
später nicht auf all diese Entity Beans zugreifen müssen, werden für die einzel-
Framework für intelligente Clients 160

nen Clients Session Bean entwickelt. Diese Session Beans versorgen die Clients
jeweils mit einer Kopie der Entity Bean. Dieses Konzept wurde bereits in Ka-
pitel 5 vorgestellt und bringt für diese Anwendung noch weitere Vorteile:

• Für die Entity Beans werden nur die lokalen Schnittstellen entwickelt.

• Clients können nur über die definierten Schnittstellen auf die Daten
(Entity Beans) des verteilten Systems zugreifen. Somit bilden die Ses-
sion Beans jeweils eine Hülle um die Entity Beans und entsprechen dem
Entwurfsmuster der Fassade (Facade). Eine Fassade ist eine einheitliche
Schnittstelle zu einer Menge von Schnittstellen eines Subsystems. Mehr
Informationen dazu findet man unter [32] .

• Mit Hilfe dieser Hüllen kann man den verschiedenen Clients (Beobachter,
Verwaltungs-Client, normaler Client) individuellen Zugriff auf die Daten
gewähren. Der Verwaltungs Client darf alle Daten lesen sowie verändern.
Ein normaler Client stellt nur Anfragen über verfügbare Applikations-
Server für einen bestimmten Dienst. Dementsprechend besitzt die EJB
ServerAdminClient nur eine Methode getApplicationServer(...).
Somit ist es dem Client nicht möglich, andere Daten zu lesen bzw. Daten
überhaupt zu verändern. Ähnlich ergeht es der Beobachter-Komponente:
Sie kann den Verwaltungs-Server nur über Zustandänderungen der Diens-
te und Applikations-Server benachrichtigen.

• Dadurch, dass für die verschiedenen Clients nur die Methoden in den
Session Beans bereitgestellt werden, die für Sie notwendig sind, werden
die zu übertragenen Stubs klein gehalten.

• Durch Kopien der Entity Beans werden die Referenzen zur Entity Bean
umgehend von der Session Bean freigegeben. Ohne Freigabe führt dies
schnell zu einem Flaschenhals einer verteilten Anwendung. Solange eine
Referenz zu einer Entity Bean besteht, ist im Allgemeinen auch eine
Datenbankverbindung aufgebaut.
Framework für intelligente Clients 161

• Einzelne Teilkomponenten der Anwendung können ausgetauscht bezie-


hungsweise weiterentwickelt werden.

• Die Entwickler der normalen Clients brauchen kein Wissen über die
Zusammenhänge der EJB-Teilkomponenten zu haben. Sie stellen ledig-
lich eine Anfrage bei der EJB ServerAdminClient und erhalten einen
verfügbaren Applikations-Server für einen bestimmten Dienst.

• Die einzelnen Clients erhalten eine Referenz zu einem entfernten Objekt


(der jeweiligen Session Bean), um Daten zu lesen beziehungsweise zu
schreiben.

• Die Session Beans werden als zustandlose Session Beans auf den
Applikations-Server abgelegt. Der Container erzeugt also nicht für je-
den einzelnen Client eine eigene Instanz. Dieses Konzept wurde bereits
in Abschnitt 3.1.1 beschrieben.
Verwaltungs-
Client Client

<<package>>
ServerAdmin

<<package>>
serverAdmin.entity.clone
AppServerServiceClone
+RMI: int = 1
siehe Abbildung 54 +EJB_SUN: int = 2
+EJB_JBOSS: int = 3
-appServer: String
-host: String
-port: int
-lookupName: String
Framework für intelligente Clients

-serviceType: int
Session Bean: ServerAdmin Session Bean: ServerAdminClient +AppServerServiceClone(host:String,port:int)
siehe Abbildung 38 siehe Abbildung 39 (lookupName:String,appServer:String)
(serviceType:int): Konstruktor
+getAppServer(): String
+getHost(): String
+getLookupName(): String
+getPort(): int <<package>>
+getServiceType(): int observer
<<package>>
serverAdmin.entity
<<Interface>>
Session Bean: ServerAdminObserver
IObserver
Entity Bean: Administrator Entity Bean: AppServerStatus Egntity Bean: Benchmark
siehe Abbildung 43 siehe Abbildung 44 siehe Abbildung 45 siehe Abbildung 40 siehe Abbildung 28
1 1 1 1 +...():

* * * *
Entity Bean: ApplicationServer Entity Bean: R_BenchmarkObserver ServiceListener
siehe Abbildung 46 siehe Abbildung 47 -ctx: InitialContext
-home: ServerAdminObserverHome <<Interface>>
1 *
-host: String IServiceListener
-lookup: String
Entity Bean: R_AppServerServiceObserver -port: String +notify(e:ServiceEvent): void
siehe Abbildung 48 * -server: ServerAdminObserver
1 1 +ServiceListener(host:String,port:String)
* 1 -init(): void
Entity Bean: Observer + (lookup:String): Konstruktor
Entity Bean: R_AppServerService siehe Abbildung 50
siehe Abbildung 49 *
1
* *
Entity Bean: Service AppServerListener <<Interface>>
siehe Abbildung 51 -ctx: InitialContext IAppServerListener
-home: ServerAdminObserverHome
-host: String +notify(e:AppServerEvent): void
1 1 -lookup: String
-port: String
Entity Bean: ServiceStatus Entity Bean: ServiceType
162

-server: ServerAdminObserver
siehe Abbildung 52 siehe Abbildung 53
+AppServerListener(host:String,port:String)
(lookup:String): Konstruktor
-init(): void

Abbildung 37: Aufbau der Komponente ServerAdmin


Framework für intelligente Clients 163

Die Session Bean ServerAdmin dient als Brücke zwischen dem Verwaltungs-
Server und den Entity Beans, die die Daten des verteilten Systems wieder-
spiegeln. Sie erlaubt es dem Verwaltungs-Client, alle Methoden der Entity
Beans aufzurufen, ohne die genauen Zusammenhänge der Entity Beans zu ken-
nen. Mit ihr kann man, neue Datensätze zu erzeugen, bestehende Datensätze
verändern, löschen oder suchen. Die Kommunikation geschieht grundsätzlich
über Kopien der Entity Beans. Die Klassen, die die Kopien bilden, sind im
Paket serverAdmin.entity.clone definiert. Sie haben die selben Attribu-
te wie die jeweiligen Entity Beans beziehungsweise die entsprechende Tabelle
in der Datenbank, stellen jedoch nur für einige Attribute Methoden set...
bereit. Dadurch wird sichergestellt, dass der Verwaltungs-Client bestimmte
Daten nicht ändert. Grundsätzlich ist es nicht erlaubt das Attribut, das den
Primärschlüssel in der Datenbank darstellt, zu ändern.

Neue Datensätze werden mit den Methoden add... erzeugt. Als Pa-
rameter wird ein Objekt der entsprechenden Klasse aus dem Paket
serverAdmin.entity.clone erwartet. Über den Konstruktor der jeweili-
gen Klasse werden alle Attribute gesetzt. Dies ist die einzige Variante, die
Attribute, die über keine entsprechende Methode set... verfügen, zu setzen.
Das Suchen nach bestimmten Daten aus der Datenbank übernehmen die
Methoden get..., die wiederum die Methoden mit den SQL Ausdrücken
der Entity Beans aufrufen. Dies hat den Vorteil, dass der Verwaltungs-Client
lediglich eine Benutzeroberfläche bereitstellt. Die Logik der Benutzerober-
fläche befindet sich somit in der Session Bean ServerAdmin. Will man in
dem Verwaltung-Client beispielsweise alle Applikations-Server auflisten, die
von einen bestimmten Administrator betreut werden, so wird nur die Me-
thode getApplicationServerByAdmin(...) aufgerufen. Als Ergebnis liefert
die Methode eine Liste von Objekten ApplicationServerClone, die vom
Verwaltungs-Client dargestellt werden. Zudem erlaubt dieses Konzept es,
mehrere Verwaltungs-Clients parallel zu betreiben, ohne die Logik doppelt zu
programmieren. Es könnte zum Beispiel ein eigenständiger Verwaltungs-Client
Framework für intelligente Clients 164

und ein Web-basierter Verwaltungs-Client entwickelt werden. Der Web ba-


sierte Verwaltungs-Client hat den Vorteil, dass er von jedem Rechner genutzt
werden kann. Er kann aber nur schwer Bedienelemente bereitstellen, mit
denen eine große Anzahl von Daten schnell eingetragen werden können. Der
eigenständige Verwaltungs-Client kann mit Hilfe von Swing-Komponenten
erzeugt werden, um dem Benutzer gewohnte Techniken wie Drag und Drop
oder ähnliches zu bieten. Je nach Aufgabe kann sich der Benutzer dann für
eine Benutzeroberfläche entscheiden. Nähere Informationen zu den einzelnen
Methoden findet man im Quellcode der EJB ServerAdmin C.2.

Die Session Bean ServerAdminClient dient als Brücke zwischen den


Clients, die bestimmte Dienste nutzen möchten, und den Entity Beans,
die die Daten des verteilten Systems wiederspiegeln. Über die Methode
getApplicationServer(serviceName : String) kann ein Client eine Lis-
te von verfügbaren Applikations-Servern bekommen. Dabei ist sichergestellt,
dass nur die Applikations-Server zurückgeliefert werden, die den angegebe-
nen Dienst serviceName unterstützen, und deren Dienst sich weder im Zu-
stand OVERLOAD noch im Zustand DISABLED befindet. Die Bedeutung
der Zustände wurde im Abschnitt 11.1 erläutert.

Die Session Bean ServerAdminObserver dient als Brücke zwischen dem Be-
obachter, der Applikations-Server und Dienste überwacht, und den Entity Be-
ans, die den Zustand der Applikations-Server und Dienste persistent speichern.
Werden mit Hilfe des Verwaltungs-Clients, beziehungsweise der Session Bean
ServerAdmin neue Daten für einen Beobachter eingetragen, wird der Beobach-
ter automatisch benachrichtigt. Die Kommunikation vom Verwaltungs-Server
zum Beobachter geschieht mit Hilfe der Schnittstelle IObserver aus Abschnitt
11.1. Für die Kommunikation in Gegenrichtung werden Listener verwendet.
In Abschnitt 11.1 wurden bereits die Schnittstellen entwickelt, die ein Liste-
ner implementieren muss, um ihn beim Beobachter zu registrieren. Es gibt
mehrere Varianten, wie man solch einen Listener in verteilten Anwendungen
Framework für intelligente Clients 165

implementiert. Im Allgemeinen wird man den Listener als entferntes Objekt


implementieren, damit Methodenaufrufe direkt auf der entfernten Maschine,
die den Listener registriert hat, ausgeführt werden. Dies hat den Vorteil, dass
man die Komponente, die der Listener benachrichtigt, direkt lokal ansprechen
kann. Zudem ist dieses Konzept recht einfach mit Hilfe von RMI zu implemen-
tieren. Verschiedene Implementationsvarianten findet man unter [33]. Leider
hat dieses Konzept für den Verwaltungs-Server zwei entscheidende Nachteile,
denn zum einen sind die Listener beziehungsweise Stubs nur solange gültig, wie
das Listener-Objekt auf dem Verwaltungs-Server existiert. Zum anderen wird
für jedes entfernte Objekt ein eigener Port verwendet. Dies könnte zu einem
Problem des Beobachters sowie des Verwaltungs-Servers führen.

Ein anderer Ansatz ist es, den Listener selbst so intelligent zu machen, dass er
eigenständig eine Verbindung zum entfernten Objekt aufbaut, die Methoden
aufruft und die Verbindung abbaut. Dies setzt natürlich voraus, dass es sich bei
dem Objekt, dass vom Listener benachrichtigt werden soll, um ein entferntes
Objekt handelt und das Objekt auf einem Name-Server abgelegt ist. Dieses
Konzept wird in den Listenern AppServerListener und ServiceListener
verwendet und führt dazu, dass selbst nach einem Neustart des Verwaltungs-
Servers die alten Listener ihre Arbeit verrichten. Zudem werden keine Res-
sourcen verbraucht, da es sich bei den Listenern nicht um entfernte Objekte
handelt. Der einzige Nachteil dieses Konzeptes ist der höhere Implementati-
onsaufwand.
Framework für intelligente Clients 166

<<Interface>> <<Interface>> <<Interface>>


javax.ejb.EJBHome javax.ejb.EJBObject javax.ejb.SessionBean
+remove(): void +remove(): void +setSessionContext(ctx:javax.ejb.SessionContext): void
+...() +getEJBHome(): javax.ejb.EJBHome +ejbRemove(): void
+...() +....()
<<package>>
serverAdmin

<<Interface>> <<Interface>> ServerAdminBean


ServerAdminHome ServerAdmin siehe Abbildung 42
siehe Abbildung 41
+create(): ServerAdmin
+...():

+...()

Abbildung 38: UML-Diagramm der EJB ServerAdmin

<<Interface>> <<Interface>> <<Interface>>


javax.ejb.EJBHome javax.ejb.EJBObject javax.ejb.SessionBean
+remove(): void +remove(): void +setSessionContext(ctx:javax.ejb.SessionContext): void
+...() +getEJBHome(): javax.ejb.EJBHome +ejbRemove(): void
+...() +....()
<<package>>
serverAdmin

<<Interface>> <<Interface>> ServerAdminClientBean


ServerAdminClientHome ServerAdminClient +ctx: SessionContext
+create(): ServerAdminClient +getApplicationServer(serviceName:String) +ejbActivate(): void
(): Collection of AppServerServiceClone +ejbCreate(): void
+ejbPassivate(): void
+ejbRemove(): void
+setSessionContext(ctx:SessionContext): void
+getApplicationServer(serviceName:String)
(): Collection of AppServerServiceClone
-init(): void

Abbildung 39: UML-Diagramm der EJB ServerAdminClient

<<Interface>> <<Interface>> <<Interface>>


javax.ejb.EJBHome javax.ejb.EJBObject javax.ejb.SessionBean
+remove(): void +remove(): void +setSessionContext(ctx:javax.ejb.SessionContext): void
+...() +getEJBHome(): javax.ejb.EJBHome +ejbRemove(): void
+...() +....()
<<package>>
serverAdmin

<<Interface>> <<Interface>> ServerAdminObserverBean


ServerAdminObserverHome ServerAdminObserver +ctx: SessionContext
+create(): ServerAdminObserver +notify(e:ServiceEvent): void +ejbActivate(): void
+notify(e:AppServerEvent): void +ejbCreate(): void
+ejbPassivate(): void
+ejbRemove(): void
+setSessionContext(ctx:SessionContext): void
+notify(e:ServiceEvent): void
+notify(e:AppServerEvent): void
-init(): void

Abbildung 40: UML-Diagramm der EJB ServerAdminObserver


Framework für intelligente Clients 167

<<Interface>>
ServerAdmin
+addAdministrator(admin:AdministratorClone): void
+changeAdministrator(admin:AdministratorClone): void
+removeAdministrator(admin:AdministratorClone): void
+getAllAdministrator(): Collection of AdministratorClone
+getAdministratorByPrimaryKey(id:long): AdministratorClone
+getAdministratorByEMail(eMail:String): Collection of AdministratorClone
+getAdministratorByName(name:String): Collection of AdministratorClone
+addAppServerStatus(status:AppServerStatusClone): void
+changeAppServerStatus(status:AppServerStatusClone): void
+removeAppServerStatus(status:AppServerStatusClone): void
+getAllAppServerStatus(): Collection of AppServerStatusClone
+getAppServerStatusByPrimaryKey(id:int): AppServerStatusClone
+getAppServerStatusByInterpretation(interpretation:String): Collection of AppServerStatusClone
+addApplicationServer(appServer:ApplicationServerClone): void
+changeApplicationServer(appServer:ApplicationServerClone): void
+removeApplicationServer(appServer:ApplicationServerClone): void
+getAllApplicationServer(): Collection of ApplicationServerClone
+getApplicationServerByPrimaryKey(name:String): ApplicationServerClone
+getApplicationServerByAdmin(id:long): Collection of ApplicationServerClone
+getApplicationServerByBenchmark(id:long): Collection of ApplicationServerClone
+getApplicationServerByHost(host:String): Collection of ApplicationServerClone
+getApplicationServerByStatus(id:int): Collection of ApplicationServerClone
+addBenchmark(bench:BenchmarkClone): void
+changeBenchmark(bench:BenchmarkClone): void
+removeBenchmark(bench:BenchmarkClone): void
+getAllBenchmark(): Collection of BenchmarkClone
+getBenchmarkByPrimaryKey(id:long): Collection of BenchmarkClone
+getBenchmarkByHost(host:String): Collection of BenchmarkClone
+addObserver(observer:ObserverClone): void
+changeObserver(observer:ObserverClone): void
+removeObserver(observer:ObserverClone): void
+getAllObserver(): Collection of ObserverClone
+getObserverByPrimaryKey(id:long): ObserverClone
+getObserverByHost(host:String): ObserverClone
+addService(service:ServiceClone): void
+changeService(service:ServiceClone): void
+removeService(service:ServiceClone): void
+getAllService(): Collection of ServiceClone
+getServiceByPrimaryKey(name:String): ServiceClone
+getServiceByDescription(description:String): Collection of ServiceClone
+addServiceStatus(status:ServiceStatusClone): void
+changeService(status:ServiceStatusClone): void
+removeServiceStatus(status:ServiceStatusClone): void
+getAllServiceStatus(): Collection of ServiceStatusClone
+getServiceStatusByPrimaryKey(id:int): ServiceStatusClone
+getServiceStatusByInterpretation(interpretation:String): Collection of ServiceStatusClone
+addServiceType(type:ServiceTypeClone): void
+changeServiceType(type:ServiceTypeClone): void
+removeServiceType(type:ServiceTypeClone): void
+getAllServiceType(): Collection of ServiceTypeClone
+getServiceTypeByPrimaryKey(id:int): ServiceTypeClone
+getServiceTypeByInterpretation(interpretation:String): Collection of ServiceTypeClone
+addR_AppServerService(appServerService:R_AppServerServiceClone): void
+changeR_AppServerService(appServerService:R_AppServerServiceClone): void
+removeR_AppServerService(appServerService:R_AppServerServiceClone): void
+getAllR_AppServerService(): Collection of R_AppServerServiceClone
+getR_AppServerServiceByPrimaryKey(id:long): R_AppServerServiceClone
+getR_AppServerServiceByAppServer(name:String): Collection of R_AppServerServiceClone
+getR_AppServerServiceByAppServerAndService(appServer:Strign,service:String)
(): Collection of R_AppServerServiceClone
+getR_AppServerServiceByHost(host:String): Collection of R_AppServerServiceClone
+getR_AppServerServiceByService(name:String): Collection of R_AppServerServiceClone
+getR_AppServerServiceByStatus(id:int): Collection of R_AppServerServiceClone
+getR_AppServerServiceByType(id:int): Collection of R_AppServerServiceClone
+registerAppServerServiceToObserver(assoziation:R_AppServerServiceObserverClone): void
+unregisterR_AppServerServiceObserverFromObserver(assoziation:R_AppServerServiceObserverClone): void
+getAllR_AppServerServiceObserver(): Collection of R_AppServerServiceObserverClone
+getR_AppServerServiceObserverByPrimaryKey(id:long): Collection of R_AppServerServiceObserverClone
+getR_AppServerServiceObserverByAppServerService(id:long): Collection of R_AppServerServiceObserverClone
+getR_AppServerServiceObserverByObserver(id:long): Collection of R_AppServerServiceObserverClone
+registerBenchmarkToObserver(assoziation:R_BenchmarkObserverClone): void
+unregisterBenchmarkFromObserver(assoziation:R_BenchmarkObserverClone): void
+getAllR_BenchmarkObserver(): Collection of R_BenchmarkObserverClone
+getR_BenchmarkObserverByPrimaryKey(id:long): R_BenchmarkObserverClone
+getR_BenchmarkObserverByBenchmark(id:long): Collection of R_BenchmarkObserverClone
+getR_BenchmarkObserverByObserver(id:long): Collection of R_BenchmarkObserverClone
+resetObserver(id:long): boolean

Abbildung 41: UML-Diagramm der Schnittstelle ServerAdmin


Framework für intelligente Clients 168

ServerAdminBean
-adminHome: AdministratorLocalHome
-appServerHome: ApplicationServerLocalHome
-appServerStatusHome: AppServerStatusLocalHome
-benchmarkHome: BenchmarkLocalHome
-ctx: SessionContext
-observerHome: ObserverLocalHome
-r_AppServerServiceHome: R_AppServerServiceLocalHome
-r_AppServerServiceObserverHome: R_AppServerServiceObserverLocalHome
-r_BenchmarkObserverHome: R_BenchmarkObserverLocalHome
-serviceHome: ServiceLocalHome
-serviceStatusHome: ServiceStatusLocalHome
-serviceTypeHome: ServiceTypeLocalHome
+ejbActivate(): void
+ejbCreate(): void
+ejbPassivate(): void
+ejbRemove(): void
+setSessionContext(ctx:SessionContext): void
+addAdministrator(admin:AdministratorClone): void
+changeAdministrator(admin:AdministratorClone): void
+removeAdministrator(admin:AdministratorClone): void
+getAllAdministrator(): Collection of AdministratorClone
+getAdministratorByPrimaryKey(id:long): AdministratorClone
+getAdministratorByEMail(eMail:String): Collection of AdministratorClone
+getAdministratorByName(name:String): Collection of AdministratorClone
+addAppServerStatus(status:AppServerStatusClone): void
+changeAppServerStatus(status:AppServerStatusClone): void
+removeAppServerStatus(status:AppServerStatusClone): void
+getAllAppServerStatus(): Collection of AppServerStatusClone
+getAppServerStatusByPrimaryKey(id:int): AppServerStatusClone
+getAppServerStatusByInterpretation(interpretation:String): Collection of AppServerStatusClone
+addApplicationServer(appServer:ApplicationServerClone): void
+changeApplicationServer(appServer:ApplicationServerClone): void
+removeApplicationServer(appServer:ApplicationServerClone): void
+getAllApplicationServer(): Collection of ApplicationServerClone
+getApplicationServerByPrimaryKey(name:String): ApplicationServerClone
+getApplicationServerByAdmin(id:long): Collection of ApplicationServerClone
+getApplicationServerByBenchmark(id:long): Collection of ApplicationServerClone
+getApplicationServerByHost(host:String): Collection of ApplicationServerClone
+getApplicationServerByStatus(id:int): Collection of ApplicationServerClone
+addBenchmark(bench:BenchmarkClone): void
+changeBenchmark(bench:BenchmarkClone): void
+removeBenchmark(bench:BenchmarkClone): void
+getAllBenchmark(): Collection of BenchmarkClone
+getBenchmarkByPrimaryKey(id:long): Collection of BenchmarkClone
+getBenchmarkByHost(host:String): Collection of BenchmarkClone
+addObserver(observer:ObserverClone): void
+changeObserver(observer:ObserverClone): void
+removeObserver(observer:ObserverClone): void
+getAllObserver(): Collection of ObserverClone
+getObserverByPrimaryKey(id:long): ObserverClone
+getObserverByHost(host:String): ObserverClone
+addService(service:ServiceClone): void
+changeService(service:ServiceClone): void
+removeService(service:ServiceClone): void
+getAllService(): Collection of ServiceClone
+getServiceByPrimaryKey(name:String): ServiceClone
+getServiceByDescription(description:String): Collection of ServiceClone
+addServiceStatus(status:ServiceStatusClone): void
+changeService(status:ServiceStatusClone): void
+removeServiceStatus(status:ServiceStatusClone): void
+getAllServiceStatus(): Collection of ServiceStatusClone
+getServiceStatusByPrimaryKey(id:int): ServiceStatusClone
+getServiceStatusByInterpretation(interpretation:String): Collection of ServiceStatusClone
+addServiceType(type:ServiceTypeClone): void
+changeServiceType(type:ServiceTypeClone): void
+removeServiceType(type:ServiceTypeClone): void
+getAllServiceType(): Collection of ServiceTypeClone
+getServiceTypeByPrimaryKey(id:int): ServiceTypeClone
+getServiceTypeByInterpretation(interpretation:String): Collection of ServiceTypeClone
+addR_AppServerService(appServerService:R_AppServerServiceClone): void
+changeR_AppServerService(appServerService:R_AppServerServiceClone): void
+removeR_AppServerService(appServerService:R_AppServerServiceClone): void
+getAllR_AppServerService(): Collection of R_AppServerServiceClone
+getR_AppServerServiceByPrimaryKey(id:long): R_AppServerServiceClone
+getR_AppServerServiceByAppServer(name:String): Collection of R_AppServerServiceClone
+getR_AppServerServiceByAppServerAndService(appServer:Strign,service:String): Collection of R_AppServerServiceClone
+getR_AppServerServiceByHost(host:String): Collection of R_AppServerServiceClone
+getR_AppServerServiceByService(name:String): Collection of R_AppServerServiceClone
+getR_AppServerServiceByStatus(id:int): Collection of R_AppServerServiceClone
+getR_AppServerServiceByType(id:int): Collection of R_AppServerServiceClone
+registerAppServerServiceToObserver(assoziation:R_AppServerServiceObserverClone): void
+unregisterR_AppServerServiceObserverFromObserver(assoziation:R_AppServerServiceObserverClone): void
+getAllR_AppServerServiceObserver(): Collection of R_AppServerServiceObserverClone
+getR_AppServerServiceObserverByPrimaryKey(id:long): Collection of R_AppServerServiceObserverClone
+getR_AppServerServiceObserverByAppServerService(id:long): Collection of R_AppServerServiceObserverClone
+getR_AppServerServiceObserverByObserver(id:long): Collection of R_AppServerServiceObserverClone
+registerBenchmarkToObserver(assoziation:R_BenchmarkObserverClone): void
+unregisterBenchmarkFromObserver(assoziation:R_BenchmarkObserverClone): void
+getAllR_BenchmarkObserver(): Collection of R_BenchmarkObserverClone
+getR_BenchmarkObserverByPrimaryKey(id:long): R_BenchmarkObserverClone
+getR_BenchmarkObserverByBenchmark(id:long): Collection of R_BenchmarkObserverClone
+getR_BenchmarkObserverByObserver(id:long): Collection of R_BenchmarkObserverClone
+resetObserver(id:long): boolean
-createAdministratorClone(admin:AdministratorLocal): AdministratorClone
-createAppServerStatusClone(status:AppServerStatusLocal): AppServerStatusClone
-createApplicationServerClone(appServer:ApplicationServerLocal): ApplicationServerClone
-createBenchmarkClone(benchmark:BenchmarkLocal): BenchmarkClone
-createObserverClone(observer:ObserverLocal): ObserverClone
-createServiceClone(service:ServiceLocal): ServiceClone
-createServiceStatusClone(status:ServiceStatusLocal): ServiceStatusClone
-createServiceTypeClone(serviceType:createServiceTypeLocal): ServiceTypeClone
-createR_AppServerServiceClone(assoziation:R_AppServerServiceLocal): R_AppServerServiceClone
-createR_AppServerServiceObserverClone(assoziation:R_AppServerServiceObserverLocal): R_AppServerServiceObserverClone
-createR_BenchmarkObserverClone(assoziation:R_BenchmarkObserverLocal): R_BenchmarkObserverClone

Abbildung 42: UML-Diagramm der Klasse ServerAdminBean


Framework für intelligente Clients 169

<<Interface>> <<Interface>> <<Interface>>


javax.ejb.EJBLocalObject javax.ejb.EJBLocalHome javax.ejb.EntityBean
+remove(): void +remove(): void +setEntityContext(ctx:javax.ejb.EntityContext): void
+getEJBLocalHome(): javax.ejb.EJBLocalHome +unsetEntityContext(): void
+...() +ejbRemove(): void
+....()
<<package>>
serverAdmin.entity

<<Interface>>
AdministratorLocalHome
+create(id:long,name:String,eMail:String): AdministratorLocal
+findByPrimaryKey(id:Long): AdministratorLocal
+findAllAdministrator(): Collection of AdministratorLocal
+findByName(name:String): Collection of AdministratorLocal
+findByEMail(eMail:String): Collection of AdministratorLocal
<<Interface>>
AdministratorLocal
AdministratorBean
+getEMail(): String -ctx: EntityContext
+getID(): long -eMail: String
+getName(): String -id: long
+setEMail(eMail:String): void -name: String
+setName(name:String): void +ejbActivate(): void
+ejbPassivate(): void
+ejbLoad(): void
+ejbRemove(): void
+ejbStore(): void
+setEntityContext(ctx:EntityContext): void
+unsetEntityContext(): void
+ejbCreate(id:long,name:String,eMail:String): Long
+ejbPostCreate(id:long,name:String,eMail:String): void
+ejbFindByPrimaryKey(id:long): Long
+ejbFindAllAdministrator(): Collection of Long
+ejbFindByName(name:String): Collection of Long
+ejbFindByEMail(eMail:String): Collection of Long
+getEMail(): String
+getID(): long
+getName(): String
+setEMail(eMail:String): void
+setName(name:String): void
-getConnection(): Connection
-setID(id:long): void

Abbildung 43: UML-Diagramm der EJB Administrator

<<Interface>> <<Interface>> <<Interface>>


javax.ejb.EJBLocalObject javax.ejb.EJBLocalHome javax.ejb.EntityBean
+remove(): void +remove(): void +setEntityContext(ctx:javax.ejb.EntityContext): void
+getEJBLocalHome(): javax.ejb.EJBLocalHome +unsetEntityContext(): void
+...() +ejbRemove(): void
+....()
<<package>>
serverAdmin.entity

<<Interface>>
AppServerStatusLocal
+getInterpretation(): String
+getID(): int
+setInterpretation(interpretation:String): void

<<Interface>>
AppServerStatusLocalHome
+create(id:int,interpretation:String): AppServerStatusLocal
+findByPrimaryKey(id:Integer): AppServerStatusLocal
+findAllAppServerStatus(): Collection of AppServerStatusLocal
+findByInterpretation(interpretation:String): Collection of AppServerStatusLocal

AppServerStatusBean
-ctx: EntityContext
-id: int
-interpretation: String
+ejbActivate(): void
+ejbPassivate(): void
+ejbLoad(): void
+ejbRemove(): void
+ejbStore(): void
+setEntityContext(ctx:EntityContext): void
+unsetEntityContext(): void
+ejbCreate(id:int,interpretation:String): Integer
+ejbPostCreate(id:int,interpretation:String): void
+ejbFindByPrimaryKey(id:Integer): Integer
+ejbFindAllAppServerStatus(): Collection of Integer
+ejbFindByInterpretation(interpretation:String): Collection of Integer
+getInterpretation(): String
+getID(): int
+setInterpretation(interpretation:String): void
-getConnection(): Connection
-setID(id:int): void

Abbildung 44: UML-Diagramm der EJB AppServerStatus


Framework für intelligente Clients 170

<<Interface>> <<Interface>> <<Interface>>


javax.ejb.EJBLocalObject javax.ejb.EJBLocalHome javax.ejb.EntityBean
+remove(): void +remove(): void +setEntityContext(ctx:javax.ejb.EntityContext): void
+getEJBLocalHome(): javax.ejb.EJBLocalHome +unsetEntityContext(): void
+...() +ejbRemove(): void
+....()
<<package>>
serverAdmin.entity

<<Interface>>
BenchmarkLocal
+getHost(): String
+getID(): long
+getLookupName(): String
+getPort(): int
+setHost(host:String): void
+setLookupName(lookupName:String): void
+setPort(port:int): void

<<Interface>>
BenchmarkLocalHome
+create(id:long,host:String,port:int,lookupName:String): BenchmarkLocal
+findByPrimaryKey(id:Long): BenchmarkLocal
+findAllBenchmark(): Collection of BenchmarkLocal
+findByHost(host:String): Collection of BenchmarkLocal

BenchmarkBean
-ctx: EntityContext
-host: String
-id: long
-lookupName: String
-port: int
+ejbActivate(): void
+ejbPassivate(): void
+ejbLoad(): void
+ejbRemove(): void
+ejbStore(): void
+setEntityContext(ctx:EntityContext): void
+unsetEntityContext(): void
+ejbCreate(id:long,host:String,port:int,lookupName:String): Long
+ejbPostCreate(id:long,host:String,port:int,lookupName:String): void
+ejbFindByPrimaryKey(id:Long): Long
+ejbFindAllBenchmark(): Collection of Long
+ejbFindByHost(host:String): Collection of Long
+getHost(): String
+getID(): long
+getLookupName(): String
+getPort(): int
+setHost(host:String): void
+setLookupName(lookupName:String): void
+setPort(port:int): void
-getConnection(): Connection
-setID(id:long): void

Abbildung 45: UML-Diagramm der EJB Benchmark


Framework für intelligente Clients 171

<<Interface>> <<Interface>> <<Interface>>


javax.ejb.EJBLocalObject javax.ejb.EJBLocalHome javax.ejb.EntityBean
+remove(): void +remove(): void +setEntityContext(ctx:javax.ejb.EntityContext): void
+getEJBLocalHome(): javax.ejb.EJBLocalHome +unsetEntityContext(): void
+...() +ejbRemove(): void
+....()

<<package>>
serverAdmin.entity

<<Interface>>
ApplicationServerLocal
+getAdministrator(): long
+getAppServerStatus(): int
+getBenchmark(): long
+getHost(): String
+getName(): String
+setAdministrator(admin:long): void
+setAppServerStatus(status:int): void
+setBenchmark(bench:long): void
+setHost(host:String): void

<<Interface>>
ApplicationServerLocalHome
+create(name:String,host:String,admin:long,status:int,bench:long): ApplicationServerLocal
+findByPrimaryKey(name:String): ApplicationServerLocal
+findAllApplicationServer(): Collection of ApplicationServerLocal
+findByAdmin(adminID:long): Collection of ApplicationServerLocal
+findByBenchmark(benchID:long): Collection of ApplicationServerLocal
+findByHost(host:String): Collection of ApplicationServerLocal
+findByStatus(statusID:int): Collection of ApplicationServerLocal

ApplicationServerBean
-admin: long
-bench: long
-ctx: EntityContext
-host: String
-name: String
-status: int
+ejbActivate(): void
+ejbPassivate(): void
+ejbLoad(): void
+ejbRemove(): void
+ejbStore(): void
+setEntityContext(ctx:EntityContext): void
+unsetEntityContext(): void
+ejbCreate(name:String,host:String,admin:long,status:int,bench:long): String
+ejbPostCreate(name:String,host:String,admin:long,status:int,bench:long): void
+ejbFindByPrimaryKey(name:String): String
+ejbFindAllApplicationServer(): Collection of String
+ejbFindByAdmin(adminID:long): Collection of String
+ejbFindByBenchmark(benchID:long): Collection of String
+ejbFindByHost(host:String): Collection of String
+ejbFindByStatus(statusID:int): Collection of String
+getAdministrator(): long
+getAppServerStatus(): int
+getBenchmark(): long
+getHost(): String
+getName(): String
+setAdministrator(admin:long): void
+setAppServerStatus(status:int): void
+setBenchmark(bench:long): void
+setHost(host:String): void
-getConnection(): Connection
-setName(name:String): void

Abbildung 46: UML-Diagramm der EJB ApplicationServer


Framework für intelligente Clients 172

<<Interface>> <<Interface>> <<Interface>>


javax.ejb.EJBLocalObject javax.ejb.EJBLocalHome javax.ejb.EntityBean
+remove(): void +remove(): void +setEntityContext(ctx:javax.ejb.EntityContext): void
+getEJBLocalHome(): javax.ejb.EJBLocalHome +unsetEntityContext(): void
+...() +ejbRemove(): void
+....()
<<package>>
serverAdmin.entity

<<Interface>>
R_BenchmarkObserverLocal
+getBenchmark(): long
+getID(): long
+getObserver(): long

<<Interface>>
R_BenchmarkObserverLocalHome
+create(id:long,benchmark:long,observer:long): R_BenchmarkObserver
+findByPrimaryKey(id:long): R_BenchmarkObserver
+findAllR_BenchmarkObserver(): Collection of R_BenchmarkObserver
+findByBenchmark(benchmark:long): Collection of R_BenchmarkObserver
+findByObserver(observer:long): Collection of R_BenchmarkObserver

R_BenchmarkObserverBean
-ctx: EntityContext
-benchmark: long
-id: long
-observer: long
+ejbActivate(): void
+ejbPassivate(): void
+ejbLoad(): void
+ejbRemove(): void
+ejbStore(): void
+setEntityContext(ctx:EntityContext): void
+unsetEntityContext(): void
+ejbCreate(id:long,benchmark:long,observer:long): Long
+ejbPostCreate(id:long,benchmark:long,observer:long): void
+ejbFindByPrimaryKey(id:long): Long
+ejbFindAllR_BenchmarkObserver(): Collection of Long
+ejbFindByBenchmark(benchmark:long): Collection of Long
+ejbFindByObserver(observer:long): Collection of Long
+getBenchmark(): long
+getID(): long
+getObserver(): long
-getConnection(): Connection
-setID(id:long): void

Abbildung 47: UML-Diagramm der EJB R BenchmarkObserver


<<Interface>> <<Interface>> <<Interface>>
javax.ejb.EJBLocalObject javax.ejb.EJBLocalHome javax.ejb.EntityBean
+remove(): void +remove(): void +setEntityContext(ctx:javax.ejb.EntityContext): void
+getEJBLocalHome(): javax.ejb.EJBLocalHome +unsetEntityContext(): void
+...() +ejbRemove(): void
+....()
<<package>>
serverAdmin.entity

<<Interface>>
R_AppServerServiceObserverLocal
+getAppServerService(): long
+getID(): long
+getObserver(): long

<<Interface>>
R_AppServerServiceObserverLocalHome
+create(id:long,appServerService:long,observer:long): R_AppServerServiceObserverLocal
+findByPrimaryKey(id:long): R_AppServerServiceObserverLocal
+findAllR_AppServerServiceObserver(): Collection of R_AppServerServiceObserverLocal
+findByAppServerService(appServerService:long): Collection of R_AppServerServiceObserverLocal
+findByObserver(observer:long): Collection of R_AppServerServiceObserverLocal

R_AppServerServiceObserverBean
-ctx: EntityContext
-appServerService: long
-id: long
-observer: long
+ejbActivate(): void
+ejbPassivate(): void
+ejbLoad(): void
+ejbRemove(): void
+ejbStore(): void
+setEntityContext(ctx:EntityContext): void
+unsetEntityContext(): void
+ejbCreate(id:long,appServerService:long,observer:long): Long
+ejbPostCreate(id:long,appserverService:long,observer:long): void
+ejbFindByPrimaryKey(id:long): Long
+ejbFindAllR_AppServerServiceObserver(): Collection of Long
+ejbFindByAppServerService(appServerService:long): Collection of Long
+ejbFindByObserver(observer:long): Collection of Long
+getAppServerService(): long
+getID(): long
+getObserver(): long
-getConnection(): Connection
-setID(id:long): void

Abbildung 48: UML-Diagramm der EJB R AppServerServiceObserver


<<Interface>> <<Interface>> <<Interface>>
javax.ejb.EJBLocalObject javax.ejb.EJBLocalHome javax.ejb.EntityBean
+remove(): void +remove(): void +setEntityContext(ctx:javax.ejb.EntityContext): void
+getEJBLocalHome(): javax.ejb.EJBLocalHome +unsetEntityContext(): void
+...() +ejbRemove(): void
+....()
<<package>>
serverAdmin.entity

<<Interface>>
R_AppServerServiceLocalHome
+create(id:long,minSpeed:double,host:String,port:int,lookupName:String,appServer:String,service:String,status:int,serviceType:int): R_AppServerServiceLocal
+findByPrimaryKey(id:Long): R_AppServerServiceLocal
+findAllR_AppServerService(): Collection of R_AppServerServiceLocal
+findByAppServer(appServer:String): Collection of R_AppServerServiceLocal
+findByAppServerAndService(AppServerName:String,serviceName:String): Collection of R_AppServerServiceLocal
+findByHost(host:String): Collection of R_AppServerServiceLocal
+findByService(service:String): Collection of R_AppServerServiceLocal
+findByServiceStatus(status:int): Collection of R_AppServerServiceLocal
+findByServiceType(serviceType:int): Collection of R_AppServerServiceLocal

<<Interface>> R_AppServerServiceBean
R_AppServerServiceLocal -appServer: String
-ctx: EntityContext
+getAppServer(): String -host: String
+getHost(): String
Framework für intelligente Clients

-id: long
+getID(): long -lookupName: String
+getLookupName(): String -minSpeed: double
+getPort(): int -port: int
+getMinSpeed(): double -service: String
+getService(): String -serviceType: int
+getServiceStatus(): int -status: int
+getServiceType(): int
+setHost(host:String): void +ejbActivate(): void
+setLookupName(lookupName:String): void +ejbPassivate(): void
+setMinSpeed(minSpeed:double): void +ejbLoad(): void
+setPort(port:int): void +ejbRemove(): void
+setServiceStatus(status:int): void +ejbStore(): void
+setEntityContext(ctx:EntityContext): void
+unsetEntityContext(): void
+ejbCreate(id:long,minSpeed:double,host:String,port:int,lookupName:String,appServer:String,service:String,status:int,serviceType:int): Long
+ejbPostCreate(id:long,minSpeed:double,host:String,port:int,lookupName:String,appServer:String,service:String,status:int,serviceType:int): void
+ejbFindByPrimaryKey(id:Long): Long
+ejbFindAllR_AppServerService(): Collection of Long
+ejbFindByAppServer(appServer:String): Collection of Long
+ejbFindByAppServerAndService(AppServerName:String,serviceName:String): Collection of Long
+ejbFindByHost(host:String): Collection of Long
+ejbFindByService(service:String): Collection of Long
+ejbFindByServiceStatus(status:int): Collection of Long
+ejbFindByServiceType(serviceType:int): Collection of Long
+getAppServer(): String
+getHost(): String
+getID(): long
+getLookupName(): String
+getPort(): int
+getMinSpeed(): double
+getService(): String
+getServiceStatus(): int
+getServiceType(): int
+setHost(host:String): void
+setLookupName(lookupName:String): void
+setMinSpeed(minSpeed:double): void
+setPort(port:int): void
+setServiceStatus(status:int): void
-getConnection(): Connection
-setID(id:long): void
173

Abbildung 49: UML-Diagramm der EJB R AppServerService


<<Interface>> <<Interface>> <<Interface>>
javax.ejb.EJBLocalObject javax.ejb.EJBLocalHome javax.ejb.EntityBean
+remove(): void +remove(): void +setEntityContext(ctx:javax.ejb.EntityContext): void
+getEJBLocalHome(): javax.ejb.EJBLocalHome +unsetEntityContext(): void
+...() +ejbRemove(): void
+....()
<<package>>
serverAdmin.entity

<<Interface>>
ObserverLocalHome
+create(id:long,host:String,port:int,lookupName:String,checkSpeedInterval:long,lookupServiceInterval:long,reconnectBenchInterval:long):
(maxCheckSpeedTime:long,maxServiceLookuptime:long,maxBenchLookuptime:long,remoteControl:boolean): ObserverLocal
+findByPrimaryKey(id:Long): ObserverLocal
+findAllObserver(): Collection of ObserverLocal
+findByHost(host:String): Collection of ObserverLocal

<<Interface>> ObserverBean
ObserverLocal -ctx: EntityContext
-host: String
+getCheckSpeedInterval(): long -id: long
+getHost(): String -lookupName: String
+getID(): long -port: int
+getLookupName(): String
Framework für intelligente Clients

-checkSpeedInterval: long
+getLookupServiceInterval(): long -lookupServiceInterval: long
+getMaxBenchLookuptime(): long -reconnectBenchInterval: long
+getMaxCheckSpeedTime(): long -maxCheckSpeedTime: long
+getMaxServiceLookupTime(): long -maxServiceLookupTime: long
+getPort(): int -maxBenchLookupTime: long
+getReconnectBenchInterval(): long -remoteControl: boolean
+isRemoteControl(): boolean
+setCheckSpeedInterval(checkSpeedInterval:long): void +ejbActivate(): void
+setHost(host:String): void +ejbPassivate(): void
+setLookupName(lookupName:String): void +ejbLoad(): void
+setLookupServiceInterval(serviceInterval:long): void +ejbRemove(): void
+setMaxBenchLookupTime(maxLookupTime:long): void +ejbStore(): void
+setMaxCheckSpeedTime(maxLookupTime:long): void +setEntityContext(ctx:EntityContext): void
+setMaxServiceLookupTime(maxLookupTime:long): void +unsetEntityContext(): void
+setPort(port:int): void +ejbCreate(id:long,host:String,port:int,lookupName:String,checkSpeedInterval:long,lookupServiceInterval:long,reconnectBenchInterval:long)
+setReconnectBenchInterval(interv:long): void (maxCheckSpeedTime:long,maxServiceLookuptime:long,maxBenchLookuptime:long,remoteControl:boolean): Long
+setRemoteControl(remoteControl:boolean): void +ejbPostCreate(id:long,host:String,port:int,lookupName:String,checkSpeedInterval:long,lookupServiceInterval:long,reconnectBenchInterval:long)
(maxCheckSpeedTime:long,maxServiceLookupTime:long,maxBenchLookuptime:long,remoteControl:boolean): void
+ejbFindByPrimaryKey(id:Long): Long
+ejbFindAllObserver(): Collection of Long
+ejbFindByHost(host:String): Collection of Long
+getCheckSpeedInterval(): long
+getHost(): String
+getID(): long
+getLookupName(): String
+getLookupServiceInterval(): long
+getMaxBenchLookuptime(): long
+getMaxCheckSpeedTime(): long
+getMaxServiceLookupTime(): long
+getPort(): int
+getReconnectBenchInterval(): long
+isRemoteControl(): boolean
+setCheckSpeedInterval(checkSpeedInterval:long): void
+setHost(host:String): void
+setLookupName(lookupName:String): void
+setLookupServiceInterval(serviceInterval:long): void
+setMaxBenchLookupTime(maxLookupTime:long): void
+setMaxCheckSpeedTime(maxLookupTime:long): void
+setMaxServiceLookupTime(maxLookupTime:long): void
+setPort(port:int): void
+setReconnectBenchInterval(interv:long): void
+setRemoteControl(remoteControl:boolean): void
174

-getConnection(): Connection
-setID(id:long): void

Abbildung 50: UML-Diagramm der EJB Observer


Framework für intelligente Clients 175

<<Interface>> <<Interface>> <<Interface>>


javax.ejb.EJBLocalObject javax.ejb.EJBLocalHome javax.ejb.EntityBean
+remove(): void +remove(): void +setEntityContext(ctx:javax.ejb.EntityContext): void
+getEJBLocalHome(): javax.ejb.EJBLocalHome +unsetEntityContext(): void
+...() +ejbRemove(): void
+....()
<<package>>
serverAdmin.entity

<<Interface>>
ServiceLocal
+getDescription(): String
+getName(): String
+setDescription(description:String): void

<<Interface>>
ServiceLocalHome
+create(name:String,description:String): ServiceLocal
+findByPrimaryKey(name:String): ServiceLocal
+findAllAppServerStatus(): Collection of ServiceLocal
+findByDescription(description:String): Collection of ServiceLocal

ServiceBean
-ctx: EntityContext
-name: String
-description: String
+ejbActivate(): void
+ejbPassivate(): void
+ejbLoad(): void
+ejbRemove(): void
+ejbStore(): void
+setEntityContext(ctx:EntityContext): void
+unsetEntityContext(): void
+ejbCreate(name:String,description:String): String
+ejbPostCreate(name:String,description:String): void
+ejbFindByPrimaryKey(name:String): String
+ejbFindAllService(): Collection of String
+ejbFindByDescription(description:String): Collection of String
+getDescription(): String
+getName(): String
+setDescription(description:String): void
-getConnection(): Connection
-setName(name:String): void

Abbildung 51: UML-Diagramm der EJB Service

<<Interface>> <<Interface>> <<Interface>>


javax.ejb.EJBLocalObject javax.ejb.EJBLocalHome javax.ejb.EntityBean
+remove(): void +remove(): void +setEntityContext(ctx:javax.ejb.EntityContext): void
+getEJBLocalHome(): javax.ejb.EJBLocalHome +unsetEntityContext(): void
+...() +ejbRemove(): void
+....()
<<package>>
serverAdmin.entity

<<Interface>>
ServiceStatusLocal
+getInterpretation(): String
+getID(): int
+setInterpretation(interpretation:String): void

<<Interface>>
ServiceStatusLocalHome
+create(id:int,interpretation:String): ServiceStatusLocal
+findByPrimaryKey(id:Integer): ServiceStatusLocal
+findAllServiceStatus(): Collection of ServiceStatusLocal
+findByInterpretation(interpretation:String): Collection of ServiceStatusLocal

ServiceStatusBean
-ctx: EntityContext
-id: int
-interpretation: String
+ejbActivate(): void
+ejbPassivate(): void
+ejbLoad(): void
+ejbRemove(): void
+ejbStore(): void
+setEntityContext(ctx:EntityContext): void
+unsetEntityContext(): void
+ejbCreate(id:int,interpretation:String): Integer
+ejbPostCreate(id:int,interpretation:String): void
+ejbFindByPrimaryKey(id:Integer): Integer
+ejbFindAllServiceStatus(): Collection of Integer
+ejbFindByInterpretation(interpretation:String): Collection of Integer
+getInterpretation(): String
+getID(): int
+setInterpretation(interpretation:String): void
-getConnection(): Connection
-setID(id:int): void

Abbildung 52: UML-Diagramm der EJB ServiceStatus


Framework für intelligente Clients 176

<<Interface>> <<Interface>> <<Interface>>


javax.ejb.EJBLocalObject javax.ejb.EJBLocalHome javax.ejb.EntityBean
+remove(): void +remove(): void +setEntityContext(ctx:javax.ejb.EntityContext): void
+getEJBLocalHome(): javax.ejb.EJBLocalHome +unsetEntityContext(): void
+...() +ejbRemove(): void
+....()
<<package>>
serverAdmin.entity

<<Interface>>
ServiceTypeLocal
+getInterpretation(): String
+getID(): int
+setInterpretation(interpretation:String): void

<<Interface>>
ServiceTypeLocalHome
+create(id:int,interpretation:String): ServiceTypeLocal
+findByPrimaryKey(id:Integer): ServiceTypeLocal
+findAllServiceType(): Collection of ServiceTypeLocal
+findByInterpretation(interpretation:String): Collection of ServiceTypeLocal

ServiceTypeBean
-ctx: EntityContext
-id: int
-interpretation: String
+ejbActivate(): void
+ejbPassivate(): void
+ejbLoad(): void
+ejbRemove(): void
+ejbStore(): void
+setEntityContext(ctx:EntityContext): void
+unsetEntityContext(): void
+ejbCreate(id:int,interpretation:String): Integer
+ejbPostCreate(id:int,interpretation:String): void
+ejbFindByPrimaryKey(id:Integer): Integer
+ejbFindAllServiceType(): Collection of Integer
+ejbFindByInterpretation(interpretation:String): Collection of Integer
+getInterpretation(): String
+getID(): int
+setInterpretation(interpretation:String): void
-getConnection(): Connection
-setID(id:int): void

Abbildung 53: UML-Diagramm der EJB ServiceType


<<package>>
serverAdmin.entity.clone

AdministratorClone AppServerStatusClone BenchmarkClone


-eMail: String -id: int -host: String
-id: long -interpretation: String -id: long
-name: String +AppServerStatusClone(id:int,interpretation:String): Konstruktor -lookupName: String
+AdmininstratorClone(id:long,name:String,eMail:String): Konstruktor +getID(): int -port: int
+getEMail(): String +getInterpretation(): String +BenchmarkClone(id:long,host:String,port:int,lookupName:String): Konstruktor
+getID(): long +setInterpretation(interpretation:String): void +getHost(): String
+getName(): String 1 +getID(): long
+setEMail(eMail:String): void +getLookupName(): String
+setName(name:String): void +getPort(): int
1 +setHost(host:String): void
+setLookupName(lookupName:String): void
+setPort(port:int): void
0,1 1

* * 1
ApplikationServerClone
-admin: long
-bench: long R_BenchmarkObserverClone
-host: String -id: long
-name: String -benchmark: long
*
-status: int -observer: long
+ApplicationServerClone(name:String,host:String,admin:long,bench:long,status:int): Konstruktor *
+getAdministrator(): long +R_BenchmarkObserverClone(id:long,benchmark:long,observer:long): Konstruktor
+getAppServerStatus(): int +getBenchmark(): long
+getBenchmark(): long +getID(): long
+getHost(): String +getObserver(): long
+getName(): String
+setAdministrator(admin:long): void
Framework für intelligente Clients

+setAppServerStatus(status:int): void
+setBenchmark(bench:long): void
+setHost(host:String): void
1

R_AppServerServiceObserverClone
-id: long *
-appServerService: long
-observer: long
1 1
* 1 +R_AppServerServiceObserverClone(id:long,appServerService:long,observer:long): Konstruktor
+getAppServerService(): long ObserverClone
R_AppServerServiceClone +getID(): long siehe Abbildung 55
-id: long +getObserver(): long
-appServer: String
-host: String
-port: int
-lookupName: String
-minSpeed: double * +...()
1
-service: String
-serviceType: int ServiceClone
-status: int -name: String
+R_AppServerServiceClone(id:long,minSpeed:double,host:String) -description: String
(port:int,lookupName:String,appServer:String) +ServiceClone(name:String,description:String): Konstruktor
(service:String,status:int,serviceType:int): Konstruktor +getDescription(): String
+getAppServer(): String +getName(): String
+getHost(): String +setDescription(description:String): void
+getID(): long
+getLookupName(): String *
+getPort(): int *
+getMinSpeed(): double 1 1
+getService(): String ServiceStatusClone ServiceTypeClone
+getServiceStatus(): int
+getServiceType(): int -id: int -id: int
+setHost(host:String): void -interpretation: String -interpretation: String
+setLookupName(lookupName:String): void +ServiceStatusClone(id:int,interpretation:String): Konstruktor +ServiceTypeClone(id:int,interpretation:String): Konstruktor
+setMinSpeed(minSpeed:double): void +getID(): int +getID(): int
+setPort(port:int): void +getInterpretation(): String +getInterpretation(): String
+setServiceStatus(status:int): void +setInterpretation(interpretation:String): void +setInterpretation(interpretation:String): void
177

Abbildung 54: UML-Diagramm des Paketes serverAdmin.entity.clone


Framework für intelligente Clients 178

ObserverClone
-id: long
-host: String
-port: int
-lookupName: String
-checkSpeedInterval: long
-lookupSpeedInterval: long
-reconnectBenchInterval: long
-maxCheckSpeedTime: long
-maxServiceLookupTime: long
-maxBenchLookupTime: long
-remoteControl: boolean
+ObserverClone(id:long,host:String,port:int,lookupName:String,checkSpeedInterval:long)
(lookupServiceInterval:long,reconnectBenchInterval:long,maxCheckSpeedTime:long)
(maxLookupServiceTime:long,maxBenchLookupTime:long,remoteControl:boolean): Konstruktor
+getCheckSpeedInterval(): long
+getHost(): String
+getID(): long
+getLookupName(): String
+getLookupServiceInterval(): long
+getMaxBenchLookuptime(): long
+getMaxCheckSpeedTime(): long
+getMaxServiceLookupTime(): long
+getPort(): int
+getReconnectBenchInterval(): long
+isRemoteControl(): boolean
+setCheckSpeedInterval(checkSpeedInterval:long): void
+setHost(host:String): void
+setLookupName(lookupName:String): void
+setLookupServiceInterval(serviceInterval:long): void
+setMaxBenchLookupTime(maxLookupTime:long): void
+setMaxCheckSpeedTime(maxLookupTime:long): void
+setMaxServiceLookupTime(maxLookupTime:long): void
+setPort(port:int): void
+setReconnectBenchInterval(interv:long): void
+setRemoteControl(remoteControl:boolean): void

Abbildung 55: UML-Diagramm des Paketes serverAdmin.entity.clone 2

11.2.1. Quellcode des Verwaltungs-Servers

Der Quellcode des Verwaltungs-Servers ist im Anhang C zu finden. Der


vollständige Quellcode kann unter [34] heruntergeladen werden. Die einzelnen
Methoden, Klassen und Schnittstellen sind im Quellcode dokumentiert und
werden hier nicht erläutert.

11.2.2. Die Komponente ServerAdmin auf den Applikations-Server


heraufladen

In den vorherigen Kapiteln ist bereits deutlich gemacht worden, wie man En-
terprise JavaBeans auf einen Applikations-Server lädt. Bei den Session Beans
ist darauf zu achten, dass sie eine Referenz zu allen Entity Beans haben. Zudem
braucen die Session Beans ServerAdminObserver und ServerAdminObserver,
die Klassen aus dem Paket Observer, um mit den Beobachtern zu kommuni-
zieren. Ein fertiges ear-Archiv liegt unter [34]. Um dieses Archiv auf einen
Framework für intelligente Clients 179

Applikations-Server heraufzuladen, ist es notwendig, dass eine Datenbank un-


ter dem JNDI Namen failsafeDB abgelegt ist. Nähere Informationen zu Da-
tenbanken sind im Abschnitt 5.3 zu finden. Auch diesmal ist darauf zu achten,
dass beim Heraufladen (deploy) der Applikation auf dem Applikations Server
ein Client Archiv erzeugt wird. Im Folgenden wird davon ausgegangen, dass
dieses Archiv den Dateinamen failsafeClient.jar trägt. In diesem Archiv
befinden sich neben den Schnittstellen der EJBs auch die Stubs, die für die Ver-
bindung zu den einzelnen Session Beans notwendig sind. Aus diesem Grund
wird der CLASSPATH, der vor dem Start des Beobachters gesetzt wird, noch
einmal angepasst, damit die Listener später eine Verbindung zur Session Bean
ServerAdminObserver aufbauen können. Das Kommando sieht folgenderma-
ßen aus.

set CLASSPATH=%J2EE_HOME%\lib\j2ee.jar;failsafe;.

11.3. Der Verwaltungs-Client

Der Verwaltungs-Server aus Abschnitt 11.2 wurde so entwickelt, dass


der Verwaltungs-Client nur eine Benutzeroberfläche für die Session Bean
ServerAdmin bereitstellt. Da die Möglichkeiten grafischer Oberflächen sehr
vielfältig sind und ihre Entwicklung viel Zeit in Anspruch nehmen kann,
wird diese Komponente nicht im Rahmen der Diplomarbeit erstellt. Sie wird
als Projekt an anderen Studenten vergeben. Sie bietet ein gutes Beispiel für
Outsourcing. Die Entwickler brauchen wegen der Schnittstellen das dahin-
ter liegende System nicht zu verstehen. Zudem müssen weder Quellcodes
noch vertrauliche Informationen der Anwendung herausgegeben werden. Ist
die Komponente fertiggestellt, kann sie ebenfalls unter [34] heruntergeladen
werden beziehungsweise befindet sich mit im ear-Archiv.
Testen der Hochverfügbarkeitsanwendung 180

12. Testen der Hochverfügbarkeitsanwendung

Um das System zu testen, wird ein eigenständiger Test-Client entwickelt, der


über die Session Bean SessionAdminClient verfügbare Applikations-Server
für einen bestimmten Dienst erhält. Beim Test des Systems geht es nicht um die
Funktionalität der Dienste sondern darum das ein Client stets einen verfügba-
ren Applikations-Server erhält. Der Test-Client wird so entwickelt, dass er als
Argument den Namen eines Dienstes erhält und daraufhin alle verfügbaren
Applikations-Server auf die Konsole ausgibt. Diese Aufgabe wird direkt in die
Methode main() implementiert. Deshalb wird kein UML-Diagramm für diese
Komponente entworfen.

12.1. Quellcode des Test-Clients

Der nachfolgende Quellcode zeigt die Implementation des Test-Clients. Der


Quellcode kann unter [34] heruntergeladen werden.

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: TestClient
9 * Date: 25.03.2003
10 * Version: 1.0
11 */
12
13 import serverAdmin.*;
14 import javax.naming.*;
15 import javax.rmi.*;
16 import java.util.*;
17
18
19 /**
20 * Dies ist ein TestClient für die Hochverfügbarkeits-
Testen der Hochverfügbarkeitsanwendung 181

21 * anwendung. Er greift über die EJB ServerAdminClient


22 * auf die Daten der Verwaltungs-Komponente zu, um
23 * verfügbare Applikations-Server für einen Dienst zu
24 * bekommen. Im Hintergrund sorgen Beobachter dafür das
25 * die Verwaltungs sorgen über den Status der Applikations-
26 * Server und Dienste informiert werden.
27 */
28 public class TestClient {
29 public static void main (String[] args)throws Exception{
30
31 if (args.length != 3){
32 System.out.println("Ungültige Anzahl von Argumenten");
33 System.out.println("Bitte geben Sie den Rechnernamen+"+
34 ", die Adresse des des Verwaltungs-Servers sowie den"+
35 " Dienst, nach dem gesucht werden soll, mit der "+
36 "folgenden Syntax an."+
37 "TestClient host port service");
38
39 System.exit(0);
40 }
41
42 String host = args[0];
43 String port = args[1];
44 String service = args[2];
45
46 Properties env = new Properties();
47 env.put("java.naming.factory.initial",
48 "com.sun.jndi.cosnaming.CNCtxFactory");
49 env.put("java.naming.provider.url","iiop://" + host
50 + ":" + port);
51
52 InitialContext ctx = new InitialContext(env);
53 ServerAdminClientHome home=null;
54 Object obj = ctx.lookup("ServerAdminClient");
55 home = (ServerAdminClientHome) PortableRemoteObject.
56 narrow(obj, ServerAdminClientHome.class);
57 ServerAdminClient server = home.create();
58
59 System.out.println("Anfrage auf Dienst :" + service);
60 Collection coll = server.getApplicationServer(service);
61 Iterator iter = coll.iterator();
62
63 while (iter.hasNext()){
64 String type ="";
65 AppServerServiceClone a =
Testen der Hochverfügbarkeitsanwendung 182

66 (AppServerServiceClone) iter.next();
67 System.out.println("--------------------------------");
68 System.out.println("Applikations-Server :" +
69 a.getAppServer());
70 System.out.println("Host :"
71 + a.getHost());
72 System.out.println("Port :"
73 + a.getPort());
74 System.out.println("Lookup :" +
75 a.getLookupName());
76 if (a.getServiceType() == a.EJB_JBOSS){
77 type = "EJB-JBOSS";
78 }
79 if (a.getServiceType() == a.EJB_SUN){
80 type = "EJB-SUN";
81 }
82 if (a.getServiceType() == a.RMI){
83 type = "RMI";
84 }
85 System.out.println("TYPE :" + type);
86 }
87 }
88 }

12.2. Vorbereitungen um den Test-Client zu starten

Um das System zu testen, werden zunächst Dienste auf die Applikations-Server


verteilt und in das System eingetragen. Als Beispieldienste dienen die Adress-
datenbank aus Kapitel 5, der asynchrone Dateileser aus Kapitel 9 und die
Benchmark-Komponente aus Kapitel 11. Die einzelnen Dienste sollen zeigen,
dass die Hochverfügbarkeitsanwendung sowohl EJBs Dienste als auch RMI
Dienste überwachen kann. Die Adressdatenbank stellt eine EJB mit Datenban-
kanbindung dar. Die Benchmark-Komponente ist ein RMI Dienst und der Da-
teileser ist eine Kombination aus EJB und RMI. Die Benchmark-Komponente
stellt bei diesem Beispiel eine Besonderheit dar. Sie wird zum einen von den
Beobachtern genutzt, um die Geschwindigkeit der Applikations-Server zu über-
Testen der Hochverfügbarkeitsanwendung 183

wachen, und zum anderen wird Sie dem Test-Client als Dienst bereitgestellt.
Dies sollte man im Allgemeinen natürlich nicht tun, da diese Komponente
ausschließlich für den Beobachter entwickelt wurde. Um die Hochverfügbar-
keit von RMI Diensten jedoch testen zu können, muss ein RMI Dienst in der
Datenbank eingetragen werden. Die Benchmark-Komponente bietet dabei, die
beste Lösung von den entwickelten RMI Anwendungen dieser Diplomarbeit.
Tabelle 10 zeigt eine Beispielverteilung der Dienste auf Applikations-Servern.

Name des App- App- App- App-


Applikations- S117R4P2 S117R4P4 S117R4P5 S117R1P2
Servers
Rechnername S117R4P2 S117R4P4 S117R4P5 S117R1P2
Typ des SUN RMI RMI SUN J-Boss RMI
Applikations-
Servers
Port des 1050 1098 1098 1050 1099 1098
Applikations-
Servers
Dienst: X X X
Benchmark
Dienst: X X X
AdressDB
Dienst: X X
ReadFile

Tabelle 10: Verteilung der Beispielkonfiguration

Die Dienste werden von zwei Beobachtern, die sich auf den Rechnern
S117R4P2 und S117R4P7 befinden, überwacht. Da zum jetzigen Zeitpunkt
kein Verwaltungs-Client entwickelt wurde, werden die Daten entweder über
die Konsole der Datenbank direkt oder über einen zusätzlichen Client, der die
SessionBean ServerAdmin nutzt, eingetragen. Der folgende Quellcode zeigt ei-
ne Klasse DataInput, die die Daten aus Tabelle 10 und die beiden Beobachter
in die Datenbank einträgt. Die minimalen Benchmark-Werte, die den einzel-
nen Diensten zugewiesen sind, sollte bei dem Test bei ungefähr 80 Prozent der
Testen der Hochverfügbarkeitsanwendung 184

Höchstleistung des Applikations-Servers liegen. Dadurch ist es einfacher, den


Zustand OVERLOAD der Dienste zu erreichen.

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: DataInputClient
9 * Date: 26.03.2003
10 * Version: 1.0
11 */
12
13 import serverAdmin.*;
14 import serverAdmin.entity.clone.*;
15 import javax.naming.*;
16 import javax.rmi.*;
17 import java.util.*;
18
19 /**
20 * Diese Klasse dient zum Eintragen der Testdaten in die
21 * Datenbank.
22 */
23 public class DataInputClient {
24 public static void main (String[] args)throws Exception{
25
26 if (args.length != 2){
27 System.out.println("Ungültige Anzahl von Argumenten");
28 System.out.println("Bitte geben Sie den Rechnernamen+"+
29 ", und Adresse des des Verwaltungs-Servers, mit der "+
30 "folgenden Syntax an."+
31 "DataInputClient host port ");
32
33 System.exit(0);
34 }
35
36 String host = args[0];
37 String port = args[1];
38
39 Properties env = new Properties();
40 env.put("java.naming.factory.initial",
41 "com.sun.jndi.cosnaming.CNCtxFactory");
Testen der Hochverfügbarkeitsanwendung 185

42 env.put("java.naming.provider.url","iiop://" + host
43 + ":" + port);
44
45 InitialContext ctx = new InitialContext(env);
46 ServerAdminHome home=null;
47 Object obj = ctx.lookup("ServerAdmin");
48 home = (ServerAdminHome) PortableRemoteObject.
49 narrow(obj, ServerAdminHome.class);
50 ServerAdmin server = home.create();
51
52 System.out.println("Administrator eintragen ");
53 AdministratorClone admin = new AdministratorClone(1,
54 "Wolke","mail@karsten-Wolke.de");
55 server.addAdministrator(admin);
56
57 System.out.println(
58 "Applikations-Server Status eintragen");
59 AppServerStatusClone appStatus = new
60 AppServerStatusClone(1,"ENABLED");
61 server.addAppServerStatus(appStatus);
62 appStatus = new AppServerStatusClone(2,"DISABLED" );
63 server.addAppServerStatus(appStatus);
64
65 System.out.println("Benchmark eintragen");
66 BenchmarkClone bench = new BenchmarkClone(1,
67 "s117r4p2",1098,"benchObj");
68 server.addBenchmark(bench);
69 bench = new BenchmarkClone(2,"s117r1p2",1098,"benchObj");
70 server.addBenchmark(bench);
71
72 System.out.println("Applikations-Server eintragen");
73 ApplicationServerClone appServer = new
74 ApplicationServerClone("App-s117r4p2","s117r4p2",1,1,1);
75 server.addApplicationServer(appServer);
76 appServer = new ApplicationServerClone(
77 "App-s117r4p4","s117r4p4",1,1,0);
78 server.addApplicationServer(appServer);
79 appServer = new ApplicationServerClone(
80 "App-s117r4p5","s117r4p5",1,1,0);
81 server.addApplicationServer(appServer);
82 appServer = new ApplicationServerClone(
83 "App-s117r1p2","s117r1p2",1,1,2);
84 server.addApplicationServer(appServer);
85
86 System.out.println("Observer eintragen");
Testen der Hochverfügbarkeitsanwendung 186

87 ObserverClone observer = new ObserverClone(1,"s117r4p2",


88 1099,"ObserverObj",5000,10000,12000,600,600,600,true);
89 server.addObserver(observer);
90 observer = new ObserverClone(2,"s117r4p7",
91 1099,"ObserverObj",5000,10000,12000,600,600,600,true);
92 server.addObserver(observer);
93
94 System.out.println("Service-Status eintragen");
95 ServiceStatusClone servicestatus =
96 new ServiceStatusClone(1,"ENABLED");
97 server.addServiceStatus(servicestatus);
98 servicestatus = new ServiceStatusClone(2,"DISABLED" );
99 server.addServiceStatus(servicestatus);
100 servicestatus = new ServiceStatusClone(3,"OVERLOAD" );
101 server.addServiceStatus(servicestatus);
102
103 System.out.println("Service-Type eintragen");
104 ServiceTypeClone serviceType =
105 new ServiceTypeClone(1,"RMI");
106 server.addServiceType(serviceType);
107 serviceType = new ServiceTypeClone(2,"EJB_SUN");
108 server.addServiceType(serviceType);
109 serviceType = new ServiceTypeClone(3,"EJB_JBOSS" );
110 server.addServiceType(serviceType);
111
112 System.out.println("Service eintragen");
113 ServiceClone service = new ServiceClone("ReadFile",
114 "asynchroner Dateileser");
115 server.addService(service);
116 service = new ServiceClone("Benchmark","RMI Benchmark "+
117 "Komponente zum überwachen der Applikations-Server");
118 server.addService(service);
119 service = new ServiceClone("AddressDB",
120 "EJB Adressdatenbank" );
121 server.addService(service);
122
123 System.out.println("Applikations-Server Service "+
124 "Assoziation eintragen");
125 R_AppServerServiceClone r = new
126 R_AppServerServiceClone(1,20000000,"s117r4p2",
127 1098,"benchObj","App-s117r4p2","Benchmark",1,1);
128 server.addR_AppServerService(r);
129 r = new R_AppServerServiceClone(2,20000000,"s117r4p4",
130 1098,"benchObj","App-s117r4p4","Benchmark",1,1);
131 server.addR_AppServerService(r);
Testen der Hochverfügbarkeitsanwendung 187

132 r = new R_AppServerServiceClone(3,20000000,"s117r1p2",


133 1098,"benchObj","App-s117r1p2","Benchmark",1,1);
134 server.addR_AppServerService(r);
135 r = new R_AppServerServiceClone(4,15000000,"s117r4p2",
136 1050,"ejb/AddressCoverHome","App-s117r4p2",
137 "AddressDB",1,2);
138 server.addR_AppServerService(r);
139 r = new R_AppServerServiceClone(5,15000000,"s117r4p5",
140 1050,"ejb/AddressCoverHome","App-s117r4p5",
141 "AddressDB",1,2);
142 server.addR_AppServerService(r);
143 r = new R_AppServerServiceClone(6,15000000,"s117r1p2",
144 1099,"AddressCoverBean","App-s117r1p2","AddressDB",1,3);
145 server.addR_AppServerService(r);
146 r = new R_AppServerServiceClone(7,10000000,"s117r4p2",
147 1050,"ejb/ReadFileHome","App-s117r4p2","ReadFile",1,2);
148 server.addR_AppServerService(r);
149 r = new R_AppServerServiceClone(8,10000000,"s117r4p5",
150 1050,"ejb/ReadFileHome","App-s117r4p5","ReadFile",1,2);
151 server.addR_AppServerService(r);
152
153 System.out.println("Service-Observer Assoziation "+
154 "eintragen");
155 R_AppServerServiceObserverClone r2 = new
156 R_AppServerServiceObserverClone(1,1,1);
157 server.registerAppServerServiceToObserver(r2);
158 r2 = new R_AppServerServiceObserverClone(2,2,1);
159 server.registerAppServerServiceToObserver(r2);
160 r2 = new R_AppServerServiceObserverClone(3,3,1);
161 server.registerAppServerServiceToObserver(r2);
162 r2 = new R_AppServerServiceObserverClone(4,4,1);
163 server.registerAppServerServiceToObserver(r2);
164 r2 = new R_AppServerServiceObserverClone(5,5,1);
165 server.registerAppServerServiceToObserver(r2);
166 r2 = new R_AppServerServiceObserverClone(6,6,1);
167 server.registerAppServerServiceToObserver(r2);
168 r2 = new R_AppServerServiceObserverClone(7,7,1);
169 server.registerAppServerServiceToObserver(r2);
170 r2 = new R_AppServerServiceObserverClone(8,8,1);
171 server.registerAppServerServiceToObserver(r2);
172 r2 = new R_AppServerServiceObserverClone(9,1,2);
173 server.registerAppServerServiceToObserver(r2);
174 r2 = new R_AppServerServiceObserverClone(10,2,2);
175 server.registerAppServerServiceToObserver(r2);
176 r2 = new R_AppServerServiceObserverClone(11,3,2);
Testen der Hochverfügbarkeitsanwendung 188

177 server.registerAppServerServiceToObserver(r2);
178 r2 = new R_AppServerServiceObserverClone(12,4,2);
179 server.registerAppServerServiceToObserver(r2);
180 r2 = new R_AppServerServiceObserverClone(13,5,2);
181 server.registerAppServerServiceToObserver(r2);
182 r2 = new R_AppServerServiceObserverClone(14,6,2);
183 server.registerAppServerServiceToObserver(r2);
184 r2 = new R_AppServerServiceObserverClone(15,7,2);
185 server.registerAppServerServiceToObserver(r2);
186 r2 = new R_AppServerServiceObserverClone(16,8,2);
187 server.registerAppServerServiceToObserver(r2);
188
189 System.out.println("Benchmark-Observer Assoziation"+
190 " eintragen");
191 R_BenchmarkObserverClone r3 = new
192 R_BenchmarkObserverClone(1,1,1);
193 server.registerBenchmarkToObserver(r3);
194 r3 = new R_BenchmarkObserverClone(2,2,1);
195 server.registerBenchmarkToObserver(r3);
196 r3 = new R_BenchmarkObserverClone(3,1,2);
197 server.registerBenchmarkToObserver(r3);
198 r3 = new R_BenchmarkObserverClone(4,2,2);
199 server.registerBenchmarkToObserver(r3);
200 System.out.println("Observer starten");
201 server.resetobserver(1);
202 server.resetobserver(2);
203 }
204 }

Das folgende Kommando führt die Klasse DataInput aus. Die Daten wer-
den auf dem Verwaltungs-Server abgelegt und die Beobachter mit den Aufga-
ben versorgt. Damit die Klasse DataInput eine Verbindung zur Session Bean
ServerAdmin aufbauen kann, muss sich das jar-Archiv aus Abschnitt 11.2.2 im
CLASSPATH befinden. Bevor man diesen Vorgang startet, sollten die Beobach-
ter und der Verwaltungs-Server gestartet sein. Die entsprechenden Befehle sind
den aus den Abschnitten des Kapitels 11 zu entnehmen. Die folgenden Befehle
gehen davon aus, dass sich der Verwaltungs-Server auf Rechner S117R4P3 be-
findet. Die zu überwachenden Dienste und Applikations-Server können auch
später heraufgeladen beziehungsweise gestartet werden, um die Funktionsweise
der Hochverfügbarkeitsanwendung zu testen. Der Klasse DataInput wird über
Testen der Hochverfügbarkeitsanwendung 189

zwei Parameter mitgeteilt, auf welchem Rechner und auf welchem Port sich
der Verwaltungs-Server befindet.

Ausführen der Klasse DataInput auf Windows-Betriebsystemen


java -classpath %J2EE_HOME%\lib\j2ee.jar;
failsafeClient.jar;. DataInput S117R4P3 1050

12.3. Starten des Test-Clients

Nachdem die Daten in die Datenbank eingetragen sind und die Beobachter
die Dienste und Applikations-Server überwachen, wird der Test-Client gest-
artet. Als Parameter wird dem Test-Client der Rechnername und der Port
des Verwaltungs-Servers übergeben. Als dritten Parameter wird der Name des
Dienstes übergeben. Der Test-Client wird je nach Verfügbarkeit der Dienste,
die Daten der entsprechenden Applikations-Server auf der Konsole ausgeben.
Ist die Adressdatenbank beispielsweise nur auf den Rechnern S117R4P2 und
S117R1P2 verfügbar, wird das folgende Kommando die Ausgabe nach Abbil-
dung 56 liefern.

Ausführen der Klasse TestClient auf Windows-Betriebsystemen


java -classpath %J2EE_HOME%\lib\j2ee.jar;
failsafeClient.jar;. TestClient S117R4P3 1050 AddressDB

Anfrage auf Dienst :AddressDB


--------------------------------
Applikations-Server :App-s117r1p2
Host :s117r1p2
Port :1099
Lookup :AddressCoverBean
TYPE :EJB-JBOSS
--------------------------------
Applikations-Server :App-s117r4p2
Host :s117r4p2
Port :1050
Lookup :ejb/AddressCoverHome
TYPE :EJB-SUN

Abbildung 56: Ausgabe des TestClients


Ausblick 190

13. Ausblick

Wie aus dem Beispiel des vorherigen Kapitel zu entnehmen ist, gewähr-
leistet die erstellte Anwendung die Hochverfügbarkeit von Enterprise Ja-
va Beans auf Applikations-Servern und RMI-Diensten. Die Hochverfügbar-
keitsanwendung sollte aber noch erweitert werden, um professionell einge-
setzt werden zu können. Mittlerweile versuchen einige Hersteller von J2EE
Applikations-Servern, ähnliche Anwendungen zu entwerfen, die speziell auf ihre
Applikations-Server zugeschnitten sind. Die hier entworfene Anwendung bietet
die Option beliebige Applikations-Server zu überwachen und die Last auf sie
zu verteilen. Zudem kann sie durch festgelegte Schnittstellen beliebig erweitert
und angepasst werden. Die nachfolgenden Punkte bieten Erweiterungen dieser
Anwendung.

• Allgemeine Komponente Client Daemon


Die Aufgabe dieser Komponente wird in Kapitel 11 beschrieben.

• Benachrichtigung des Administrators


Es ist wünschenswert, dass bei einem Ausfall eines Applikations-Servers
beziehungsweise Dienstes automatisch den Administrator via E-Mail
zu benachrichtigen. Um dies zu erreichen, erweitert man die EJB
ServerAdminObserver um eine E-Mailversendung. Die notwendigen Da-
ten befinden sich bereits in der Datenbank.

• Optimierte Benchmark-Komponente
Die Benchmark-Komponente muss erweitert werden, um nicht nur eine
Aussage über die Flops (Floating Point Operations per Second), sondern
auch über die Speicherauslastung und weitere wichtige Dinge des Systems
zu erhalten. Dies kann durch Austausch der Klasse Benchmark oder durch
Bildung von Unterklassen ereicht werden.

• Verwaltungs-Client
Ausblick 191

Die Aufgabe und Entwicklung dieser Komponente wird in Kapitel 11


beschrieben.

• Selbstüberwachung
Die Beobachter überwachen EJBs und RMI Objekte. Somit kann die
Anwendung sich auch selbst überwachen. Die Beobachter können die
Verwaltungs-Server und andere Beobachter überwachen. So können die
Verwaltungs-Server dafür sorgen, dass zu jeden Zeitpunkt mindestens
ein Beobachter die notwendigen Komponenten überwacht. Durch das
Benachrichtigen der Administratoren ruft das System selbst um Hil-
fe, falls einzelne Komponenten der Hochverfügbarkeitsanwendung nicht
verfügbar sind.

Um dies zu realisieren, ist weitere Logik in die Methoden notify(...)


der Session Bean ServerAdminObserver zu implementieren.
Schlußbetrachtung 192

14. Schlußbetrachtung

Die Arbeit zeigt, wie mit Hilfe von Enterprise JavaBeans oder der Remote Me-
thod Invocation (RMI) verteilte Anwendungen implementiert werden. Mit der
Java 2 Plattform Enterprise Edition (J2EE) ist es relativ einfach, dreischich-
tige Anwendungen zu entwickeln. Jede Schicht kann ausgetauscht werden und
macht somit die Anwendung flexibel und skalierbar. Enterprise JavaBeans un-
terliegen Einschränkungen, da sie auf Applikations-Servern ausgeführt werden.
Mit Hilfe von RMI kann man diese Einschränkungen umgehen, somit erweitert
man den Funktionsumfang von Enterprise JavaBeans.

Enterprise JavaBeans stellen oft Dienste dar, die mehrere andere Anwendun-
gen nutzen möchten. Dadurch ist die Nachfrage nach Hochverfügbarkeit der
Dienste relativ hoch. Die hier vorgestellte Anwendung bietet eine Variante, die
Hochverfügbarkeit von Enterprise JavaBeans und RMI Diensten zu gewährleis-
ten. Zudem unterstützt die Anwendung Lastverteilung, um Clients eine hohe
Performance zu geben. Damit diese Anwendung selbst hochverfübar ist, be-
steht sie aus mehreren eigenständigen Komponenten, die mehrfach eingesetzt
werden.
Abbildungsverzeichnis 193

Abbildungsverzeichnis
1. Kommunikation zwischen Client und J2EE-Server . . . . . . . . 23
2. Nachrichtenübermittlung via JMS . . . . . . . . . . . . . . . . . 39
3. UML Diagramm der Komponenten von Address . . . . . . . . . 48
4. UML-Diagramm der Komponenten von AddressCover . . . . . . 49
5. Einstellungen der Bean AddressCover . . . . . . . . . . . . . . . 68
6. Allgemeine Einstellungen der Bean Address . . . . . . . . . . . 69
7. Spezielle Einstellungen der Bean Address . . . . . . . . . . . . . 70
8. EJB Referenzen der Bean AddressCover . . . . . . . . . . . . . 72
9. Kommunikation via RMI-Registry . . . . . . . . . . . . . . . . . 77
10. Kommunikation zwischen Client und Server via RMI . . . . . . 79
11. UML Diagramm für das RMI-Beispiel . . . . . . . . . . . . . . . 81
12. Kommunikation zwischen Client und Server . . . . . . . . . . . 89
13. Ausgabe auf dem Client . . . . . . . . . . . . . . . . . . . . . . 93
14. Ausgabe auf dem Server . . . . . . . . . . . . . . . . . . . . . . 93
15. Kommunikation zwischen Client und Server via RMI IIOP . . . 95
16. UML Diagramm des RMI entfernten Objektes . . . . . . . . . . 99
17. UML Diagramm der Enterprise JavaBean . . . . . . . . . . . . 100
18. Fehlermeldung bei ReadFile.readFile() . . . . . . . . . . . . . . 107
19. Ausgabe des Clients . . . . . . . . . . . . . . . . . . . . . . . . 115
20. Kommunikationsablauf des Beispiels . . . . . . . . . . . . . . . 116
21. Hochverfügbarkeit via JNDI . . . . . . . . . . . . . . . . . . . . 118
22. Hochverfügbarkeit via Container . . . . . . . . . . . . . . . . . . 119
23. Hochverfügbarkeit via Home Stub . . . . . . . . . . . . . . . . . 120
24. Hochverfügbarkeit via Remote Stub . . . . . . . . . . . . . . . . 121
25. Hochverfügbarkeit via intellegentem Client . . . . . . . . . . . . 122
26. Framework des Intelligenten Clients . . . . . . . . . . . . . . . 125
27. Komponenten des Beobachters (Observers) . . . . . . . . . . . . 128
28. Komponenten des Beobachters (Observers) 2 . . . . . . . . . . . 129
Abbildungsverzeichnis 194

29. Komponenten des Beobachters (Observers) 3 . . . . . . . . . . . 130


30. Initialisierung des Beobachters (Observers) . . . . . . . . . . . 134
31. Aufbau des Threads checkSpeedTimer . . . . . . . . . . . . . . 136
32. Aufbau des Threads reconnectBenchTimer . . . . . . . . . . . . 138
33. Aufbau des Threads lookupServiceTimer . . . . . . . . . . . . . 140
34. Ausgabe nach dem Start des BenchServers . . . . . . . . . . . . 145
35. Ausgabe nach dem Start des Beobachters . . . . . . . . . . . . . 146
36. Entity Relationship Model des Verwaltungs-Servers . . . . . . . 148
37. Aufbau der Komponente ServerAdmin . . . . . . . . . . . . . . 162
38. UML-Diagramm der EJB ServerAdmin . . . . . . . . . . . . . . 166
39. UML-Diagramm der EJB ServerAdminClient . . . . . . . . . . . 166
40. UML-Diagramm der EJB ServerAdminObserver . . . . . . . . . 166
41. UML-Diagramm der Schnittstelle ServerAdmin . . . . . . . . . 167
42. UML-Diagramm der Klasse ServerAdminBean . . . . . . . . . . 168
43. UML-Diagramm der EJB Administrator . . . . . . . . . . . . . 169
44. UML-Diagramm der EJB AppServerStatus . . . . . . . . . . . . 169
45. UML-Diagramm der EJB Benchmark . . . . . . . . . . . . . . . 170
46. UML-Diagramm der EJB ApplicationServer . . . . . . . . . . . 171
47. UML-Diagramm der EJB R BenchmarkObserver . . . . . . . . 172
48. UML-Diagramm der EJB R AppServerServiceObserver . . . . . 172
49. UML-Diagramm der EJB R AppServerService . . . . . . . . . . 173
50. UML-Diagramm der EJB Observer . . . . . . . . . . . . . . . . 174
51. UML-Diagramm der EJB Service . . . . . . . . . . . . . . . . . 175
52. UML-Diagramm der EJB ServiceStatus . . . . . . . . . . . . . . 175
53. UML-Diagramm der EJB ServiceType . . . . . . . . . . . . . . 176
54. UML-Diagramm des Paketes serverAdmin.entity.clone . . . . . . 177
55. UML-Diagramm des Paketes serverAdmin.entity.clone 2 . . . . 178
56. Ausgabe des TestClients . . . . . . . . . . . . . . . . . . . . . . 189
Literatur 195

Tabellenverzeichnis
1. Namensvergabe für Enterprise JavaBeans . . . . . . . . . . . . . 28
2. Geforderte Methoden einer Session Bean . . . . . . . . . . . . . 32
3. Geforderte Methoden einer Entity Bean . . . . . . . . . . . . . 38
4. Geforderte Methoden einer Message-Driven Bean . . . . . . . . 43
5. EJB-QL Befehle für die Bean Address . . . . . . . . . . . . . . 71
6. Einschränkungen von Enterprise JavaBeans . . . . . . . . . . . 97
7. Verteilung der Beipielanwendung . . . . . . . . . . . . . . . . . 114
8. Das vollständiges Data Dictionary des Verwaltungs-Servers . . 152
9. SQL-Befehle zum Erzeugen der Datenbank Verwaltungs-Server . 159
10. Verteilung der Beispielkonfiguration . . . . . . . . . . . . . . . 183

Literatur

[1] Object Management Group (OMG). CORBA Documentation and Speci-


fication. http://www.corba.org, 2000. 2.4.8

[2] Sun Microsystems. Java API Specification, J2EE Documentation.


http://java.sun.com/j2ee/docs.html, 2002. 2.4.8

[3] Sun Microsystems. Java Naming and Directory InterfaceTM 1.2.1 Spe-
cification. http://java.sun.com/products/jndi/1.2/javadoc/, 2002. 3,
7.1.2

[4] Sun Microsystems. Java API Specification, Interface EJBCon-


text. http://java.sun.com/j2ee/sdk 1.3/techdocs/api/javax/ejb/% -
EntityContext.html, 2002. 3

[5] Sun Microsystems. XML Schemas for J2EETM Deployment Descriptors.


http://java.sun.com/xml/ns/j2ee/, 2002. 3
Literatur 196

[6] Sun Microsystems. Enterprise JavaBeansQuery Language. http://java.-


sun.com/j2ee/tutorial/1 3-fcs/doc/EJBQL.html, 2002. 3.1.2, 5.2

[7] Tyler Jewell Ed Roman, Scott Ambler. Mastering Enterprise JavaBeans,


Second Edition. John Wiley & Sons, Inc, 605 Third Avenue, New York,
NY 10158-0012, 2002. 3.1.3

[8] Richard Monson-Haefel. Enterprise JavaBeans, 2nd edition. O’Reilly &


Associates, Inc, 90 Sherman Street, Cambridge, MA 02140, 2000. 3.1.3

[9] Sun Microsystems. Enterprise JavaBeans Technology. http://java.-


sun.com/products/ejb/, 2001. 3.1.3

[10] Sun Microsystems. The Source for JavaTM Technology. http://java.-


sun.com, 2003. 4.1

[11] Flashline. Application Server Comparison Matrix. http://www.-


flashline.com/components/appservermatrix.jsp, 2003. 4.1

[12] Christian Abt. Enterprise JavaBeans - Einführung und Dokumentation


anhand eines Beispiels (Volltextsuchmaschine mit Datenbankanbindung).
Internes Papier im FB Technik, 2002. 4.2, 5.3

[13] Karsten Wolke. Dokumentation zu Diplomarbeit. http://www.Karsten-


Wolke.de/Diplomarbeit/doc/Diplomarbeit.pdf, April 2003. 3

[14] Sun Microsystems. Java 2 SDK, Enterprise Edition 1.3.1 Confi-


guration Guide. http://java.sun.com/j2ee/sdk 1.3/techdocs/release/-
ConfigGuide.html, 2002. 5.3

[15] Sun Microsystems. JAR Guide. http://java.sun.com/docs/books/-


tutorial/jar/, 1997. 5.4, 7.2

[16] Sun Microsystems. Java RMI Specification. http://java.sun.com/-


products/jdk/1.2/docs/guide/rmi/spec/rmiTOC.doc.html, 1998. 6, 6.1,
5
Literatur 197

[17] Sun Microsystems. Java API Specification, RemoteException. http://-


java.sun.com/j2se/1.4.1/docs/api/java/rmi/RemoteException.html,
2002. 6.1

[18] Sun Microsystems by jGuru. Fundamentals of RMI. http://developer.-


java.sun.com/developer/onlineTraining/rmi/, February 2000. 6.1

[19] Sun Microsystems. Java API Specification, Serializable. http://-


java.sun.com/j2se/1.4.1/docs/api/java/io/Serializable.html, 2002. 7.1.1

[20] Sun Microsystems. Java API Specification, Class SecurityMa-


nager. http://java.sun.com/j2se/1.4.1/docs/api/java/lang/-
SecurityManager.html, 2002. 5

[21] Sun Microsystems. Java API Specification, UnicastRemoteOb-


ject. http://java.sun.com/j2se/1.4.1/docs/api/java/rmi/server/-
UnicastRemoteObject.html, 2002. 7.1.2

[22] Sun Microsystems. Java API Specification, Naming. http://java.sun.-


com/j2se/1.4.1/docs/api/java/rmi/Naming.html, 2002. 7.1.2

[23] Sun Microsystems. Java RMI Specification, Dynamic Class Loa-


ding. http://java.sun.com/products/jdk/1.2/docs/guide/rmi/spec/-
rmi-arch.doc4.html, 1998. 7.3

[24] Sun Microsystems. Java API Specification, PortableRemoteOb-


ject. http://-java.sun.com/j2se/1.4.1/docs/api/javax/rmi/Portable-
RemoteObject.html, 2002. 8

[25] Sun Microsystems. Java RMI over IIOP. http://java.sun.com/-


products/rmi-iiop/, 1999. 8

[26] Sun Microsystems. Java API Specification, Enterprise JavaBeans Restric-


tion. http://java.sun.com/products/ejb/docs.html, 2002. 8
Literatur 198

[27] Sun Microsystems. JDBC Data Access API. http://java.sun.com/-


products/jdbc/, 2003. 8.1

[28] Sun Microsystems. JDBC Short Course. http://developer.java.sun.-


com/developer/onlineTraining/Database/JDBCShortCourse/, 1996. 8.1

[29] Sun Microsystems. JDBCTM 2.0 Fundamentals. http://developer.-


java.sun.com/developer/onlineTraining/Database/JDBC20Intro/, 2000.
8.1

[30] Sun Microsystems. JavaTM 2 Platform, Standard Edition, v 1.4.1, API


Specification. http://java.sun.com/j2se/1.4.1/docs/api/, 2003. 9.2, 9.2

[31] Karsten Wolke. Quellcode und Archive des RMI und EJB Beispiels (asyn-
chroner Dateileser). http://www.Karsten-Wolke.de/Diplomarbeit/-
rar/RMI-EJB.rar, April 2003. 9.3, 9.4

[32] Erich Gamme Richard Helm Ralph Johnson John Vlissides. Entwurfs-
muster, Elemente wiederverwendbarer objektorientierter Software. Addi-
son-Wesley, Reading, Massachusetts, 2001. 11, 11.2

[33] Niels Peter de Witt Karsten Wolke, Helge Jancike. Listener Concept
and Java RMIl. http://www.Karsten-Wolke.de/Diplomarbeit/doc/-
Paper.pdf, November 2002. 11, 11.2

[34] Karsten Wolke. Quellcode und Archive der Hochverfügbarkeitsanwendung.


http://www.Karsten-Wolke.de/Diplomarbeit/rar/failsafe.rar, April 2003.
11.1.1, 11.1.2, 11.2.1, 11.2.2, 11.3, 12.1, B, C

[35] IBM. SQL Erste Schritte. http://www.rrz.uni-hamburg.de/RRZ/-


Software/DB2/db2y0g70.pdf, 2000. 11.2
GLOSSAR 199

Glossar

Applet Ein Applet ist eine in Java programmierte Anwendung, die auf einer
Webseite eingebettet werden kann. Java Applets sind im wesentli-
chen mit Microsofts ActiveX Objekten zu vergleichen - können aber
auf allen Rechnern eingesetzt werden auf denen eine Java Laufzeit-
umgebung vorhanden ist.

classpath Eine Umgebungsvariable, die der virtuellen Java-Maschine und an-


deren Java-Anwendungen mitteilt, wo sich die Klassenbibliotheken
einschließlich der benutzerdefinierten Klassenbibliotheken befinden.

CORBA Abkürzung für Common Object Request Broker Architecture (ge-


meinsame Architektur für Objektanforderungsvermittler)
Eine 1992 von der OMG (Object Management Group) entwickelte
Spezifikation, bei der Teile von Programmen (Objekte) mit anderen
Objekten anderer Programme kommunizieren. Die Kommunikati-
on ist auch dann möglich, wenn zwei Programme in verschiedenen
Programmiersprachen geschrieben wurden oder auf unterschiedli-
chen Plattformen laufen. Bei CORBA fordert ein Programm Ob-
jekte mit Hilfe eines ORB (Object Request Broker) an. Kenntnisse
hinsichtlich der Strukturen des Programms, aus dem das Objekt
stammt, sind dabei nicht erforderlich. CORBA wurde für den Ein-
satz in objektorientierten Umgebungen entwickelt.
GLOSSAR 200

Deployment Descriptor Datei im XML-Format, die ein oder mehrere EJBs


und das Archiv beschreibt, zu dem die EJBs zusammengefasst sind.
Hier werden alle Daten abgelegt, die nicht im Bean-Code enthalten
sind. Das sind deklarative Informationen, die beim Zusammenfas-
sen von EJBs zu Applikationen und beim Installieren der EJBs
auf einen Applikations-Server notwendig sind. Im Deployment De-
scriptor stehen Informationen zum Typ der EJB, zur Struktur der
EJB und über die Abhängigkeit der EJB zu anderen EJBs oder
Ressourcen, wie z.B. Datenbankverbindungen.

Garbage Collector Automatisches Erfassen und Freigeben von nicht mehr


benutztem Speicherplatz. Das Java-Laufzeitsystem führt die Gar-
bage Collection durch, so dass Programmierer Objekte nicht aus-
drücklich freigeben.

JDBC Abkürzung für Java DataBase Connectivity


Eine Java-Anwendungsschnittstelle die es Java-Programmen
ermöglicht, SQL-Anweisungen auszuführen und somit mit allen
SQL-kompatiblen Datenbanken zu kommunizieren. Das JDBC-
Funktionsprinzip lehnt sich an ODBC (open database connectivi-
ty) an, ist im Gegensatz dazu aber speziell auf Java-Programme
zugeschnitten.

JNDI Abkürzung für Java Naming and Directory Interface


GLOSSAR 201

Eine Sammlung von Programmierschnittstellen, die die Verbindung


zu verschiedenen Naming and Directory Diensten erleichtert.

JSP Abkürzung für Java Server Pages.


Eine von Sun Microsystems entwickelte Technologie, die bei der
Entwicklung von plattformunabhängigen, webbasierten Anwendun-
gen verwendet wird. JSP unterstützt Websiteentwickler dabei,
plattformübergreifende Programme herzustellen, indem sie eine
Mischung aus HTML- und XML-Tags und so genannten Java-
Scriptlets verwenden. JSP-Scriptlets werden auf dem Server und
nicht durch den Webbrowser ausgeführt und erstellen so dynami-
schen Webinhalt. Die Integration von Inhalten aus einer Anzahl von
Datenquellen, wie z. B. aus Datenbanken, Dateien und Enterprise
JavaBeans, wird so ebenfalls ermöglicht.

LDAP Abkürzung für Lightweight Directory Access Protocol


Dabei handelt es sich um ein Protokoll, mit dem Verzeichnisinfor-
mationen von einem Verzeichnisdienst abgefragt werden können.

Normalform In einer relationalen Datenbank eine Methode zur Strukturie-


rung von Informationen. Normalformen vermeiden Redundanz und
Inkonsistenz und fördern die effiziente Verwaltung, Speicherung und
Aktualisierung von Informationen. Dabei werden verschiedene Ebe-
nen der Normalisierung gemeinhin anerkannt. Jede stellt eine Ver-
feinerung der vorangegangenen dar. Von diesen Formen werden in
der Praxis meist drei verwendet: die erste Normalform (1NF), die
zweite Normalform (2NF) und die dritte Normalform (3NF).
GLOSSAR 202

Servlet Auch als Servelet oder Serverlet bezeichnet


Ein kleines Java-Programm, das auf einem Server läuft. Der Aus-
druck ist das Gegenstück zu einem Applet, einem Java-Programm,
das in der Regel auf einem Client ausgeführt wird. Servlets führen
kleinere Webdienste durch, die früher üblicherweise durch CGI-
Anwendungen realisiert wurden. Da Servlets automatisches Threa-
ding unterstützen und schnell auf Anfragen reagieren, werden sie
mit hoher Geschwindigkeit ausgeführt, wodurch der Systemover-
head des Servers reduziert wird.

SQL Abkürzung für Structured Query Language (strukturierte Abfra-


gesprache)
Eine Datenbanksprache zur Abfrage, Aktualisierung und Verwal-
tung relationaler Datenbanken.

Umgebungsvariable Ein mit Namen versehener Speicherplatz einer Shell.


Vorwiegend verwendet um das Verhalten von Programmen oder
Kommandos dauerhaft zu steuern.

UML Abkürzung für Unified M odeling Language (vereinheitlichte Mo-


dellierungssprache)
Eine Programmiersprache, die von Grady Booch, Ivar Jacobson und
Jim Rumbaugh entwickelt wurde. Sie kann zur Bestimmung, Her-
stellung und Dokumentation von Softwaresystemen genutzt wer-
den. UML wurde inzwischen von der Object Management Group
(OMG) übernommen.
GLOSSAR 203

Wizard (Assistent,Zauberer, Genie)


Ein interaktives Hilfedienstprogramm innerhalb einer Anwendung,
das dem Benutzer schrittweise Anleitungen zur Bewältigung einer
bestimmten Aufgabe vermittelt.

XML Abkürzung für eXtensible Markup Language (erweiterbare Spra-


che zur Auszeichnung)
Eine reduzierte Variante von SGML (Standard Generalized Mar-
kup Language), die als Nachfolgerin von HTML für Internetanwen-
dungen entwickelt wurde. XML ermöglicht es, individuelle Tags zu
erzeugen, mit denen sich eine größere Flexibilität bei der Organisa-
tion und Darstellung von Informationen erreichen lässt als mit dem
älteren HTML-System. Zudem ist XML besonders für den syste-
munabhängigen Datenaustausch geeignet.
Stichwortverzeichnis 204

Stichwortverzeichnis
0-9 Benchmark-Komponente . . . . . . . 127
3-Schichten-Modell . . . . . . . . . . . . . 16 Benutzeroberfläche . . . . . . . . . . . . 179
Beobachter . . . . . . . . . . . . . . . 126, 127
A
Berechnungen. . . . . . . . . . . . . . . . . . .30
abstrakte Methoden . . . . . . . . . . . . 50
Bibliotheken . . . . . . . . . . . . . . . . . . . . 45
acknowledgment . . . . . . . . . . . . . . . . 40
bind . . . . . . . . . . . . . . . . . . . . . . . . 86, 89
Adressdatenbank . . . . . . . . . . . . . . . 47
binding . . . . . . . . . . . . . . . . . . . . . . . . . 76
Anwendung
blockiert . . . . . . . . . . . . . . . . . . . . . . . . 39
verteilte . . . . . . . . . . . . . . . . . . . . 15
BMP . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
Application Logic . . . . . . . . . . . . . . . 17
Brücke. . . . . . . . . . . . . . . . . . . .164, 165
Applikation . . . . . . . . . . . . . 15, 16, 22
Business
serverseitige . . . . . . . . . . . . . . . . 19
Layer . . . . . . . . . . . . . . . . . . . . . . . 17
Applikations-Server 22, 45, 66, 117,
Logic . . . . . . . . . . . . . . . . . . . . . . . 17
127
Methode . . . . . . . . . . . . . . . . . . . . 96
Architektur
Objekte. . . . . . . . . . . . . . . . . . . . .17
J2EE . . . . . . . . . . . . . . . . . . . 15, 18
Archiv C
EAR. . . . . . . . . . . . . . . . . . . . . . . .72 cast . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
JAR . . . . . . . . . . . . . . . . . . . . 74, 90 CLASSPATH . . . . . . . . . . . . . . . 92, 94
asynchrone Kommunikation 20, 39, Client/Server . . . . . . . . . . . . . . . . . . . 76
99 Client/Server Achitektur . . . . . . . . 16
asynchroner Dateileser . . . . . . . . . . 98 CMP . . . . . . . . . . . . . . . . . . . . . . . 33, 34
atomar . . . . . . . . . . . . . . . . . . . . . . . . . 18 commit . . . . . . . . . . . . . . . . . . . . . . . . . 18
automatische Persistenz. . . . . . . . .34 Container . . . . . . . 18, 22, 26, 29, 119
Container-Managed Persistent . . 33,
B
34
Backend . . . . . . . . . . . . . . . . . . . . . . . . 17
conversational state . . . . . . . . . . . . . 17
Baustein . . . . . . . . . . . . . . . . . . . . . . . . 16
CORBA . . . . . . . . . . . . . . . . . . . . 20, 94
Bean-Managed Persistent . . . . . . . 33
CreateException . . . . . . . . . . . . . . 24
Stichwortverzeichnis 205

D ejbHome . . . . . . . . . . . . . . . . . . . . . . . . 36
Datenabfragebefehle . . . . . . . . . . . . 69 ejbLoad . . . . . . . . . . . . . . . . . . . . . . . . 37
Datenbank . . . . . . . . . . . . . . 17, 20, 46 EJBObject . . . . . . . . . . . . . . . . . . . . . . 25
URL . . . . . . . . . . . . . . . . . . . . . . . . 74 ejbPassivate . . . . . . . . . . . . . . 32, 37
Datenschicht . . . . . . . . . . . . . . . . 16, 17 ejbPostCreate . . . . . . . . . . . . . . . . . 37
deploy . . . . . . . . . . . . . . . . . . . . . 27, 114 ejbRemove . . . . . . . . . . . . . . 32, 38, 43
Deployment Descriptor . . 30, 34, 72 ejbStore . . . . . . . . . . . . . . . . . . . . . . . 38
Deploytool . . . . . . . . . . . . . . . . . . 27, 67 Enterprise JavaBeans . . . . . . . . . . . 22
Descriptor . . . . . . . . . . . . . . . . . . . . . . 27 Definition . . . . . . . . . . . . . . . . . . . 15
Dienst . . . . . . . . . . . . . . . . . . . . . . . . . . 22 entfernte Objekt . . . . . . . . . . . . . . . . 99
DNS . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 entferntes Objekt . . . . . . . . . . . 48, 78
Domain Name System . . . . . . . . . . 20 Referenz . . . . . . . . . . . . . . . . . . . . 76
Entity . . . . . . . . . . . . . . . . . . . . . . . . . 147
E
Entity Bean . . . . . . . . . . . . . . . . . 29, 33
EAR Archiv . . . . . . . . . . . . . . . . . . . . 72
EntityBean . . . . . . . . . . . . . . . . . . . . 34
einbetten . . . . . . . . . . . . . . . . . . . . . . . 48
Entwicklungsumgebung . . . . . . . . . 45
Eingabefelder . . . . . . . . . . . . . . . . . . . 16
Entwicklungswerkzeuge . . . . . . . . . 16
Einschränkungen . . . . . . . . . . . . . . . 96
Entwurfsmuster . . . . . . . . . . . 126, 160
EJB . . . . . . . . . . . . . . . . . . . . . . . . 19, 22
export . . . . . . . . . . . . . . . . . . . . . . . . . . 86
-QL . . . . . . . . . . . . . . . . . . . . . 34, 71
Definition . . . . . . . . . . . . . . . . . . . 15 F
Einschränkungen . . . . . . . 96, 106 Facade . . . . . . . . . . . . . . . . . . . . . . . . 160
Klasse . . . . . . . . . . . . . . . . . . . . . . 26 Fassade . . . . . . . . . . . . . . . . . . . . . . . . 160
Namensvergabe . . . . . . . . . . . . . 28 Framework . . . . . . . . . . . . . . 15, 18, 97
Objekt . . . . . . . . . . . . . . . . . . 24, 96 Frontend . . . . . . . . . . . . . . . . . . . . . . . 17
Spezifikation . . . . . . . . . . . . . . . . 96
G
Typen . . . . . . . . . . . . . . . . . . 25, 28
Garbage Collector . . . . . . . . . . . . . . 94
ejbActivate. . . . . . . . . . . . . . . .32, 37
Geschäfts
ejbCreate . . . . . . . . . . . 26, 31, 36, 42
anwendung . . . . . . . . . . . . . . . . . 15
ejbFind . . . . . . . . . . . . . . . . . . . . . . . . 35
logik . . . . . . . . . . . . . . . . . . . . . . . . 17
EJBHome . . . . . . . . . . . . . . . . . . . . . . . . 24
Stichwortverzeichnis 206

Geschwindigkeitsmessung . . . . . . 131 J2SE. . . . . . . . . . . . . . . . . . . . . . . .19, 45


Graphical User Interface . . . 17, 179 JAR Archiv . . . . . . . . . . . . . . . . . 74, 90
GUI . . . . . . . . . . . . . . . . . . . . . . . 17, 179 Java Database Connectivity. . . . .20
Java IDL . . . . . . . . . . . . . . . . . . . . . . . 20
H
Java Message Service. . . . . . . .20, 39
Hörer . . . . . . . . . . . . . . . . . . . . . . . . . . 126
Java Naming and Directory Inter-
Hülle . . . . . . . . . . . . . . . . . . . . . . . . . . 160
face . . . . . . . . . . . . . . . . . . . . . 20
Hardwarefehler . . . . . . . . . . . . . . . . 117
Java Virtual Machine . . . . . . . . . . . 19
heraufladen . . . . . . . . . . . . . . . . 27, 114
JAVA HOME . . . . . . . . . . . . . . . . . . . . . . 46
Hochverfügbarkeit . . . . . . . . 117, 126
JavaBeans . . . . . . . . . . . . . . . . . . . . . . 16
Konzepte . . . . . . . . . . . . . . . . . . 117
javac . . . . . . . . . . . . . . . . . . . . . . . 79, 90
Home
JavaMail . . . . . . . . . . . . . . . . . . . . . . . 20
Interface . . . . . . . . . . . . . . . . . . . . 24
JDBC. . . . . . . . . . . . . . . . . . . . . . .20, 33
Objekt. . . . . . . . . . . . . . . . . . . . . .24
JMS . . . . . . . . . . . . . . . . . . . . . . . . 20, 39
Stub . . . . . . . . . . . . . . . . . . . . . . . 120
JMS-Server . . . . . . . . . . . . . . . . . . . . . 40
I JNDI . . . . . . 20, 23, 87, 97, 118, 144
Infrastruktur JNDI-Name . . . . . . . . . . . . . . . . . . . . 71
serverseitige . . . . . . . . . . . . . . . . 18 JNDI-Server . . . . . . . . . . . . . . . . . . . 118
intelligenter Client . . . . . . . . 122, 124 JSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
Internet Inter-ORB Protocol . . . . 94 JVM . . . . . . . . . . . . . . . . . . . . . . . . 19, 80
Internet-Shop . . . . . . . . . . . . . . . . . . . 30
K
J Kapselung . . . . . . . . . . . . . . . . . . . . . . 33
J2EE . . . . . . . . . . . . . . . . . . . . 19, 45, 96 Kardinalität . . . . . . . . . . . . . . . . . . . 147
Architektur . . . . . . . . . . . . . 15, 18 Klassenvariablen . . . . . . . . . . . . . . . . 30
Einschränkungen. . . . . . . . . . . .96 Kommunikation . . . . . . . . . . . . . . . . 22
Server . . . . . . . . . . . . . . . 15, 24, 45 asynchrone . . . . . . . . . . 20, 39, 99
Technologie . . . . . . . . . . . . . . . . . 19 synchrone . . . . . . . . . . . . . . . . . . . 39
j2ee. . . . . . . . . . . . . . . . . . . . . . . . . . . .74 Komponente . . . . . . . . . . . . . . . . 16, 22
J2EE Home . . . . . . . . . . . . . . . . . . . . . . 46 serverseitige . . . . . . . . . . . . . . . . 19
j2eeadmin . . . . . . . . . . . . . . . . . . . . . . 73 Komponenten-Architektur . . . . . . 15
Stichwortverzeichnis 207

komponentenbasierte Softwareent- Middleware . . . . . . . . . . . . . . . . . . . . . 17


wicklung . . . . . . . . . . . . . . . . 16 Middleware Service . . . . . . . . . . . . . 19
Komponentenmodell . . . . . . . . . . . . 15 Mikroprozessor . . . . . . . . . . . . . . . . . 16
konsistent . . . . . . . . . . . . . . . . . . 33, 159
N
Konstruktor . . . . . . . . . . . . . . . . . . . . 24
Nachricht . . . . . . . . . . . . . . . . . . . . . . . 39
Kontext . . . . . . . . . . . . . . . . . . . . . . . . 26
Name-Server . . . . . . . . . . . . . . . . 23, 76
L Namensdienste. . . . . . . . . . . . . . . . . .20
LDAP . . . . . . . . . . . . . . . . . . . . . . . . . . 20 Namensvergabe EJBs . . . . . . . . . . . 28
Lightweight Directory Access Pro- naming . . . . . . . . . . . . . . . . . . . . . . . . . 76
tocol . . . . . . . . . . . . . . . . . . . . 20 Naming . . . . . . . . . . . . . . . . . . 77, 86, 89
Liste. . . . . . . . . . . . . . . . . . . . . . . . . . . .16 Naming and Directory Service . . 87
Listener . . . . . . . . . . . . . . 126, 142, 165 Netzwerkressourcen . . . . . . . . 47, 126
Local-Interface. . . . . . . . . . . . . . . . . .27 Normalisierung . . . . . . . . . . . . . . . . 147
LocalHome-Interface . . . . . . . . . . . . 27
O
Logikschicht . . . . . . . . . . . . . . . . 16, 17
Objekt
lokales Objekt . . . . . . . . . . . . . . 27, 77
EJB. . . . . . . . . . . . . . . . . . . . .24, 96
lookup . . . . . . . . . . . . . . . . . . . . . . . . . . 76
entfernte . . . . . . . . . . . . . . . . . . . . 99
lookup . . . . . . . . . . . . . . . . . . . . . . . . . 89
entferntes . . . . . . . . . . . . . . . . . . . 48
M Home . . . . . . . . . . . . . . . . . . . . . . . 24
Mehrbenutzer-geeignet . . . . . . . . . . 15 lokales . . . . . . . . . . . . . . . . . . 27, 77
Mehrfachvererbung . . . . . . . . . . . . . 86 remote . . . . . . . 48, 78, 83, 96, 99
Message . . . . . . . . . . . . . . . . . . . . . . . . 39 objektorientierte Datenbank . . . . 34
Message-Driven Bean . . . . . . . 29, 39 objektorientiertes Programmieren
MessageBean . . . . . . . . . . . . . . . . . . . 41 71
MessageListener . . . . . . . . . . . . . . 41 Observer. . . . . . . . . . . . . . . . . .126, 127
Methoden Entity Bean. . . . . . . . . .38 onMessage . . . . . . . . . . . . . . . . . . . . . . 42
Methoden Message-Driven Bean 43
P
Methoden Session Bean . . . . . . . . . 32
Parse by Referenz . . . . . . . . . . . . . . 83
Middle Tier. . . . . . . . . . . . . . . . . . . . .17
Parse by Value . . . . . . . . . . . . . . . . . 83
Stichwortverzeichnis 208

Performance . . . . . . . . . . . . . . . . 28, 45 RemoteException . . . . . . 24, 78, 120


Performancevorteil . . . . . . . . . . . . . . 27 Ressource Manager . . . . . . . . . . . . . 97
persistent . . . . . . . . . . . . . . . . . . . 17, 33 RMI . . . . . . . . . . . . . . . . . . . . 19, 76, 94
persistent state . . . . . . . . . . . . . . . . . 17 Umgebung . . . . . . . . . . . . . . . . . . 77
Persistenz . . . . . . . . . . . . . . . . . . . . . . 33 RMI IIOP . . . . . . . . . . . . . . . 19, 76, 94
automatische . . . . . . . . . . . . . . . 34 RMI-Registry . . 76, 83, 87, 97, 131,
Bean-Managed . . . . . . . . . . . . . . 33 144
Container-Managed . . . . . 33, 34 rmic. . . . . . . . . . . . . . . . . . . . . . . . . . . .79
PortableRemoteObject . . . . . . . . 94 rmic rmic . . . . . . . . . . . . . . . . . . . . . . 90
Präsentationsschicht . . . . . . . . 16, 17 roll back . . . . . . . . . . . . . . . . . . . . . . . . 18
Prüfthread . . . . . . . . . . . . . . . . . . . . 135 runClient . . . . . . . . . . . . . . . . . . . . . . 74
Primärschlüssel . . . . . . . . . . . . . 35, 70
S
Programmiersprache . . . . . . . . 21, 22
Schaltfläche . . . . . . . . . . . . . . . . . . . . . 16
Proxy . . . . . . . . . . . . . . . . . . . . . . . . . . 79
Schnittstelle. . . . . . . . . . . . . . . . .15, 22
Q Security Manager . . . . . . . . . . . . . . . 91
queue . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 Selbstüberwachung . . . . . . . . . . . . 191
Serializable . . . . . . . . . . . . . . . . . . 83
R
Server
rebind. . . . . . . . . . . . . . . . . . . . . .86, 89
Applikation 22, 45, 66, 117, 127
referentielle Integrität. . . . . . . . . .147
J2EE . . . . . . . . . . . . . . . . 15, 24, 45
Relation . . . . . . . . . . . . . . . . . . . . . . . 147
JMS . . . . . . . . . . . . . . . . . . . . . . . . 40
relationale Datenbank . . . . . . . . . . 34
JNDI . . . . . . . . . . . . . . . . . . . . . . 118
relationales Datenmodell . . . . . . . . 17
Serverplattform . . . . . . . . . . . . . . . . . 15
Remote. . . . . . . . . . . . . . . . . . . . . .78, 83
serverseitige
remote Interface . . . . . . . . . 24, 78, 96
Applikation . . . . . . . . . . . . . . . . . 19
Remote Method Invocation . 19, 76
Infrastruktur. . . . . . . . . . . . . . . .18
remote Objekt48, 76, 78, 83, 96, 99
Komponente . . . . . . . . . . . . . . . . 19
Referenz . . . . . . . . . . . . . . . . . . . . 76
Servlet . . . . . . . . . . . . . . . . . . . . . . . . . 22
via Referenz . . . . . . . . . . . . . . . . 76
Session Bean . . . . . . . . . . . . . . . . . . . 29
remote Stub . . . . . . . . . . . . . . . . . . . 121
stateful . . . . . . . . . . . . . . . . . 29, 30
Stichwortverzeichnis 209

stateless. . . . . . . . . . . . . . . . .29, 30 this. . . . . . . . . . . . . . . . . . . . . . . . . . . .96


zustandbehaftete . . . . . . . . 29, 30 Thread . . . . . . . . . . . . . . . . . . . . 96, 135
zustandlose . . . . . . . . . . . . . 29, 30 three tier model . . . . . . . . . . . . . . . . 16
SessionBean . . . . . . . . . . . . . . . . . . . 30 Transaktion . . . . . . . . . . . . 18, 26, 160
setEntityContext . . . . . . . . . . . . . 35 Mechanismen . . . . . . . . . . . . . . . 20
setMessageDrivenContext . . . . . 42
U
setSessionContext . . . . . . . . . . . . 31
Umgebungsvariable . . . . . . . . . . . . . 46
Sicherheitsinformationen . . . . . . . . 26
UnicastRemoteObject . . . . . . . . . . 86
skalierbar . . . . . . . . . . . . . . . . . . . . . . . 15
unsetEntityContext . . . . . . . . . . . 38
Skeleton . . . . . . . . . . . . . . . . . . . . 79, 94
Socket . . . . . . . . . . . . . . . . . . . . . . . . . . 97 V
Softwareentwicklung Vergangenheit . . . . . . . . . . . . . . . . . . 30
komponentenbasierte. . . . . . . .16 Verhalten . . . . . . . . . . . . . . . . . . . . . . . 17
Softwarekomponente . . . . . . . . . . . . 29 verteilte
Softwarepakete . . . . . . . . . . . . . . . . . 45 Anwendung . . . . . . . . . . . . . 15, 77
SQL . . . . . . . . . . . . . . . . . . . . . . . . 33, 34 Systeme . . . . . . . . . . . . . . . . . . . . 18
SQL 92 . . . . . . . . . . . . . . . . . . . . . . . . 147 Verwaltungs-Client . . . . . . . 126, 179
Standard-Methoden. . . . . . . . . . . . .26 Verwaltungs-Server . . . . . . . 126, 146
state. . . . . . . . . . . . . . . . . . . . . . . . . . . .17
W
stateful Session Bean . . . . . . . . 29, 30
Warteschlange . . . . . . . . . . . . . . . . . . 39
stateless Session Bean . . . . . . . 29, 30
wiederverwendbar . . . . . . . . . . . 15, 18
Stub . . . . . . . . . . . . . . . . . . . 79, 94, 160
Wizard . . . . . . . . . . . . . . . . . . 27, 46, 69
Home . . . . . . . . . . . . . . . . . . . . . . 120
remote. . . . . . . . . . . . . . . . . . . . .121 Z
Swapping . . . . . . . . . . . . . . . . . . . . . . . 30 Zustand . . . . . . . . . . . . . . . . . . . . . . . . 17

synchrone Kommunikation . . . . . . 39 zustandbehaftete Session Bean . 29,

Synchronisation . . . . . . . . . . . . . . . 142 30
zustandlose Session Bean . . . . 29, 30
T
Technologie
J2EE . . . . . . . . . . . . . . . . . . . . . . . 19
Eidesstattliche Erklärung 210

Erklärung

Soweit meine Rechte berührt sind, erkläre ich mich einverstanden, dass die
Diplomarbeit Angehörigen der Fachhochschule OOW für Studium, Lehre und
Forschung uneingeschränkt zugänglich gemacht werden kann.

Eidesstattliche Erklärung

Name: Karsten Wolke


Matrikelnr.: 2083967

Hiermit erkläre ich an Eides Statt, dass ich die vorliegende Diplomarbeit bis
auf die offizielle Betreuung selbst und ohne fremde Hilfe angefertigt habe und
die benutzten Quellen und Hilfsmittel vollständig angegeben sind.

Emden, den 7. April 2003 .........................................


Unterschrift
AddressBean als Bean Managed Entity Bean 211

Anhang

A. AddressBean als Bean Managed Entity Bean

Der folgende Quellcode zeigt die AddressBean aus Kapitel 5 als Bean
Managed Entity Bean. Sie ist äquivalent zu der Container Managed
AddressBean aus Abschnitt 5.1.3.

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: AddressBean
9 * Date: 29.01.2003
10 * Version: 1.0
11 */
12 package address_bmp;
13
14 import javax.ejb.*;
15 import java.sql.*;
16 import java.util.*;
17 import javax.naming.*;
18
19
20 /**
21 * Entity Bean zum Demonstrieren von Bean-managed
22 * Persistence
23 *
24 * Dies ist eine persistente Adresse. Sie besitzt einen
25 * Namen, einen Vornamen, eine Straße, eine Postleitzahl und
26 * einen Ort.
27 */
28 public class AddressBean implements EntityBean {
29
AddressBean als Bean Managed Entity Bean 212

30 protected EntityContext ctx;


31
32 /*
33 * Bean managed persistente Felder
34 */
35 private String addressID;
36 private String name;
37 private String firstName;
38 private String street;
39 private String postcode;
40 private String location;
41
42
43 /*
44 * get/set Methoden der persitenten Felder
45 */
46
47 /**
48 * Gibt den Primärschlüssel zurück.
49 * @return AddressID (Primärschlüssel) der Adresse.
50 */
51 public String getID(){
52
53 return addressID;
54 }
55
56
57 /**
58 * Gibt den Namen des Adressbesitzers zurück.
59 * @return Name des Adressbesitzers.
60 */
61 public String getName(){
62
63 return name;
64 }
65
66
67 /**
68 * Gibt den Vornamen des Adressbesitzers zurück.
69 * @return Vorname des Adressbesitzers.
70 */
71 public String getFirstName(){
72
73 return firstName;
74 }
AddressBean als Bean Managed Entity Bean 213

75
76
77 /**
78 * Gibt die Strasse der Adresse zurück.
79 * @return Strasse der Adresse.
80 */
81 public String getStreet(){
82
83 return street;
84 }
85
86
87 /**
88 * Gibt die Postleitzahl der Adresse zurück.
89 * @return Postleitzahl der Adresse.
90 */
91 public String getPostcode(){
92
93 return postcode;
94 }
95
96
97 /**
98 * Gibt die Ort der Adresse zurück.
99 * @return Ort der Adresse.
100 */
101 public String getLocation(){
102
103 return location;
104 }
105
106
107 /**
108 * Setzt den Primärschlüssel der Addresse.
109 * @param addressID Primärschlüssel der Adresse.
110 */
111 public void setID(String addressID){
112
113 this.addressID = addressID;
114 }
115
116
117 /**
118 * Setzt den Namen des Adressbesitzers.
119 * @param name Name des Adressbesitzers.
AddressBean als Bean Managed Entity Bean 214

120 */
121 public void setName(String name){
122
123 this.name = name;
124 }
125
126
127 /**
128 * Setzt den Vornamen des Adressbesitzers.
129 * @param firstName Vorname des Adressbesitzers.
130 */
131 public void setFirstName(String firstName){
132
133 this.firstName = firstName;
134 }
135
136
137 /**
138 * Setzt die Strasse der Adresse.
139 * @param street Strasse der Adresse.
140 */
141 public void setStreet(String street){
142
143 this.street = street;
144 }
145
146
147 /**
148 * Setzt die Postleitzahl der Adresse.
149 * @param postcode Postleitzahl der Adresse.
150 */
151 public void setPostcode(String postcode){
152
153 this.postcode = postcode;
154 }
155
156
157 /**
158 * Setzt den Ort der Adresse.
159 * @param location Ort der Adresse.
160 */
161 public void setLocation(String location){
162
163 this.location = location;
164 }
AddressBean als Bean Managed Entity Bean 215

165
166
167 /*
168 * Hilfsmethoden
169 */
170
171 /**
172 * Hilfsmethode, um eine Datenbankverbindung zu bekommen.
173 *
174 * @return JDBC Datenbankverbindung.
175 */
176 public Connection getConnection() throws Exception{
177
178 try{
179 Context ctx = new InitialContext();
180 javax.sql.DataSource ds = (javax.sql.DataSource)
181 ctx.lookup("java:comp/env/jdbc/addressDB");
182 return ds.getConnection();
183 }catch (Exception e){
184 System.out.println("Konnte Datenbank nicht finden");
185 e.printStackTrace();
186 throw e;
187 }
188 }
189
190
191
192
193 /*
194 * Benötigte Methoden einer Entity Bean.
195 */
196 public void setEntityContext(EntityContext ctx){
197
198 this.ctx = ctx;
199 }
200
201
202 public void unsetEntityContext(){
203
204 this.ctx = null;
205 }
206
207
208 public void ejbRemove(){
209
AddressBean als Bean Managed Entity Bean 216

210 String id = (String) ctx.getPrimaryKey();


211
212 PreparedStatement pstmt = null;
213 Connection conn = null;
214 try{
215 /*
216 * 1) Erwerben einer neuen JDBC Verbindung
217 */
218 conn = getConnection();
219
220 /*
221 * 2) Löschen der Adresse aus der Datenbank
222 */
223 pstmt = conn.prepareStatement(
224 "DELETE FROM AddressBeanTable WHERE iD = ?");
225 pstmt.setString(1,id);
226
227 /*
228 * 3) Eine Exception werfen, falls der Datensatz nicht
229 * gelöscht werden konnte
230 */
231 if (pstmt.executeUpdate()==0){
232 throw new RemoveException("Adresse " + id +
233 " konnte nicht gelöscht werden");
234 }
235 }catch(Exception ex){
236 throw new EJBException(ex.toString());
237 }
238 finally{
239 /*
240 * 4) Freigeben der JDBC Datenbankverbindung
241 */
242 try{ if (pstmt !=null) pstmt.close();}
243 catch(Exception e){}
244 try{ if (conn != null) conn.close();}
245 catch(Exception e){}
246 }
247 }
248
249 public void ejbActivate(){
250
251 }
252
253
254 public void ejbPassivate(){
AddressBean als Bean Managed Entity Bean 217

255
256 }
257
258
259 public void ejbLoad(){
260
261 String id = (String) ctx.getPrimaryKey();
262
263 PreparedStatement pstmt = null;
264 Connection conn = null;
265
266 try{
267 /*
268 * 1) Erwerben einer neuen JDBC Verbindung
269 */
270 conn = getConnection();
271
272 /*
273 * 2) Adresse aus der Datenbank holen
274 */
275 pstmt = conn.prepareStatement("SELECT name,firstName,"+
276 "street,postcode,location FROM AddressBeanTable"+
277 " WHERE iD = ?");
278 pstmt.setString(1,id);
279
280 /*
281 * 3) Auslesen der Attribute aus dem ResultSet
282 */
283 ResultSet rs = pstmt.executeQuery();
284 rs.next();
285 name = rs.getString("name");
286 firstName = rs.getString("firstName");
287 street = rs.getString("street");
288 postcode = rs.getString("postcode");
289 location = rs.getString("location");
290 }catch(Exception ex){
291 throw new EJBException("Adresse "+ id + "konnte nicht"+
292 " geladen werden");
293 }
294 finally{
295 /*
296 * 4) Freigeben der JDBC Datenbankverbindung
297 */
298 try{ if (pstmt !=null) pstmt.close();}
299 catch(Exception e){}
AddressBean als Bean Managed Entity Bean 218

300 try{ if (conn != null) conn.close();}


301 catch(Exception e){}
302 }
303 }
304
305
306 public void ejbStore(){
307
308 String id = (String) ctx.getPrimaryKey();
309
310 PreparedStatement pstmt = null;
311 Connection conn = null;
312
313 try{
314 /*
315 * 1) Erwerben einer neuen JDBC Verbindung
316 */
317 conn = getConnection();
318
319 /*
320 * 2) Adresse in die Datenbank erneut ablegen
321 */
322 pstmt =conn.prepareStatement("UPDATE AddressBeanTable"+
323 "SET id = ?,name = ?, firstName = ?, street = ?, "+
324 "postcode = ?, location = ? WHERE iD = ?");
325 pstmt.setString(1,id);
326 pstmt.setString(2,name);
327 pstmt.setString(3,firstName);
328 pstmt.setString(4,street);
329 pstmt.setString(5,postcode);
330 pstmt.setString(6,location);
331 pstmt.setString(7,id);
332
333 pstmt.executeUpdate();
334 }catch(Exception ex){
335 throw new EJBException("Adresse "+ id + "konnte nicht"+
336 " gespeichert werden");
337 }
338 finally{
339 /*
340 * 3) Freigeben der JDBC Datenbankverbindung
341 */
342 try{ if (pstmt !=null) pstmt.close();}
343 catch(Exception e){}
344 try{ if (conn != null) conn.close();}
AddressBean als Bean Managed Entity Bean 219

345 catch(Exception e){}


346 }
347 }
348
349
350 public String ejbCreate(String addressID, String name,
351 String firstName, String street,
352 String postcode, String location)
353 throws CreateException{
354
355 PreparedStatement pstmt = null;
356 Connection conn = null;
357
358 try{
359 /*
360 * Parameter in den Klassenvariablen speichern
361 */
362 setID(addressID);
363 setName(name);
364 setFirstName(firstName);
365 setStreet(street);
366 setPostcode(postcode);
367 setLocation(location);
368 /*
369 * 1) Erwerben einer neuen JDBC Verbindung
370 */
371 conn = getConnection();
372
373 /*
374 * 2) Adresse in die Datenbank ablegen
375 */
376 pstmt = conn.prepareStatement("INSERT INTO "+
377 "AddressBeanTable (iD, name, firstName, street, "+
378 "postcode, location) values(?, ?, ?, ?, ?, ?)");
379 pstmt.setString(1,addressID);
380 pstmt.setString(2,name);
381 pstmt.setString(3,firstName);
382 pstmt.setString(4,street);
383 pstmt.setString(5,postcode);
384 pstmt.setString(6,location);
385
386 pstmt.executeUpdate();
387 return addressID;
388 }catch(Exception ex){
389 ex.printStackTrace();
AddressBean als Bean Managed Entity Bean 220

390 throw new EJBException("Adresse "+addressID+ "konnte "+


391 " nicht erstellt werden");
392 }
393 finally{
394 /*
395 * 3) Freigeben der JDBC Datenbankverbindung
396 */
397 try{ if (pstmt !=null) pstmt.close();}
398 catch(Exception e){}
399 try{ if (conn != null) conn.close();}
400 catch(Exception e){}
401 }
402 }
403
404
405 public void ejbPostCreate(String addressID, String name,
406 String firstName, String street,
407 String postcode,String location){
408
409 }
410
411 /*
412 * Geschäftsmethoden der Entity Bean
413 */
414
415 /**
416 * Sucht nach einer Adresse, die als ID den übergebenen
417 * Parameter adressID besitzt.
418 * @param adressID ID nach der gesucht werden soll.
419 *
420 * @return Primärschlüssel des zu suchenden EJB Objektes.
421 */
422 public String ejbFindByPrimaryKey(String addressID)
423 throws FinderException{
424
425 PreparedStatement pstmt = null;
426 Connection conn = null;
427
428 try{
429 setID(addressID);
430 /*
431 * 1) Erwerben einer neuen JDBC Verbindung
432 */
433 conn = getConnection();
434
AddressBean als Bean Managed Entity Bean 221

435 /*
436 * 2) Adresse aus der Datenbank holen
437 */
438 pstmt = conn.prepareStatement("SELECT iD FROM "+
439 " AddressBeanTable WHERE iD = ?");
440 pstmt.setString(1,addressID);
441 ResultSet rs = pstmt.executeQuery();
442 rs.next();
443
444 /*
445 * 3) Da kein Fehler aufgetreten ist, existiert der
446 * Datensatz. Diese Methode gibt lediglich den
447 * Primärschlüssel zurück. Der Container erzeugt
448 * automatisch neue EJB-Objekte und ruft die ejbLoad()
449 * Methode auf.
450 */
451
452 return addressID;
453 }catch(Exception ex){
454 throw new FinderException(ex.toString());
455 }
456 finally{
457 /*
458 * 4) Freigeben der JDBC Datenbankverbindung
459 */
460 try{ if (pstmt !=null) pstmt.close();}
461 catch(Exception e){}
462 try{ if (conn != null) conn.close();}
463 catch(Exception e){}
464 }
465 }
466
467
468 /**
469 * Sucht nach Adressen, die als Namen den übergebenen
470 * Parameter name besitzen.
471 * @param name Der Name, nach dem gesucht werden soll.
472 *
473 * @return Eine Liste von Primärschlüsseln.
474 */
475 public Collection ejbFindByName(String name)
476 throws FinderException{
477
478 PreparedStatement pstmt = null;
479 Connection conn = null;
AddressBean als Bean Managed Entity Bean 222

480 Vector v = new Vector();


481
482 try{
483 /*
484 * 1) Erwerben einer neuen JDBC Verbindung
485 */
486 conn = getConnection();
487
488 /*
489 * 2) Adresse aus der Datenbank holen
490 */
491 pstmt = conn.prepareStatement("SELECT iD FROM "+
492 " AddressBeanTable WHERE name = ?");
493 pstmt.setString(1,name);
494 ResultSet rs = pstmt.executeQuery();
495
496 /*
497 * Auslesen der Primärschlüssel aus dem ResultSet und
498 * hinzufügen in den Vector
499 */
500 while(rs.next()){
501 String id = rs.getString("iD");
502 v.addElement(id);
503 }
504 return v;
505 }catch(Exception ex){
506 throw new FinderException(ex.toString());
507 }
508 finally{
509 /*
510 * 4) Freigeben der JDBC Datenbankverbindung
511 */
512 try{ if (pstmt !=null) pstmt.close();}
513 catch(Exception e){}
514 try{ if (conn != null) conn.close();}
515 catch(Exception e){}
516 }
517 }
518
519
520 /**
521 * Liefert alle Datensätze der Adressen-Datenbank zurück.
522 * @return Primärschlüssel aller Datensätze in der
523 * Datenbank.
524 */
AddressBean als Bean Managed Entity Bean 223

525 public Collection ejbFindAllAdresses()


526 throws FinderException{
527
528 PreparedStatement pstmt = null;
529 Connection conn = null;
530 Vector v = new Vector();
531
532 try{
533 /*
534 * 1) Erwerben einer neuen JDBC Verbindung
535 */
536 conn = getConnection();
537
538 /*
539 * 2) Adresse aus der Datenbank holen
540 */
541 pstmt = conn.prepareStatement("SELECT iD FROM "+
542 " AddressBeanTable");
543 ResultSet rs = pstmt.executeQuery();
544
545 /*
546 * Auslesen der Primärschlüssel aus dem ResultSet und
547 * hinzufügen in den Vector
548 */
549 while(rs.next()){
550 String id = rs.getString("iD");
551 v.addElement(id);
552 }
553 return v;
554 }catch(Exception ex){
555 throw new FinderException(ex.toString());
556 }
557 finally{
558 /*
559 * 4) Freigeben der JDBC Datenbankverbindung
560 */
561 try{ if (pstmt !=null) pstmt.close();}
562 catch(Exception e){}
563 try{ if (conn != null) conn.close();}
564 catch(Exception e){}
565 }
566 }
567 }
Quellcode der Beobachter Komponente 224

B. Quellcode der Beobachter Komponente

Die folgenden Quellcodes zeigen die konkrete Umsetzung des Beobachters aus
Kapitel 11.1. Der vollständige Quellcode kann auch unter [34]
heruntergeladen werden. Die einzelnen Methoden, Klassen und Schnittstellen
sind im Quellcode dokumentiert und werden hier nicht erläutert.

B.1. Quellcode der Schnittstelle IBenchMachine

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: IBenchMachine
9 * Date: 10.02.2003
10 * Version: 1.0
11 */
12 package observer;
13
14 import java.util.*;
15 import java.rmi.*;
16
17
18 /**
19 * Das remote Interface für die BenchMachine Klasse. Alle
20 * hier deklarierten Methoden können von einem entfernten
21 * Client aufgerufen werden.
22 */
23 public interface IBenchMachine extends Remote{
24
25 /**
26 * Gibt den Namen des Rechners zurück.
27 * Dies sollte der Rechnername im Netzwerk sein.
28 */
29 public String getHostName() throws RemoteException;
30
31
32 /**
Quellcode der Beobachter Komponente 225

33 * Gibt den größten Wert der Messung zurück. Dieser Wert


34 * repräsentiert die maximale Geschwindigkeit (in flops)
35 * des Rechners.
36 * @return Die maximale Geschwindigkeit des Rechners.
37 */
38 public double getMaxSpeed() throws RemoteException;
39
40
41 /**
42 * Gibt den kleinsten Wert der Messung zurück. Dieser Wert
43 * repräsentiert die niedrigste Geschwindigkeit (in flops)
44 * des Rechners.
45 * @return Die minimale Geschwindigkeit des Rechners.
46 */
47 public double getMinSpeed() throws RemoteException;
48
49
50 /**
51 * Gibt die aktuelle Geschwindigkeit (in flops) des
52 * Rechners zurück.
53 * @return Die aktuelle Geschwindigkeit des Rechners
54 */
55 public double getCurrentSpeed() throws RemoteException;
56
57
58 /**
59 * Gibt die durchschnittliche Geschwindigkeit (in flops)
60 * des Rechners zurück.
61 * @return Die durchschnittliche Geschwindigkeit des
62 * Rechners
63 */
64 public double getAverageSpeed() throws RemoteException;
65 }

B.2. Quellcode der Klasse BenchMachine

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
Quellcode der Beobachter Komponente 226

7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)


8 * Class: BenchMachine
9 * Date: 09.02.2003
10 * Version: 1.0
11 */
12 package observer;
13
14 import java.rmi.server.*;
15 import java.rmi.*;
16
17 /**
18 * Eine BenchMachine ist ein zu überwachender Rechner. Eine
19 * Instanz dieser Klasse bezieht sich auf den Rechner auf dem
20 * das Objekt erzeugt wurde. Die Geschwindigkeiten werden in
21 * FLoating Operations Per Second (flops) gemessen.
22 */
23 public class BenchMachine extends UnicastRemoteObject
24 implements IBenchMachine{
25
26 /** Der Name des Rechners im Netzwerk. */
27 private String hostName;
28
29 /** Die maximale Geschwindigkeit des Rechners. */
30 private double maxSpeed;
31
32 /** Die minimale Geschwindigkeit des Rechners. */
33 private double minSpeed;
34
35 /** Die aktuelle Geschwindigkeit des Rechners. */
36 private double currentSpeed;
37
38 /** Die durchschnittliche Geschwindigkeit des Rechners. */
39 private double averageSpeed;
40
41 /**
42 * Der Cache zum Errechnen der Durchschnitt-
43 * geschwindigkeit
44 */
45 private double cache[];
46
47 /**
48 * Hilfsvariablen zum Errechnen des Durchschnitts
49 */
50 private double cacheSum;
51 private long measureCount;
Quellcode der Beobachter Komponente 227

52
53
54 /** Erzeugt ein neues BenchMachine Objekt.
55 * @param hostName Der Name des Rechners.
56 * @param cacheSize Die Größe des zu verwendenden Caches
57 * für die Durchschnittsgeschwindigkeit.
58 */
59 public BenchMachine(String hostName, int cacheSize)
60 throws RemoteException {
61
62 if (cacheSize <1) throw new IllegalArgumentException(
63 "Größe des Caches muss größer als 0 sein");
64 this.hostName = hostName;
65 this.maxSpeed = 0;
66 this.minSpeed = Double.MAX_VALUE;
67 cache =new double[cacheSize];
68 }
69
70 // Methoden des remote Interface.
71
72 public String getHostName() throws RemoteException {
73
74 return hostName;
75 }
76
77
78 public double getMaxSpeed() throws RemoteException {
79
80 return maxSpeed;
81 }
82
83
84 public double getMinSpeed() throws RemoteException {
85
86 return minSpeed;
87 }
88
89
90 public double getCurrentSpeed() throws RemoteException {
91
92 return currentSpeed;
93 }
94
95
96 public double getAverageSpeed() throws RemoteException {
Quellcode der Beobachter Komponente 228

97
98 return averageSpeed;
99 }
100
101
102 // lokale Methoden
103
104 /**
105 * Setzt die maximale Geschwindigkeit des Rechners.
106 * @param maxSpeed Die zu setzende Geschwindigkeit.
107 */
108 private void setMaxSpeed(double maxSpeed){
109
110 this.maxSpeed = maxSpeed;
111 }
112
113
114 /**
115 * Setzt die minimale Geschwindigkeit des Rechners.
116 * @param minSpeed Die zu setzende Geschwindigkeit.
117 */
118 private void setMinSpeed(double minSpeed){
119
120 this.minSpeed = minSpeed;
121 }
122
123
124 /**
125 * Setzt die aktuelle Geschwindigkeit des Rechners.
126 * @param currentSpeed Die zu setzende Geschwindigkeit.
127 */
128 protected void setCurrentSpeed(double currentSpeed){
129
130 this.currentSpeed = currentSpeed;
131 if (currentSpeed < minSpeed) {
132 setMinSpeed(currentSpeed);
133 }
134 if (currentSpeed > maxSpeed) {
135 setMaxSpeed(currentSpeed);
136 }
137 setAverageSpeed(currentSpeed);
138 }
139
140
141 /**
Quellcode der Beobachter Komponente 229

142 * Setzt die durchschnittliche Geschwindigkeit des


143 * Rechners.
144 * @param currentSpeed Die aktuelle Geschwindigkeit.
145 */
146 private void setAverageSpeed(double currentSpeed){
147
148 if (measureCount == 0){
149 for (int i=0; i<cache.length;i++){
150 cache[i]=currentSpeed;
151 }
152 cacheSum = cache.length * currentSpeed;
153 averageSpeed = currentSpeed;
154 measureCount++;
155 }else{
156 measureCount++;
157 cacheSum -= cache[cache.length-1];
158 System.arraycopy(cache,0,cache,1,cache.length-1);
159 cache[0] = currentSpeed;
160 cacheSum += cache[0];
161 averageSpeed = cacheSum / cache.length;
162 }
163 }
164 }

B.3. Quellcode der Klasse BenchServer und Benchmark

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: BenchServer, BenchServer.Benchmark
9 * Date: 09.02.2003
10 * Version: 1.0
11 */
12
13 package observer;
14
15 import java.rmi.*;
16 import java.util.*;
Quellcode der Beobachter Komponente 230

17 import java.rmi.registry.*;
18
19
20 /**
21 * Diese Klasse repräsentiert einen BenchServer. Ein
22 * BenchServer erzeugt ein BenchMachine Objekt und legt
23 * es in die lokale RMI - Registry ab. In festgelegten
24 * Zeitabständen wird die Leistung des Rechners gemessen und
25 * die Daten in das BenchMachine Objekt abgelegt. Der
26 * Algorithmus zum Messen der Leistung ist in der Unter-
27 * klasse Benchmark zu sehen. Es werden lediglich die
28 * FLoating Operations Per Second (flops) gemessen. Der
29 * BenchServer benötigt zum Sstart eine Konfigurationsdatei
30 * die den Namen:
31 * BenchServer.properties
32 * tragen muss. Die folgenden keywords müssen definiert
33 * sein:
34 * MAC_HOSTNAME
35 * MAC_REG_NAME
36 * MAC_REG_HOSTNAME
37 * MAC_REG_PORT
38 * INTERVAL
39 * VERBOSE
40 * SHOWEXCEPTION
41 * BENCH_COUNT
42 * CACHE_SIZE
43 */
44 public class BenchServer {
45
46 /**
47 * Instanz einer BenchMachine. In der init Methode wird das
48 * Object erzeugt und zugewiesen.
49 */
50 private BenchMachine machine = null;
51
52 /**
53 * Der Name, unter dem das BenchMachine Objekt in der RMI-
54 * Registry abgelegt werden soll.
55 */
56 private String machineRegName = "";
57
58 /** Der Name des Rechners, der die RMI-Registry
59 * bereitstellt, wo das BenchMachine Objekt abgelegt
60 * werden soll. Dies wird im Allgemeinen localhost sein.
61 * Es ist aber möglich, dass das System die Variable
Quellcode der Beobachter Komponente 231

62 * localhost nicht kennt. In diesem Fall muss der


63 * Rechnername oder die IP Adresse den Rechners genutzt
64 * werden.
65 */
66 private String machineRegHostname = "localhost";
67
68 /**
69 * Die Portadresse der genutzten RMI-Registry. Der
70 * Standardport der RMI-Registry ist 1099.
71 */
72 private int machineRegPort = 1099;
73
74 /**
75 * Die Zeit zwischen den einzelnen Benchmark Messungen. Die
76 * Zeit ist gemessen in Millisekunden. Der Standardwert
77 * beträgt 10000 Millisekunden.
78 */
79 private int interval = 10000;
80
81 /**
82 * Die Anzahl, wie oft der Algorithmus für eine einzelne
83 * Messung ausgeführt werden soll. Der Wert sollte nicht
84 * zu hoch sein, da jede Messung Leistung des Rechners
85 * kostet. Der Standardwert beträgt 500.
86 */
87 private long BenchmarkCount = 500;
88
89 /**
90 * Wahr bedeutet, dass alle Daten der Messungen auf den
91 * Standard-Ausgang geschrieben werden. Der Standardwert
92 * ist wahr;
93 */
94 private boolean verbose = true;
95
96 /**
97 * Wahr bedeutet, dass alle Exceptions auf den
98 * Standard-Error Ausgang geschrieben werden. Der
99 * Standardwert ist false.
100 */
101 private boolean showException = false;
102
103 /**
104 * Der Timertask repräsentiert mit der Unterklasse
105 * Benchmark den Benchmark Algorithmus. Es werden eine
106 * bestimmte Anzahl von floating point Operationen durch-
Quellcode der Beobachter Komponente 232

107 * geführt und die Zeit gemessen. Danach wird das


108 * Messergebnis in das BenchMachine Objekt geschrieben. Ist
109 * das Verbose Flag gesetzt, so werden die derzeitigen
110 * Geschwindigkeiten auch noch auf den Standardausgang
111 * geschrieben.
112 */
113 TimerTask task = new TimerTask() {
114
115 public void run() {
116 Benchmark bench = new Benchmark();
117 try {
118
119 long startTime = System.currentTimeMillis();
120 for (int i=0;i<BenchmarkCount;i++) {
121 bench.instructions();
122 }
123 long endTime = System.currentTimeMillis();
124 long time = (endTime-startTime);
125 double speed = (BenchmarkCount*5000*1000/time);
126 machine.setCurrentSpeed(speed);
127
128 StringBuffer output = new StringBuffer();
129 output.append("Akt: "+(int) machine.
130 getCurrentSpeed());
131 output.append(" Max: "+(int) machine.getMaxSpeed());
132 output.append(" Min: "+(int) machine.getMinSpeed());
133 output.append(" Durchschnitt: "+
134 machine.getAverageSpeed());
135
136 if (verbose) System.out.println(output.toString());
137
138 } catch (java.rmi.RemoteException exc) {
139 /*
140 * dürfte niemals auftauchen, da auf das Objekt lokal
141 * zugegriffen wird
142 */
143 logException(exc);
144 }
145 }
146 };
147
148
149 /**
150 * Wenn das showException - Flag gesetzt ist, werden alle
151 * Exceptions auf den Standard-Error-Ausgang geschrieben.
Quellcode der Beobachter Komponente 233

152 */
153 private void logException(Exception exc) {
154
155 if (showException) exc.printStackTrace();
156 }
157
158
159 /**
160 * Konstruktor zum Initialisieren des BenchServers. Ruft
161 * nur die init() Methode auf.
162 */
163 public BenchServer() {
164
165 init();
166 }
167
168
169 /**
170 * Initialisieren des BenchServers. Ein BenchMachine Objekt
171 * wird erzeugt und in die RMI-Registry eingetragen. Zum
172 * Registrieren werden die Daten aus den Feldern
173 * machineRegHostname, machineRegPort und machineRegName
174 * der Konfigurationsdatei "BenchServer.properties"
175 * genutzt. Die einzelnen Einstellungen werden direkt nach
176 * dem Start auf den Standardausgang geschrieben.
177 */
178 public void init() {
179
180 String machineHostname = "";
181 ResourceBundle rb = null;
182 int cacheSize = 10;
183 /*
184 * Daten aus der Konfigurationsdatei lesen. Sind nicht alle
185 * Daten angegeben, wird die Initialisierung abgebrochen.
186 */
187 try {
188 rb = ResourceBundle.getBundle("BenchServer");
189 machineRegName =rb.getString("MAC_REG_NAME");
190 machineRegHostname =rb.getString("MAC_REG_HOSTNAME");
191 machineHostname =rb.getString("MAC_HOSTNAME");
192 verbose =((rb.getString("VERBOSE").equalsIgnoreCase(
193 "true"))?true:false);
194 showException =((rb.getString("SHOWEXCEPTION").
195 equalsIgnoreCase("true"))?true:false);
196
Quellcode der Beobachter Komponente 234

197 try {
198 machineRegPort = Integer.parseInt(rb.getString(
199 "MAC_REG_PORT"));
200 } catch (NumberFormatException exc) {
201 System.out.println("Konnte den Wert für das Feld "+
202 "MAC_REG_PORT nicht in einen Integer casten. Es wird "+
203 "der Standardport von "+machineRegPort+" verwendet");
204 }
205
206 try {
207 interval = Integer.parseInt(rb.getString("INTERVAL"));
208 } catch (NumberFormatException exc) {
209 System.out.println("Konnte den Wert für das Feld "+
210 "INTERVAL nicht in einen Integer casten. Es wird der "+
211 "Standardwert von "+interval+" ms verwendet");
212 }
213
214 try {
215 cacheSize = Integer.parseInt(
216 rb.getString("CACHE_SIZE"));
217 } catch (NumberFormatException exc) {
218 System.out.println("Konnte den Wert für das Feld "+
219 "CACHE_SIZE nicht in einen Integer casten. Es wird "+
220 "der Standardwert von "+cacheSize+" verwendet");
221 }
222
223 try {
224 BenchmarkCount = Long.parseLong(
225 rb.getString("BENCH_COUNT"));
226 } catch (NumberFormatException exc) {
227 System.out.println("Konnte den Wert für das Feld"+
228 " BENCH_COUNT nicht in einen Long casten. Es wird "+
229 "der Standardwert von "+BenchmarkCount +" verwendet");
230 }
231
232 } catch(MissingResourceException exc) {
233 logException(exc);
234 System.out.println("Konfigurationsdatei konnte nicht"+
235 " gefunden werden");
236 System.out.println("BenchServer angehalten");
237 System.exit(-1);
238 }
239
240 // Initialisieren des BenchMachine Objektes
241 try{
Quellcode der Beobachter Komponente 235

242 machine = new BenchMachine(machineHostname,cacheSize);


243
244 /*
245 * Neue RMI-Registry erzeugen, falls noch keine
246 * gestartet wurde
247 */
248 Registry registry = LocateRegistry.getRegistry(
249 machineRegPort);
250 try {
251 registry.list();
252 } catch (RemoteException exc) {
253 LocateRegistry.createRegistry(machineRegPort);
254 System.err.println("Neue RMI-Registry auf Port " +
255 machineRegPort + " gestartet");
256 }
257 } catch (RemoteException exc) {
258 exc.printStackTrace();
259 }
260 try{
261 Naming.rebind("rmi://"+machineRegHostname+":"+
262 machineRegPort+"/"+machineRegName, machine);
263 System.out.println("----------------------------------");
264 System.out.println("Der Rechner: "+machine.
265 getHostName());
266 System.out.println("wurde unter dem Namen " +
267 machineRegName);
268 System.out.println("in der folgenden RMI-Registry "+
269 "registriert.");
270 System.out.println("Rechner: "+machineRegHostname);
271 System.out.println("Port : "+machineRegPort);
272 System.out.println("----------------------------------");
273 System.out.println("Die Geschwindigkeit wird alle "+
274 interval + " Millisekunden gemessen.");
275 System.out.println("----------------------------------");
276 new Timer(true).
277 scheduleAtFixedRate(task,interval,interval);
278 System.out.println("BenchServer erfolgreich "+
279 "initialisiert.");
280
281 } catch (Exception exc) {
282 logException(exc);
283 System.out.println("Während der registrierung trat ein "+
284 "Fehler auf.");
285 System.out.println("BenchServer angehalten!");
286 System.exit(-1);
Quellcode der Beobachter Komponente 236

287 }
288 }
289
290
291 /**
292 * Startet den BenchServer.
293 */
294 public static void main(String args[]) {
295
296 BenchServer server = new BenchServer();
297 }
298
299
300
301 /**
302 * Diese Klasse repräsentiert den Benchmark Algorithmus.
303 */
304 public class Benchmark {
305
306 public void instructions() {
307
308 float temp =1;
309 for (int i=0; i<5000; i++) {
310 temp++;
311 }
312 }
313 }
314 }

B.4. Beispiel für die Konfigurationsdatei des BenchServer

1 # ###########################################################
2 # Daten für den BenchServer bzw. für das BenchMachine Objekt
3 # ###########################################################
4
5 # IP Adresse bzw. Name des Rechners im Netzwerk
6 MAC_HOSTNAME = hope0
7
8
9 # ###########################################################
10 # Daten für die zu benutzende RMI REGISTRY
11 # ###########################################################
Quellcode der Beobachter Komponente 237

12
13 # Der Name, unter dem das BenchMachine Objekt in der RMI-
14 # Registry abgelegt werden soll.
15 MAC_REG_NAME = benchObj
16
17 # Der Name des Rechners, der die RMI-Registry bereitstellt
18 # wo das BenchMachine Objekt abgelegt werden soll.
19 # Dies wird im Allgemeinen localhost sein. Es ist aber
20 # möglich, dass das System die Variable localhost nicht
21 # kennt. In diesem Fall muss der Rechnername oder die IP
22 # Adresse den Rechners genutzt werden.
23 MAC_REG_HOSTNAME = localhost
24
25 # Die Portadresse der genutzten RMI-Registry. Der
26 # Standardport der RMI-Registry ist 1099.
27 MAC_REG_PORT = 1098
28
29 # ###########################################################
30 # Konfiguration des BenchServer
31 # ###########################################################
32
33 # Die Zeit zwischen den einzelnen Benchmark Messungen. Die
34 # Zeit ist gemessen in Millisekunden. Der Standardwert
35 # beträgt 10000 Millisekunden (10 Sekunden).
36 INTERVAL = 1000
37
38 # Wahr bedeutet, dass alle Daten der Messungen auf den
39 # Standard-Ausgang geschrieben werden. Der Standardwert ist
40 # true.
41 VERBOSE = true
42
43 # Wahr bedeutet, das alle Exceptions auf den Standart-Error-
44 # Ausgang geschrieben werden. Der Standardwert ist false.
45 SHOWEXCEPTION = true
46
47 # Die Anzahl, wie oft der Algorithmus zur Geschwindigkeits-
48 # prüfung für eine Messung ausgeführt werden soll.
49 # Der Wert sollte nicht zu hoch sein, da jede Messung
50 # Leistung des Rechners kostet. Der Standardwert
51 # beträgt 500.
52 BENCH_COUNT = 500
53
54 # Der Cache zum Rrrechnen der Durchschnittgeschwindigkeit.
55 CACHE_SIZE = 5
Quellcode der Beobachter Komponente 238

B.5. Quellcode der Klasse ObserverAppServer

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: ObserverMachine
9 * Date: 13.02.2003
10 * Version: 1.0
11 */
12
13 package observer;
14
15 import java.io.Serializable;
16
17
18 /**
19 * Die ObserverAppServer Klasse speichert Informationen über
20 * einen zu überwachenden Applikations-Server. Ein
21 * Appliaktions-Server kann normalerweise über den
22 * Rechnernamen eindeutig indentifiziert werden. Zu jedem
23 * Applikations-Server kann ein ObserverBenchMachine Objekt
24 * gespeichert werden. Ist ein solches Objekt vorhanden,
25 * können die einzelnen Dienste des Applikations-Servers
26 * minimale Geschwindigkeiten voraussetzen.
27 */
28 public class ObserverAppServer implements Serializable{
29
30
31 /**
32 * Der Name des Applikations Servers. Dieser Name muss
33 * einzigartig für jedes Objekt dieser Klasse sein. Dies
34 * erreicht man am einfachsten, indem man den Namen des
35 * Rechners im Netzwerk wählt.
36 */
37 public String name;
38
39 /**
40 * Das BenchMachine Objekt des Applikations-Servers. Ein
41 * Applikations-Server kann ein BenchMachine Objekt
42 * bereitstellen, um Lastenverteilung zu unterstützen.
43 */
Quellcode der Beobachter Komponente 239

44 public ObserverBenchMachine observerBenchMachine;


45
46
47 /**
48 * Erzeugt ein neues ObserverAppServer Objekt.
49 * @param name Der Name des zu überwachenden
50 * Applikations-Servers.
51 */
52 public ObserverAppServer(String name) {
53
54 this.name = name;
55 observerBenchMachine = null;
56 }
57
58
59 /**
60 * Erzeugt ein neues ObserverAppServer Objekt.
61 * @param name Der Name des zu überwachenden
62 * Applikations-Servers.
63 * @param hostname Der Name des Rechners, auf dem sich das
64 * BenchMachine Objekt befindet.
65 * @param portnumber Die Addresse der RMI-Registry des
66 * BenchMachine Objektes.
67 * @param regName Der Name unter dem das BenchMachine
68 * Objekt auf dem entfernten Rechner registriert ist.
69 */
70 public ObserverAppServer(String name, String hostname,
71 String portnumber, String regName) {
72
73 this.name = name;
74 observerBenchMachine = new
75 ObserverBenchMachine(hostname, portnumber, regName);
76 }
77
78
79 /**
80 * Liefert den Namen des Applikations-Servers zurück.
81 * @return Der Name des Applikations-Servers.
82 */
83 public String getName() {
84
85 return name;
86 }
87
88
Quellcode der Beobachter Komponente 240

89 /**
90 * Liefert das gesetzte ObserverBenchMachine Objekt
91 * zurück. Unterstützt der Applikations-Server kein
92 * BenchMachine Objekt, so gibt diese Methode null zurück.
93 */
94 public ObserverBenchMachine getObserverBenchMachine() {
95
96 return observerBenchMachine;
97 }
98
99
100 /**
101 * Setzt den Namen des zu überwachenden Applikations-
102 * Servers.
103 * @param name Der Name des zu überwachenden
104 * Applikations-Servers.
105 */
106 public void setName(String name) {
107
108 this.name = name;
109 }
110
111
112 /**
113 * Setzt ein ObserverBenchMachine Objekt.
114 * @param portnumber Die Addresse der RMI-Registry des
115 * BenchServer Objektes.
116 * @param regName Der Name, unter dem das BenchMachine
117 * Objekt auf dem entfernten Rechner registriert ist.
118 * @param hostname Der Name des Rechners, auf dem sich das
119 * BenchMachine Objekt befindet.
120 */
121 public void setObserverBenchMachine(String hostname, String
122 portnumber, String regName) {
123
124 observerBenchMachine = new
125 ObserverBenchMachine(hostname, portnumber, regName);
126 }
127
128
129 /**
130 * Überschreiben der toString() Methode. Damit bei Ausgabe
131 * eines ObserverAppServer Objektes automatisch der Name
132 * des Applikations-Servers ausgegeben wird.
133 */
Quellcode der Beobachter Komponente 241

134 public String toString() {


135
136 return name;
137 }
138
139
140 /**
141 * Zwei ObserverAppServer sind gleich, wenn ihre String
142 * Repräsentationen gleich sind.
143 * @param obj Das zu untersuchende ObserverAppMachine
144 * Objekt.
145 * @return true Wenn beide Objekte gleich sind.
146 */
147 public boolean equals(Object obj) {
148
149 if (obj instanceof ObserverAppServer) {
150 return toString().compareTo(obj.toString()) == 0;
151 }
152 return false;
153 }
154
155
156 /**
157 * Liefert den Hash-Code eines ObserverAppServer Objektes
158 * zurück. Dieser Wert muss für jedes ObserverAppMachine
159 * Objekt eindeutig sein. Da der Name des Applikations-
160 * Servers eindeutig sein muss, wird der Hash-Code des
161 * Namens zurückgeliefert.
162 */
163 public int hashCode() {
164
165 return toString().hashCode();
166 }
167 }

B.6. Quellcode der Klasse ObserverBenchMachine

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
Quellcode der Beobachter Komponente 242

6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: ObserverBenchMachine
9 * Date: 11.02.2003
10 * Version: 1.0
11 */
12
13 package observer;
14
15 import java.io.Serializable;
16
17
18 /**
19 * Die ObserverBenchMachine Klasse speichert Informationen zu
20 * einem entfernten BenchServer Objekt. Damit das entfernte
21 * BenchServer Objekt im Falle eines Verbindungsabbruchs
22 * wieder gefunden werden kann, werden zudem noch alle
23 * Informationen zum lookup gespeichert.
24 */
25 public class ObserverBenchMachine implements Serializable{
26
27
28 /**
29 * Der Rechnername, auf dem sich das BenchMachine Objekt
30 * befindet.
31 */
32 public final String host;
33
34 /**
35 * Die Addresse der RMI-Registry auf dem entfernten
36 * Rechner.
37 */
38 public final String regPort;
39
40 /**
41 * Der Name, unter dem das BenchMachine Objekt auf dem
42 * entfernten Rechner registriert ist.
43 */
44 public final String regName;
45
46 /**
47 * Die letzte bekannte Geschwindigkeit des Rechners.
48 */
49 private double lastSpeed = Double.NaN;
50
Quellcode der Beobachter Komponente 243

51 /**
52 * Wahr, wenn bereits ein Thread eine Verbindung zu dem
53 * BenchMachine Objekt aufbaut.
54 */
55 private boolean checking = false;
56
57 /** Referenz zu dem BenchMachine Objekt. */
58 private IBenchMachine machine = null;
59
60
61 /**
62 * Erzeugt ein neues ObserverBenchMachine Objekt.
63 * @param hostname Der Rechnername des zu
64 * überwachenden BenchServer Objektes.
65 * @param portnumber Die Addresse der RMI-Registry des
66 * BenchServer Objektes.
67 * @param regName Der Name, unter dem das BenchMachine
68 * Objekt auf dem entfernten Rechner registriert ist.
69 */
70 public ObserverBenchMachine(String hostname, String
71 portnumber, String regName) {
72
73 this.host = hostname;
74 this.regPort = portnumber;
75 this.regName = regName;
76 }
77
78
79 /**
80 * Setzt das checking flag.
81 * Ist dieser Wert bereits auf wahr gesetzt, sollte
82 * kein anderer Thread eine Verbindung aufbauen.
83 * @param value Der neue checking Wert.
84 */
85 synchronized void setChecking(boolean value) {
86
87 checking = value;
88 }
89
90
91 /**
92 * Prüft, ob bereits ein Thread versucht eine Verbindung
93 * aufzubauen.
94 * @return true Wenn breits ein anderer Thread eine
95 * Verbindung aufbaut.
Quellcode der Beobachter Komponente 244

96 */
97 public synchronized boolean isChecking() {
98
99 return checking;
100 }
101
102
103 /**
104 * Setzt die Referenz zum remote BenchMachine Objekt.
105 * @param machine Die BenchMachine Referenz.
106 */
107 void setBenchMachine(IBenchMachine machine) {
108
109 this.machine = machine;
110 }
111
112
113 /**
114 * Liefert die letzte bekannte Geschwindigkeit des
115 * entfernten Rechners zurück.
116 * Ist das BenchMachine Object des Rechners nicht
117 * erreichbar, wird Double.NaN zurückgeliefert.
118 * @return Die letzte bekannte Geschwindigkeit.
119 */
120 public double getLastSpeed() {
121
122 return lastSpeed;
123 }
124
125
126 /**
127 * Setzt die letzte bekannte Geschwindigkeit. Falls andere
128 * Objekte Interesse an der Geschwindigkeit des Rechners
129 * haben, müssen sie nicht extra einen remote Call
130 * ausführen, sondern erhalten mit diesem Objekt
131 * automatisch eine Kopie des letzten Wertes.
132 * @param lastSpeed Die Geschwindigkeit.
133 */
134 void setLastSpeed(double lastSpeed) {
135
136 this.lastSpeed = lastSpeed;
137 }
138
139
140 /**
Quellcode der Beobachter Komponente 245

141 * Liefert die Referenz zum entfernten BenchMachine Objekt


142 * zurück.
143 * @return Das BenchMachine Objekt.
144 */
145 public IBenchMachine getBenchMachine() {
146
147 return machine;
148 }
149 }

B.7. Quellcode der Klasse ObserverService

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: ObserverService
9 * Date: 13.02.2003
10 * Version: 1.0
11 */
12
13 package observer;
14
15
16 /**
17 * Ein ObserverService spezifiziert einen Dienst. Jeder
18 * Dienst hat einen eindeutigen Namen, einen Typ,
19 * einen lookup Namen und eine minimale vorausgesetzte
20 * Rechnergeschwindigkeit. Hat ein Rechner momentan nicht die
21 * vorausgesetzte Geschwindigkeit, kann der Dienst auf diesen
22 * Rechner nicht ausgeführt werden.
23 */
24 public class ObserverService {
25
26
27 /**
28 * Der Name des Dienstes.
29 */
30 private String name;
Quellcode der Beobachter Komponente 246

31
32 /**
33 * Die minimale Geschwindigkeit, die für das Ausführen
34 * dieses Dienstes notwendig ist. Hat ein Rechner, der
35 * diesen Dienst bereitstellt, momentan nicht die
36 * vorausgesetzte Geschwindigkeit, kann der Dienst auf
37 * diesem Rechner nicht ausgeführt werden.
38 */
39 private double minSpeed;
40
41 /**
42 * Der genaue Typ dieses Dienstes.
43 */
44 private ServiceType type;
45
46 /**
47 * Der Name des Rechners, unter dem das Objekt auf dem
48 * entfernten Rechner abgelegt ist.
49 */
50 private String lookupHost;
51
52 /**
53 * Der Port, unter dem das Objekt auf dem entfernten
54 * Rechner abgelegt ist.
55 */
56 private int lookupPort;
57
58 /**
59 * Der Name, unter dem das Objekt auf dem entfernten
60 * Rechner abgelegt ist.
61 */
62 private String lookupName;
63
64 /**
65 * Wahr, wenn bereits ein Thread eine Verbindung zu dem
66 * Dienst aufbaut.
67 */
68 private boolean checking = false;
69
70 /**
71 * Wahr, wenn der letzte Lookup erfoglreich war.
72 */
73 private boolean available = true;
74
75 /**
Quellcode der Beobachter Komponente 247

76 * Wahr, wenn der Applikations-Server momentan nicht die


77 * minimale Geschwindigkeit bereitstellt.
78 */
79 private boolean overload = false;
80
81
82 /**
83 * Erzeugt ein neues ObserverService Objekt.
84 * @param name Der Name des Dienstes
85 * @param type Der Typ des Dienstes
86 * @param host Der Name des Rechners, unter dem der
87 * Dienst abgelegt ist.
88 * @param mSpeed Die minimale Geschwindigkeit, die für
89 * das Ausführen dieses Dienstes notwendig ist.
90 * @param port Der Port, unter dem der Dienst auf
91 * dem entfernten Rechner abgelegt ist.
92 * @param RegName Der Name, unter dem das Objekt auf dem
93 * entfernten Rechner abgelegt ist.
94 */
95 public ObserverService(String name, int type, double
96 minSpeed, String host, int port, String RegName) {
97
98 this.name = name;
99 this.type = new ServiceType(type);
100 this.minSpeed = minSpeed;
101 this.lookupHost = host;
102 this.lookupPort = port;
103 this.lookupName = RegName;
104 }
105
106
107 /**
108 * Liefert den Namen des Dienstes zurück. Dieser Name muss
109 * eindeutig für jeden Dienst sein.
110 * @return Den Namen des Dienstes.
111 */
112 public String getName() {
113
114 return name;
115 }
116
117
118 /**
119 * Liefert den Namen des Rechners, unter dem das Objekt
120 * auf dem entfernten Rechner abgelegt ist, zurück.
Quellcode der Beobachter Komponente 248

121 * @return Der lookup Host des Dienstes.


122 */
123 public String getLookupHost() {
124
125 return lookupHost;
126 }
127
128
129 /**
130 * Liefert den Port, unter dem das Objekt auf dem
131 * entfernten Rechner abgelegt ist, zurück.
132 * @return Der lookup Port des Dienstes.
133 */
134 public int getLookupPort() {
135
136 return lookupPort;
137 }
138
139
140 /**
141 * Liefert den Namen, unter dem das Objekt auf dem
142 * entfernten Rechner abgelegt ist, zurück.
143 * @return Der lookup Name des Dienstes.
144 */
145 public String getLookupName() {
146
147 return lookupName;
148 }
149
150
151 /**
152 * Liefert die minimale Geschwindigkeit, die für das
153 * Ausführen dieses Dienstes notwendig ist, zurück. Hat
154 * ein Rechner, der diesen Dienst bereitstellt, momentan
155 * nicht die vorausgesetzte Geschwindigkeit, kann der
156 * Dienst auf diesem Rechner nicht ausgeführt werden.
157 */
158 public double getMinSpeed() {
159
160 return minSpeed;
161 }
162
163
164 /**
165 * Liefert den genauen Typ dieses Dienstes zurück.
Quellcode der Beobachter Komponente 249

166 * @return Der Typ dieses Deinstes.


167 */
168 public ServiceType getServiceType() {
169
170 return type;
171 }
172
173
174 /**
175 * Setzt den Namen dieses Dienstes.
176 * @param name Der Name des Dienstes.
177 */
178 public void setName(String name) {
179
180 this.name = name;
181 }
182
183
184 /**
185 * Setzt den Namen des Rechners, unter dem das Objekt
186 * abgelegt ist.
187 * @param lookupHost Der Name des Rechners, unter dem
188 * der Dienst abgelegt ist.
189 */
190 public void setLookupHost(String lookupHost) {
191
192 this.lookupHost = lookupHost;
193 }
194
195
196 /**
197 * Setzt den Port, unter dem das Objekt auf dem
198 * entfernten Rechner abgelegt ist.
199 * @param lookupPort Der lookup Port des Dienstes.
200 */
201 public void setLookupPort(int lookupPort) {
202
203 this.lookupPort = lookupPort;
204 }
205
206
207 /**
208 * Setzt den Namen, unter dem das Objekt auf dem
209 * entfernten Rechner abgelegt ist.
210 * @param lookupName Der lookup Name des Dienstes.
Quellcode der Beobachter Komponente 250

211 */
212 public void setLookupName(String lookupName) {
213
214 this.lookupName = lookupName;
215 }
216
217
218 /**
219 * Setzt das checking flag.
220 * Ist dieser Wert bereits auf wahr gesetzt, sollte
221 * kein anderer Thread eine Verbindung aufbauen.
222 * @param value Der neue checking Wert.
223 */
224 synchronized void setChecking(boolean value) {
225
226 checking = value;
227 }
228
229
230 /**
231 * Prüft, ob bereits ein Thread versucht eine Verbindung
232 * aufzubauen.
233 * @return true Wenn breits ein anderer Thread ein
234 * Verbindung aufbaut.
235 */
236 public synchronized boolean isChecking() {
237
238 return checking;
239 }
240
241
242 /**
243 * Setzt das available Flag.
244 * Wahr bedeutet, dass der letzte Lookup Versuch
245 * erfolgreich war.
246 * @param value Der neue available Wert.
247 */
248 synchronized void setAvailable(boolean value) {
249
250 available = value;
251 }
252
253
254 /**
255 * Setzt das overload flag.
Quellcode der Beobachter Komponente 251

256 * Wahr bedeutet, dass der entsprechende Applikations-


257 * Server momentan nicht die minimale Geschwindigkeit
258 * bereitstellt.
259 * @param value Der neue overload Wert.
260 */
261 synchronized void setOverload(boolean value) {
262
263 overload = value;
264 }
265
266
267 /**
268 * Prüft, ob der Dienst erreichbar ist. Wahr bedeutet,
269 * dass der letzte Lookup Versuch erfolgreich war.
270 * @return true Wenn der letzte Lookup erfolgreich war.
271 */
272 public synchronized boolean isAvailable() {
273
274 return available;
275 }
276
277
278 /**
279 * Prüft, ob der Dienst genügend Leistung hat. Wahr
280 * bedeutet, dass der Applikations-Server nicht die
281 * minimale Geschwindigkeit dieses Dienstes bereistellt.
282 * @return true Wenn der Applikations-Server nicht die
283 * minimale Geschwindigkeit bereitstellt.
284 */
285 public synchronized boolean isOverload() {
286
287 return overload;
288 }
289
290
291 /**
292 * Überschreiben der toString() Methode. Damit bei Ausgabe
293 * eines ObserverService Objektes automatisch der Name des
294 * Rechners ausgegeben wird.
295 */
296 public String toString() {
297
298 return name;
299 }
300
Quellcode der Beobachter Komponente 252

301
302 /**
303 * Zwei ObserverService sind gleich, wenn ihre String
304 * Repräsentationen gleich sind.
305 * @param obj Das zu untersuchende ObserverService Objekt.
306 * @return true Wenn beide Objekte gleich sind.
307 */
308 public boolean equals(Object obj) {
309
310 if (obj instanceof ObserverService) {
311 return toString().compareTo(obj.toString()) == 0;
312 }
313 return false;
314 }
315
316
317 /**
318 * Liefert den Hash-Code eines ObserverService Objektes
319 * zurück. Dieser Wert muss für jedes ObserverService
320 * Objekt eindeutig sein.
321 */
322 public int hashCode() {
323
324 return toString().hashCode();
325 }
326 }

B.8. Quellcode der Klasse ServiceType

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: ServiceType
9 * Date: 11.02.2003
10 * Version: 1.0
11 */
12
13 package observer;
Quellcode der Beobachter Komponente 253

14
15 /**
16 * Spezifiziert den genauen Typ eines Service (Dienstes).
17 * Diese Information ist notwendig, da der Verbindungsaufbau
18 * und das Erwerben einer Referenz für jeden Typ
19 * unterschiedlich ist. Bei RMI Objekten wird z.B. mit Hilfe
20 * der RMI-Registry gearbeitet und bei EJB mit Hilfe
21 * eines JNDI Servers. Die Hersteller von Aplikations-Servern
22 * liefern jeweils einen eigenen Treiber für den Zugriff auf
23 * den JNDI Dienst. Aus diesem Grund wird für jeden
24 * Hersteller ein eigener Typ definiert.
25 */
26 public class ServiceType {
27
28 /**
29 * Spezifiziert einen RMI Dienst.
30 */
31 public static final int RMI = 1;
32
33 /**
34 * Spezifiziert einen EJB Dienst auf einem SUN
35 * Applikations-Server.
36 */
37 public static final int EJB_SUN = 2;
38
39 /**
40 * Spezifiziert einen EJB Dienst auf einem JBoss
41 * Applikations-Server.
42 */
43 public static final int EJB_JBOSS = 3;
44
45 /**
46 * Der Typ des Dienstes.
47 */
48 private int type;
49
50
51 /**
52 * Erzeugt ein neues ServiceType Objekt.
53 * @param type Der Typ des Dienstes.
54 * @throws IllegalArgumentException falls der übergebene
55 * Typ nicht existiert.
56 */
57 public ServiceType(int type){
58
Quellcode der Beobachter Komponente 254

59 if ((type <1)||(type >3)) throw new


60 IllegalArgumentException("Ungültiger Typ");
61 this.type = type;
62 }
63
64 /**
65 * Liefert den gesetzten Typ zurück.
66 * @return Den gesetzen Typ.
67 */
68 public int getType() {
69
70 return type;
71 }
72 }

B.9. Quellcode der Schnittstelle IAppServerListener

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: IAppServerListener
9 * Date: 16.02.2003
10 * Version: 1.0
11 */
12
13 package observer;
14
15 import java.rmi.*;
16
17
18 /**
19 * Ein IAppServerListener kann beim Observer eingehängt
20 * werden, um bei einem Ereignis eines Applikations-Servers
21 * benachrichtigt zu werden.
22 */
23 public interface IAppServerListener {
24
25 /**
Quellcode der Beobachter Komponente 255

26 * Diese Methode wird aufgerufen, wenn der Observer eine


27 * Veränderung des Applikations-Servers bemerkt. Dies kann
28 * ein Ausfall des BenchMachine Objektes oder das Wieder-
29 * auffinden des BenchMachine Objektes sein.
30 */
31 public void notify(AppServerEvent e)throws Exception;
32 }

B.10. Quellcode der Schnittstelle IServiceListener

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: IServiceListener
9 * Date: 15.02.2003
10 * Version: 1.0
11 */
12
13 package observer;
14
15 import java.rmi.*;
16
17 /**
18 * Ein IServiceListener kann beim Observer eingehängt
19 * werden, um bei einem Ereignis eines Dienstes
20 * benachrichtigt zu werden.
21 */
22 public interface IServiceListener{
23
24 /**
25 * Diese Methode wird aufgerufen, wenn der Observer eine
26 * Veränderung des Dienstes bemerkt. Dies kann
27 * ein Ausfall des Dienst Objektes, das Wieder-
28 * auffinden des Dienst Objektes oder eine Überlastung
29 * des entsprechenden Applikations-Servers sein.
30 */
31 public void notify(ServiceEvent e) throws Exception;
32 }
Quellcode der Beobachter Komponente 256

B.11. Quellcode der Klasse AppServerEvent

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: AppServerEvent
9 * Date: 16.02.2003
10 * Version: 1.1
11 */
12
13 package observer;
14
15
16 /**
17 * Ein IAppServerEvent Objekt speichert Informationen über
18 * ein Ereignis eines Applikations-Servers. Ein solches
19 * Objekt wird als Parameter der notify() Methode eines
20 * AppServerListener Objektes übergeben.
21 */
22 public class AppServerEvent extends java.util.EventObject{
23
24 /**
25 * Das BenchMachine Objekt konnte wiedergefunden werden.
26 */
27 public static final int ENABLED = 1;
28
29 /**
30 * Das BenchMachine Objekt konnte nicht gefunden werden.
31 */
32 public static final int DISABLED = 2;
33
34 /**
35 * Der Name des Applikations-Servers.
36 */
37 private String appServerName;
38
39 /**
40 * Der Typ des Ereignisses.
41 */
42 private int type;
43
Quellcode der Beobachter Komponente 257

44 /**
45 * Gleiche Bedeutung wie in der Oberklasse EventObject.
46 * Das Feld muss nur neu deklariert werden, da die Ober-
47 * klasse es als transient deklariert hat. Transiente
48 * Felder werden beim Serialisierungs Mechanismus nicht
49 * übertragen.
50 */
51 private Object source;
52
53
54 /**
55 * Erzeugt ein neues AppServerEvent Objekt.
56 * @param source Die Herkunft des Ereignisses.
57 * @param name Der Name des Applikations-Servers.
58 * @param type Der Typ des Ereignisses.
59 */
60 public AppServerEvent(Object source, String name,
61 int type) {
62
63 super(source);
64 this.source=source;
65 this.appServerName = name;
66 this.type = type;
67 }
68
69
70 /**
71 * Gibt den Namen des betroffenen Applikations-Servers
72 * zurück.
73 * @return Der Name des Applikations-Servers.
74 */
75 public String getApplikationServerName() {
76
77 return appServerName;
78 }
79
80
81 /**
82 * Gibt den Typ des Ereignisses zurück.
83 * @return Der Typ des Ereignisses.
84 */
85 public int getType() {
86
87 return type;
88 }
Quellcode der Beobachter Komponente 258

89
90
91 /**
92 * Gleiche Bedeutung wie in der Oberklasse EventObject.
93 * Die Methode der Oberklasse muss deswegen überschrieben
94 * werden, damit das source Feld aus dieser Klasse
95 * zurückgegeben wird.
96 * @return Das Quell-Objekt.
97 */
98 public Object getSource(){
99
100 return source;
101 }
102
103
104 /**
105 * Gibt die String Repräsentation des
106 * AppServerEvent Objektes zurück.
107 *
108 * @return String der das AppServerEvent Objekte
109 * repräsentiert.
110 */
111 public String toString() {
112
113 return "[Quelle=" + source + "]";
114 }
115 }

B.12. Quellcode der Klasse ServiceEvent

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: ServiceEvent
9 * Date: 15.02.3003
10 * Version: 1.0
11 */
12
Quellcode der Beobachter Komponente 259

13 package observer;
14
15
16 /**
17 * Ein ServiceEvent Objekt speichert Informationen über
18 * ein Ereignis eines Dienstes. Ein solches Objekt wird als
19 * Parameter der notify() Methode eines IServiceListener
20 * Objektes übergeben.
21 */
22 public class ServiceEvent extends java.util.EventObject{
23
24
25 /**
26 * Der Dienst auf dem Applikations-Server konnte
27 * wiedergefunden werden.
28 */
29 public static final int ENABLED = 1;
30
31 /**
32 * Der Dienst auf dem Applikations-Server konnte nicht
33 * gefunden werden.
34 */
35 public static final int DISABLED = 2;
36
37 /**
38 * Der entsprechende Apllikations-Server ist überlastet.
39 */
40 public static final int OVERLOAD = 3;
41
42 /**
43 * Der Name des Dienstes.
44 */
45 private String serviceName;
46
47 /**
48 * Der Name des entsprechenden Applikations-Servers.
49 */
50 private String appServerName;
51
52 /**
53 * Der Typ des Ereignisses.
54 */
55 private int type;
56
57 /**
Quellcode der Beobachter Komponente 260

58 * Gleiche Bedeutung wie in der Oberklasse EventObject.


59 * Das Feld muss nur neu deklariert werden, da die Ober-
60 * klasse es als transient deklariert hat. Transiente
61 * Felder werden beim Serialisierungs Mechanismus nicht
62 * übertragen.
63 */
64 private Object source;
65
66 /**
67 * Erzeugt ein neues AppServerEvent Objekt.
68 * @param source Die Herkunft des Ereignisses.
69 * @param appServerName Der Name des Applikations-Servers.
70 * @param serviceName Der Name des Dienstes.
71 * @param type Der Typ des Ereignisses.
72 */
73 public ServiceEvent(Object source, String appServerName,
74 String serviceName, int type) {
75 super(source);
76 this.source = source;
77 this.appServerName = appServerName;
78 this.serviceName = serviceName;
79 this.type = type;
80 }
81
82
83 /**
84 * Gibt den Namen des betroffenen Applikations-Servers
85 * zurück.
86 * @return Der Name des Applikations-Servers.
87 */
88 public String getApplikationServerName() {
89 return appServerName;
90 }
91
92
93 /**
94 * Gibt den Namen des betroffenen Dienstes zurück.
95 * @return Der Name des Dienstes.
96 */
97 public String getServiceName() {
98 return serviceName;
99 }
100
101
102 /**
Quellcode der Beobachter Komponente 261

103 * Gibt den Typ des Ereignisses zurück.


104 * @return Der Typ des Ereignisses.
105 */
106 public int getType() {
107 return type;
108 }
109
110
111 /**
112 * Gleiche Bedeutung wie in der Oberklasse EventObject.
113 * Die Methode der Oberklasse muss deswegen überschrieben
114 * werden, damit das source Objekt aus dieser Klasse
115 * zurückgegeben wird.
116 * @return Das Quell-Objekt.
117 */
118 public Object getSource(){
119 return source;
120 }
121
122
123 /**
124 * Gibt die String EventObject Repräsentation des
125 * ServiceEvent Objektes zurück.
126 *
127 * @return String der das ServiceEvent Objekte
128 * repräsentiert.
129 */
130 public String toString() {
131 return "[Quelle=" + source + "]";
132 }
133 }

B.13. Quellcode der Schnittstelle IObserver

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: IObserver
Quellcode der Beobachter Komponente 262

9 * Date: 16.02.2003
10 * Version: 1.1
11 */
12
13 package observer;
14
15 import java.rmi.Remote;
16 import java.rmi.RemoteException;
17 import java.util.Collection;
18 import java.util.HashMap;
19
20
21 /**
22 * Dies ist die remote Schnittstelle für die Observer Klasse.
23 * Ein Observer kann Applikations-Server und ihre angebotenen
24 * Dienste überwachen. Jeder Applikations-Server kann ein
25 * BenchMAchine Objekt bereitstellen. Mit diesem Objekt kann
26 * die Geschwindigkeit des Applikations-Servers während der
27 * Laufzeit geprüft werden. Das Überwachen der Dienste und
28 * der BenchMachine Objekte wird jeweils in einem eigenen
29 * Thread durchgeführt, um eine höhere Performance zu
30 * erreichen, da eine RemoteException teilweise bis zu 2
31 * Minuten dauern kann.
32 */
33 public interface IObserver extends Remote {
34
35
36 /**
37 * Liefert das BenchMachine Objekt eines Applikations-
38 * Servers zurück. Existiert der Applikations-Server nicht
39 * bzw. ist kein BenchMachine Objekt registriert, wird
40 * null zurückgeliefert.
41 * @param appServerName Der Name des Applikations-Servers.
42 * @return Das BenchMachine Objekt des Applikations-
43 * Servers.
44 */
45 public IBenchMachine getBenchMachine(String appServerName)
46 throws RemoteException;
47
48
49 /**
50 * Liefert das ObserverAppServer Objekt eines
51 * Applikations-Servers zurück. Existiert der
52 * ApplikationsServer nicht, wird null zurückgeliefert.
53 * @param appServerName Der Name des Applikations-Servers.
Quellcode der Beobachter Komponente 263

54 * @return Das ObserverAppServer Objekt des Applikations-


55 * Servers.
56 */
57 public ObserverAppServer getObserverAppServer(String
58 appServerName) throws RemoteException;
59
60
61 /**
62 * Liefert eine Collection von ObserverAppServer Objekten
63 * (Applikations-Server) zurück, die den angegebenen
64 * Dienst unterstützen.
65 * @param service Der Name des Dienstes.
66 * @return Collection von ObserverAppServer Objekten.
67 */
68 public Collection getAppServerForService(String service)
69 throws RemoteException;
70
71
72 /**
73 * Liefert eine Collection von ObserverAppServer Objekten
74 * (Applikations-Server) zurück, die den angegebenen
75 * Dienst unterstützen und momentan verfügbar sind.
76 * @param service Der Name des Dienstes.
77 * @return Collection von ObserverAppServer Objekten.
78 */
79 public Collection getAvailableAppServerForService(String
80 service) throws RemoteException;
81
82
83 /**
84 * Liefert alle registrierten Dienste eines Applikations-
85 * Servers zurück.
86 * @param appServerName Der Name des Applikations-Servers.
87 * @return Collection von ObserverService Objekten.
88 */
89 public Collection getServices(String appServerName) throws
90 RemoteException;
91
92
93 /**
94 * Liefert alle zur Verfügung stehenden ObsererAppServer
95 * Objekte. Zur Verfügung stehend bedeutet, dass eine
96 * Verbindung zum BenchServer Objekt aufgebaut werden
97 * kann. Der Schlüssel dieser HashMap ist der Name des
98 * Applikations-Servers und der Wert das entsprechende
Quellcode der Beobachter Komponente 264

99 * ObserverAppServer Objektes.
100 * @return HashMap von ObserverAppServer Objekten.
101 */
102 public HashMap getAvailableAppServer()
103 throws RemoteException;
104
105
106 /**
107 * Liefert alle nicht zur Verfügung stehenden
108 * ObsererAppServer Objekte. Nicht zur Verfügung stehend
109 * bedeutet, dass keine Verbindung zum BenchServer Objekt
110 * aufgebaut werden kann. Der Schlüssel dieser HashMap ist
111 * der Name des Applikations-Server und der Wert das
112 * entsprechende ObserverAppServer Objektes.
113 * @return HashMap von ObserverAppServer Objekten.
114 */
115 public HashMap getNotAvailableAppServer()
116 throws RemoteException;
117
118
119 /**
120 * Liefert alle registrierten ObsererAppServer Objekte
121 * zurück. Der Schlüssel dieser HashMap ist der Name des
122 * Applikations-Servers und der Wert das entsprechende
123 * ObserverAppServer Objekt.
124 * @return HashMap von ObserverAppServer Objekten.
125 */
126 public HashMap getAllAppServer() throws RemoteException;
127
128
129 /**
130 * Liefert alle registrierten Dienste zurück. Der
131 * Schlüssel dieser HashMap ist der Name des Applikations-
132 * Servers und des Dienstes. Der Wert ist das
133 * ObserverService Objekt.
134 * @return HashMap von ObserverService Objekten.
135 */
136 public HashMap getServices() throws RemoteException;
137
138
139 /**
140 * Registriert einen Applikations-Server, der kein
141 * BenchMachine Objekt unterstützt.
142 * @param appServerName Der Name des Applikations-Servers.
143 */
Quellcode der Beobachter Komponente 265

144 public void registerAppServer(String appServerName)


145 throws RemoteException;
146
147
148 /**
149 * Registriert einen Applikations-Server mit BenchMachine
150 * Objekt.
151 * @param appServerName Der Name des Applikations-Servers.
152 * @param hostname Der Rechnername des zu überwachenden
153 * BenchMachine Objektes.
154 * @param port Die Addresse der RMI-Registry des
155 * BenchMachine Objektes.
156 * @param regName Der Name, unter dem das BenchMachine
157 * Objekt auf dem entfernten Rechner registriert ist.
158 */
159 public void registerAppServer(String appServerName,
160 String hostname, String port,
161 String regName) throws RemoteException;
162
163
164 /**
165 * Entfernt einen Applikations-Server.
166 * @param appServerName Der Name des Applikations-Servers.
167 */
168 public void unregisterAppServer(String
169 appServerName) throws RemoteException;
170
171
172 /**
173 * Registriert einen Dienst für einen bestimmten
174 * Applikations-Server.
175 * @param appServerName Der Name des Applikations-Servers.
176 * @param serviceName Der Name des Dienstes.
177 * @param lookupHost Der Name des Rechners, unter dem der
178 * Dienst abgelegt ist.
179 * @param minSpeed Die minimale Geschwindigkeit, die für
180 * das Ausführen dieses Dienstes notwendig ist.
181 * @param lookupPort Der Port, unter dem der Dienst auf
182 * dem entfernten Rechner abgelegt ist.
183 * @param lookupName Der Name, unter dem das Objekt auf
184 * dem entfernten Rechner abgelegt ist.
185 * @param type Der Typ des Dienstes
186 */
187 public void registerAppServerService(String appServerName,
188 String serviceName, double minSpeed, String lookupHost,
Quellcode der Beobachter Komponente 266

189 int lookupPort, String lookupName, int type)


190 throws RemoteException;
191
192
193 /**
194 * Entfernt einen Dienst eines Applikations-Servers.
195 * @param appServerName Der Name des Applikations-Servers.
196 * @param serviceName Der Name des Dienstes.
197 */
198 public void unregisterAppServerService(String serviceName,
199 String appServerName) throws RemoteException;
200
201
202 /**
203 * Prüft, ob ein Applikations-Servers verfügbar ist. Ein
204 * Applikations-Server ist verfügbar, wenn das
205 * BenchMachine Objekt angesprochen werden kann.
206 * Unterstüzt der Applikations-Server kein BenchMachine
207 * Objekt, wird false zurückgeliefert.
208 * @param appServerName Der Name des Applikations-Servers.
209 * @return True, wenn der Applikations-Server verfügbar
210 * ist.
211 */
212 public boolean isAvailable(String appServerName)
213 throws RemoteException;
214
215
216 /**
217 * Prüft, ob ein Applikations-Server verfügbar ist. Ein
218 * Applikations-Server ist verfügbar, wenn das
219 * BenchMachine Objekt angesprochen werden kann.
220 * Unterstüzt der Applikations-Server kein BenchMachine
221 * Objekt, wird false zurückgeliefert.
222 * @param appS Das ObserverAppServer Objekt des
223 * Applikations-Servers.
224 * @return True, wenn der Applikations-Server verfügbar
225 * ist.
226 */
227 public boolean isAvailable(ObserverAppServer
228 appS) throws RemoteException;
229
230
231 /**
232 * Prüft, ob der übergebene Applikations-Server bereits
233 * registriert ist.
Quellcode der Beobachter Komponente 267

234 * @param appServerName Der Name des Applikations-Servers.


235 * @return True, wenn der Applikations-Server registriert
236 * ist.
237 */
238 public boolean isRegistered(String appServerName)
239 throws RemoteException;
240
241
242 /**
243 * Gibt das Intervall der Geschwindigkeitsprüfung zurück.
244 * @return Das Intervall der Geschwindigkeitsprüfung.
245 */
246 public long getCheckSpeedInterval() throws RemoteException;
247
248
249 /**
250 * Gibt das Intervall des lookups für angebotene Dienste
251 * zurück.
252 * @return Das Intervall des lookups für angebotene
253 * Dienste.
254 */
255 public long getLookupServiceInterval()
256 throws RemoteException;
257
258
259 /**
260 * Gibt das Intervall des lookups für nicht verfügbare
261 * ObsererAppServer Objekte zurück. Nicht zur Verfügung
262 * stehend bedeutet, dass keine Verbindung zum BenchServer
263 * Objekt aufgebaut werden kann.
264 * @return Das Intervall des lookups für nicht verfügbare
265 * ObsererAppServer Objekte.
266 */
267 public long getReconnectBenchInterval()
268 throws RemoteException;
269
270
271 /**
272 * Gibt die Zeit, die ein checkMachineSpeed() Aufruf
273 * benötigen darf, zurück.
274 * @return Zeit, die ein checkMachineSpeed() Aufruf
275 * benötigen darf.
276 */
277 public long getMaxCheckSpeedTime() throws RemoteException;
278
Quellcode der Beobachter Komponente 268

279
280 /**
281 * Gibt die Zeit, die ein lookupService() Aufruf benötigen
282 * darf, zurück.
283 * @return Zeit, die ein lookupService() Aufruf benötigen
284 * darf.
285 */
286 public long getMaxServiceLookupTime()
287 throws RemoteException;
288
289
290 /**
291 * Gibt die Zeit, die ein lookupAppServer() Aufruf
292 * benötigen darf, zurück.
293 * @return Zeit, die ein lookupAppServer() Aufruf
294 * benötigen darf.
295 */
296 public long getMaxBenchLookupTime() throws RemoteException;
297
298
299 /**
300 * Setzt das Intervall der Geschwindigkeitsprüfung.
301 * Der Standardwert beträgt 5000 ms (Millisekunden).
302 * @param checkSpeedInterval Das neue Intervall.
303 */
304 public void setCheckSpeedInterval(long checkSpeedInterval)
305 throws RemoteException;
306
307
308 /**
309 * Setzt das Intervall des lookups für angebotene Dienste.
310 * Es werden alle angebotene Dienste überprüft. Selbst die
311 * Dienste eines Applikations-Servers, dessen BenchMachine
312 * Objekt nicht mehr verfügbar ist. Der Standardwert
313 * beträgt 10000 ms (Millisekunden)
314 * @param serviceInterval Das neue Intervall.
315 */
316 public void setLookupServiceInterval(long serviceInterval)
317 throws RemoteException;
318
319
320 /**
321 * Setzt das Intervall des lookups für nicht verfügbare
322 * ObsererAppServer Objekte. Nicht zur Verfügung stehend
323 * bedeutet, dass keine Verbindung zum BenchServer Objekt
Quellcode der Beobachter Komponente 269

324 * aufgebaut werden kann. Der Standardwert beträgt


325 * 12000 ms (Millisekunden)
326 * @param interv Das neue Intervall.
327 */
328 public void setReconnectBenchInterval(long interv)
329 throws RemoteException;
330
331
332 /**
333 * Setzt die Zeit, die ein checkMachineSpeed() Aufruf
334 * benötigen darf. Wenn ein Aufruf schneller erledigt ist,
335 * wird direkt mit der Verarbeitung fortgefahren. Der
336 * Standardwert beträgt 200 ms (Millisekunden).
337 * @param maxCheckSpeedTime Die neue Zeit.
338 */
339 public void setMaxCheckSpeedTime(long maxCheckSpeedTime)
340 throws RemoteException;
341
342
343 /**
344 * Setzt die Zeit, die ein lookupService() Aufruf
345 * benötigen darf. Wenn ein Aufruf schneller erledigt ist,
346 * wird direkt mit der Verarbeitung fortgefahren. Der
347 * Standardwert beträgt 200 ms (Millisekunden).
348 * @param maxLookupTime Die neue Zeit.
349 */
350 public void setMaxServiceLookupTime(long maxLookupTime)
351 throws RemoteException;
352
353
354 /**
355 * Setzt die Zeit, die ein lookupAppServer() Aufruf
356 * benötigen darf. Wenn ein Aufruf schneller erledigt ist
357 * wird direkt mit der Verarbeitung fortgesetzt. Der
358 * Standardwert beträgt 200 ms (Millisekunden).
359 * @param maxLookupTime Die neue Zeit.
360 */
361 public void setMaxReconnectLookupTime(long maxLookupTime)
362 throws RemoteException;
363
364 /**
365 * Fügt einen Interessenten eines bestimmten Applikations-
366 * Servers hinzu. Alle Interessenten eines Applikations-
367 * Servers werden benachrichtigt, wenn bestimmte
368 * Ereignisse eintreten.
Quellcode der Beobachter Komponente 270

369 * @param appServerName Der Name des Applikations-Server.


370 * @param l Das IAppServerListener Objekt des
371 * Interessenten.
372 */
373 public void addAppServerListener(String appServerName,
374 IAppServerListener l) throws RemoteException;
375
376
377 /**
378 * Löscht einen Interessenten eines bestimmten
379 * Applikations-Server.
380 * @param appServerName Der Name des Applikations-Servers.
381 * @param l Das zu löschende IAppServerListener Objekt.
382 */
383 public void removeHostMachineListener(String
384 appServerName, IAppServerListener l)
385 throws RemoteException;
386
387
388 /**
389 * Fügt einen Interessenten eines bestimmten Dienstes
390 * hinzu. Alle Interessenten eines Dienstes werden
391 * benachrichtigt, wenn Ereignisse des Dienstes
392 * eintreten.
393 * @param appServerName Der Name des Applikations-Servers.
394 * @param serviceName Der Name des Dienstes.
395 * @param l Das IAppServerListener Objekt des
396 * Interessenten.
397 */
398 public void addServiceListener(String appServerName, String
399 serviceName, IServiceListener l) throws RemoteException;
400
401
402 /**
403 * Löscht einen Interessenten eines bestimmten Dienstes.
404 * @param appServerName Der Name des Applikations-Servers.
405 * @param serviceName Der Name des Dienstes.
406 * @param l Das IAppServerListener Objekt des
407 * Interessenten.
408 */
409 public void removeServiceListener(String
410 appServerName, String serviceName, IServiceListener l)
411 throws RemoteException;
412 }
Quellcode der Beobachter Komponente 271

B.14. Quellcode der Klasse Observer

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: Observer
9 * Date: 14.02.2003
10 * Version: 1.2
11 */
12
13 package observer;
14
15 import java.util.*;
16 import java.rmi.*;
17 import java.net.*;
18 import java.rmi.registry.*;
19 import java.io.*;
20 import javax.naming.*;
21 import java.rmi.server.*;
22
23
24 /**
25 * Dies ist die Implementation der IObserver Schnittstelle.
26 * Die Dokumentation der einzelnen public Methoden ist aus
27 * der IObserver Schnittstelle zu entnehmen. Der Observer
28 * benötigt zum Start eine Konfigurationsdatei die
29 * den Namen:
30 * Observer .properties
31 * tragen muss. Die folgenden Keywords müssen definiert
32 * sein:
33 * OBSERVER_REG_NAME
34 * OBSERVER_REG_HOSTNAME
35 * OBSERVER_REG_PORT
36 * VERBOSE
37 * SHOWEXCEPTION
38 * CHECK_SPEED_INTERVAL
39 * LOOKUP_SERVICE_INTERVAL
40 * RECONNECT_BENCH_INTERVAL
41 * MAX_CHECK_SPEED_TIME
42 * MAX_SERVICE_LOOKUP_TIME
43 * MAX_BENCH_LOOKUP_TIME
Quellcode der Beobachter Komponente 272

44 */
45 public class Observer extends UnicastRemoteObject
46 implements IObserver {
47
48 /*
49 **********************************************************
50 * Atribute des Observers
51 **********************************************************
52 */
53
54 /**
55 * Das Intervall der Geschwindigkeitsprüfung.
56 * Der Standardwert beträgt 5000 ms (Millisekunden).
57 */
58 private long checkSpeedInterval = 5000;
59
60 /**
61 * Timer zum Ausgeben der Geschwindigkeiten auf dem
62 * Standardausgang. Dieser Timer benutzt
63 * checkSpeedTimerTask.
64 */
65 private Timer checkSpeedTimer;
66
67 /**
68 * Mit Hilfe dieses Threads wird die Geschwindigkeits-
69 * prüfung durchgeführt. Die Prüfung durchläuft die Liste
70 * aller zur Verfügung stehenden ObserverAppServer
71 * Objekte und versucht die aktuelle Geschwindigkeit
72 * zu ermitteln. Diese Geschwindigkeit wird im
73 * entsprechenden ObserverBenchMachine Objekt hinterlegt.
74 */
75 private TimerTask checkSpeedTimerTask;
76
77 /**
78 * Das Intervall des lookups für angebotene Dienste. Es
79 * werden alle angebotene Dienste überprüft. Selbst die
80 * Dienste eines Applikations-Servers, dessen BenchMachine
81 * Objekt nicht mehr verfügbar ist. Der Standardwert
82 * beträgt 10000 ms (Millisekunden)
83 */
84 private long lookupServiceInterval = 10000;
85
86 /**
87 * Timer, der versucht eine Verbindung zu den Diensten
88 * aufzubauen. Dieser Timer benutzt
Quellcode der Beobachter Komponente 273

89 * lookupServiceTimerTask.
90 */
91 private Timer lookupServiceTimer;
92
93 /**
94 * Mit Hilfe dieses Threads wird geprüft, ob wieder ein
95 * Zugriff auf einen Dienst Objekt möglich ist.
96 * Die Prüfung durchläuft die Liste aller angebotenen
97 * Dienste und versucht mit Hilfe der Atttribute der
98 * entsprechenden Dienste (ObserverService) eine
99 * Verbindung aufzubauen. Das Attribut
100 * maxServiceLookupTime spezifiziert das Timeout für die
101 * lookup..() Methode. War der Lookup in dieser Zeit nicht
102 * erfolgreich, wird das Attribut available des Dienstes
103 * auf false gesetzt. Der Thread für den Lookup wird nicht
104 * gestoppt. Es ist also möglich, dass der Dienst auf
105 * nicht verfügbar gesetzt wird, obwohl der Dienst zur
106 * Verfügung steht. Dies kann passieren, wenn das
107 * Attribut maxServiceLookupTime zu niedrig gewählt ist.
108 */
109 private TimerTask lookupServiceTimerTask;
110
111 /**
112 * Das Intervall des lookups für nicht verfügbare
113 * ObsererAppServer Objekte. Nicht zur Verfügung stehend
114 * bedeutet, dass keine Verbindung zum BenchServer Objekt
115 * aufgebaut werden kann. Der Standardwert beträgt 12000
116 * ms (Millisekunden)
117 */
118 private long reconnectBenchInterval = 12000;
119
120 /**
121 * Timer der versucht eine Verbindung zu nicht verfügbaren
122 * ObsererAppServer Objekten wiederherzustellen.
123 * Dieser Timer benutzt reconnectBenchTimerTask.
124 */
125 private Timer reconnectBenchTimer;
126
127 /**
128 * Mit Hilfe dieses Threads wird geprüft, ob wieder ein
129 * Zugriff auf das BenchServer Objekt möglich ist.
130 * Die Prüfung durchläuft die Liste aller nicht zur
131 * Verfügung stehenden ObserverAppServer Objekte und
132 * versucht mit Hilfe der Atttribute des entsprechenden
133 * ObserBenchMachine Objektes wieder eine Verbindung
Quellcode der Beobachter Komponente 274

134 * aufzubauen. Das Attribut maxBenchLookupTime


135 * spezifiziert das Timeout für die Lookup() Methode.
136 * War der Lookup in dieser Zeit nicht erfolgreich, wird
137 * das ObserverAppServer Objekt in die nicht zur Verfügung
138 * stehenden ObserverAppServer Liste geschrieben bzw.
139 * behalten. Der Thread für den Lookup wird nicht
140 * gestoppt. Es ist also möglich, dass das
141 * ObserverAppServer Objekt in die nicht zur Verfügung
142 * stehenden ObserverAppServer Liste eingetragen wird,
143 * obwohl das BenchMachine Objekt zur Verfügung steht.
144 * Dies kann passieren, wenn das Attribut
145 * maxBenchLookupTime zu niedrig gewählt ist.
146 */
147 private TimerTask reconnectBenchTimerTask;
148
149 /**
150 * Zeit, die ein checkMachineSpeed() Aufruf benötigen
151 * darf. Wenn ein Aufruf schneller erledigt ist, wird
152 * direkt mit der Verarbeitung fortgesetzt. Der
153 * Standardwert beträgt 200 ms (Millisekunden).
154 */
155 private long maxCheckSpeedTime = 200;
156
157 /**
158 * Zeit, die ein lookupService() Aufruf benötigen darf.
159 * Wenn ein Aufruf schneller erledigt ist, wird direkt mit
160 * der Verarbeitung fortgesetzt. Der Standardwert beträgt
161 * 600 ms (Millisekunden).
162 */
163 private long maxServiceLookupTime =600;
164
165 /**
166 * Zeit, die ein lookupAppServer() Aufruf benötigen darf.
167 * Wenn ein Aufruf schneller erledigt ist, wird direkt mit
168 * der Verarbeitung fortgesetzt. Der Standardwert beträgt
169 * 400 ms (Millisekunden).
170 */
171 private long maxBenchLookupTime =400;
172
173 /**
174 * Flag, das angibt, ob Informationen auf dem Standard -
175 * Ausgang ausgegeben werden. Der Standardwert ist false.
176 */
177 private boolean verbose = false;
178
Quellcode der Beobachter Komponente 275

179 /**
180 * Flag, das angibt ob Exceptions auf dem Standard -
181 * Ausgang ausgegeben werden. Der Standardwert ist false.
182 */
183 private boolean showException = false;
184
185 /**
186 * Speichert alle registrierten Dienste. Der Schlüssel
187 * dieser HashMap ist der Name des Applikations-Servers und
188 * des Dienstes. Der Wert ist das ObserverService Objekt.
189 */
190 private HashMap services;
191
192 /**
193 * Speichert alle nicht zur Verfügung stehenden
194 * ObsererAppServer Objekte. Nicht zur Verfügung stehend
195 * bedeutet, dass keine Verbindung zum BenchServer Objekt
196 * aufgebaut werden kann. Es werden nur ObsererAppServer
197 * Objekt gespeichert, die auch einen BenchServer
198 * bereitstellen. Der Schlüssel dieser HashMap ist der
199 * Name des Applikations-Server und der Wert das
200 * entsprechende ObserverAppServer Objekt.
201 */
202 private HashMap notAvailableAppServer;
203
204 /**
205 * Speichert alle zur Verfügung stehenden ObsererAppServer
206 * Objekte. Zur Verfügung stehend bedeutet, dass eine
207 * Verbindung zum BenchServer Objekt aufgebaut werden
208 * kann. Es werden nur ObsererAppServer Objekte
209 * gespeichert die auch einen BenchServer bereitstellen.
210 * Der Schlüssel dieser HashMap ist der Name des
211 * Applikations-Server und der Wert das entsprechende
212 * ObserverAppServer Objekt.
213 */
214 private HashMap availableAppServer;
215
216 /**
217 * Speichert alle registrieten ObsererAppServer Objekte.
218 * Der Schlüssel dieser HashMap ist der Name des
219 * Applikations-Server und der Wert das entsprechende
220 * ObserverAppServer Objekt.
221 */
222 private HashMap allAppServer;
223
Quellcode der Beobachter Komponente 276

224 /**
225 * Association zwischen einem ObserverService Objekt
226 * (Dienst) und ObsererAppServer Objekten
227 * (Applikations Server). Der Schlüssel dieser HashMap ist
228 * der Name des ObserverService Objektes und der Wert ist
229 * eine Menge von ObsererAppServer Objekten.
230 */
231 private HashMap mapServiceAppServer;
232
233 /**
234 * Association zwischen einem ObsererAppServer Objekt
235 * (Applikation Server) und ObserverService Objekten
236 * (Diensten). Der Schlüssel dieser HashMap ist der Name
237 * des ObsererAppServer Objektes und der Wert ist eine
238 * Menge von ObserverService Objekten.
239 */
240 private HashMap mapAppServerServices;
241
242 /**
243 * Speichert zu jedem Applikations-Server eine Liste von
244 * Interessenten. Diese Interessenten werden
245 * benachrichtigt, wenn ein Ereignis des Applikations-
246 * Server eintritt.
247 */
248 private HashMap appServerListeners = new HashMap();
249
250 /**
251 * Speichert zu jedem Dienst eine Liste von Interessenten.
252 * Diese Interessenten werden benachrichtigt wenn ein
253 * Ereignis des entsprechenden Dienstes eintritt.
254 */
255 private HashMap serviceListeners = new HashMap();
256
257 /**
258 * Der Name dieses Observer Objektes.
259 */
260 private String observerName = "";
261
262
263 /*
264 **********************************************************
265 * Konstruktoren und Initialisierung des Observers
266 **********************************************************
267 */
268
Quellcode der Beobachter Komponente 277

269 /** Erzeugt ein neues Observer Objekt. */


270 public Observer() throws RemoteException {
271
272 init();
273 }
274
275
276 /**
277 * Inititialisieren des Observers. Es werden die
278 * Behälter der Attribute erzeugt und die Timer
279 * checkSpeedTimer, lookupServiceTimer und
280 * reconnectBenchTimer gestartet.
281 */
282 private void init() throws RemoteException {
283
284 availableAppServer = new HashMap();
285 notAvailableAppServer = new HashMap();
286 allAppServer = new HashMap();
287
288 services = new HashMap();
289
290 mapAppServerServices = new HashMap();
291 mapServiceAppServer = new HashMap();
292
293 setCheckSpeedInterval(checkSpeedInterval);
294 setReconnectBenchInterval(reconnectBenchInterval);
295 setLookupServiceInterval(lookupServiceInterval);
296 }
297
298 /*
299 **********************************************************
300 * Implementation der IObserver Schnittstelle
301 **********************************************************
302 */
303
304
305 public IBenchMachine getBenchMachine(String appServerName)
306 throws RemoteException {
307
308 ObserverAppServer appS = (ObserverAppServer)
309 availableAppServer.get(appServerName);
310 if (appS.getObserverBenchMachine() == null){
311 return null;
312 }else{
313 if (appS.getObserverBenchMachine().getBenchMachine()
Quellcode der Beobachter Komponente 278

314 == null){
315 return null;
316 }else{
317 return appS.getObserverBenchMachine().
318 getBenchMachine();
319 }
320 }
321 }
322
323
324 public ObserverAppServer getObserverAppServer(String
325 appServerName) throws RemoteException{
326
327 return getAppServer(appServerName);
328 }
329
330
331 public Collection getAppServerForService(String service)
332 throws RemoteException {
333
334 HashSet hash =(HashSet) mapServiceAppServer.get(service);
335 return (hash == null)? null : (Collection) hash.clone();
336 }
337
338
339 public Collection getAvailableAppServerForService(String
340 service) throws RemoteException {
341
342 HashSet hash =(HashSet) mapServiceAppServer.get(service);
343 if (hash == null) {
344 return null;
345 }else{
346 HashSet avail = new HashSet();
347 Iterator iter = hash.iterator();
348 while (iter.hasNext()){
349 ObserverAppServer appS = (ObserverAppServer)
350 iter.next();
351 ObserverService ser = (ObserverService)
352 services.get(appS.getName()+service);
353 if (ser.isAvailable()){
354 if (appS.getObserverBenchMachine()==null){
355 avail.add(appS);
356 }else{
357 ObserverBenchMachine bm =
358 appS.getObserverBenchMachine();
Quellcode der Beobachter Komponente 279

359
360 if ( (bm.getLastSpeed()!=Double.NaN)&&
361 (bm.getLastSpeed()>ser.getMinSpeed())) {
362 avail.add(appS);
363 }
364 }
365 }
366 }
367 return (Collection) avail.clone();
368 }
369 }
370
371
372 public Collection getServices(String appServerName) throws
373 RemoteException {
374
375 HashSet hash = (HashSet) mapAppServerServices.
376 get(appServerName);
377 return (hash == null)? null : (Collection) hash.clone();
378 }
379
380
381 public synchronized HashMap getAvailableAppServer()
382 throws RemoteException {
383
384 return (HashMap) availableAppServer.clone();
385 }
386
387
388 public synchronized HashMap getNotAvailableAppServer()
389 throws RemoteException {
390
391 return (HashMap) notAvailableAppServer.clone();
392 }
393
394
395 public synchronized HashMap getAllAppServer()
396 throws RemoteException {
397
398 return (HashMap) allAppServer.clone();
399 }
400
401
402 public synchronized HashMap getServices()
403 throws RemoteException {
Quellcode der Beobachter Komponente 280

404
405 return (HashMap) services.clone();
406 }
407
408
409 public void registerAppServer(String appServerName)
410 throws RemoteException {
411
412 if (! allAppServer.containsKey(appServerName)){
413 ObserverAppServer appS = new ObserverAppServer(
414 appServerName);
415 log("Neuer Applikations-Server "+
416 "registriert : "+ appServerName + "\n");
417
418 allAppServer.put(appServerName,appS);
419 mapAppServerServices.put(appS.getName(),new HashSet());
420 }
421 }
422
423
424 public void registerAppServer(String appServerName,
425 String hostname, String port,
426 String regName) throws RemoteException {
427
428 if (! allAppServer.containsKey(appServerName)){
429 ObserverAppServer appS = new ObserverAppServer(
430 appServerName, hostname, port, regName);
431 log("Neuer Applikations-Server mit BenchMachine "+
432 "registriert : "+ appServerName +
433 "\nRechnername der Registry = " + hostname +
434 "\nPort der Registry = " + port +
435 "\nName in der Registry = " + regName +"\n");
436
437 availableAppServer.put(appServerName, appS);
438 allAppServer.put(appServerName,appS);
439
440 lookupAppServer(appS);
441 mapAppServerServices.put(appS.getName(),new HashSet());
442 }
443 }
444
445
446 public synchronized void unregisterAppServer(String
447 appServerName) throws RemoteException {
448
Quellcode der Beobachter Komponente 281

449 if (! isRegistered(appServerName)) {
450 throw new IllegalArgumentException("Applikations-"+
451 "Server " + appServerName + " existiert nicht");
452 }
453
454 log("Applikations-Server " + appServerName +
455 " wird entfernt\n");
456 ObserverAppServer appS = null;
457 try {
458 Collection services = getServices(appServerName);
459 Iterator iter = services.iterator();
460 appS = getAppServer(appServerName);
461 availableAppServer.remove(appServerName);
462 notAvailableAppServer.remove(appServerName);
463 allAppServer.remove(appServerName);
464 while (iter.hasNext()) {
465 ObserverService s = (ObserverService) iter.next();
466 Set set = (Set) mapServiceAppServer.get(s.getName());
467 set.remove(appS);
468 services.remove(appServerName+s.getName());
469 }
470 } catch (RemoteException exc) {
471 log(exc);
472 }
473 mapAppServerServices.remove(appServerName);
474 }
475
476
477 public void registerAppServerService(String appServerName,
478 String serviceName, double minSpeed, String lookupHost,
479 int lookupPort, String lookupName, int type)
480 throws RemoteException {
481
482 if (!services.containsKey(appServerName+serviceName)){
483 log("Applikations-Server: "+ appServerName +
484 " <--- Dienst "+serviceName+"\n");
485
486 HashSet set = (HashSet) mapAppServerServices.get(
487 appServerName);
488 if (set == null) {
489 throw new NoSuchElementException("Applikations-"+
490 "Server existiert nicht");
491 }
492 ObserverService os = new ObserverService(serviceName,
493 type, minSpeed, lookupHost, lookupPort, lookupName);
Quellcode der Beobachter Komponente 282

494 //Dienst dem Applikations-Server hinzufügen


495 services.put(appServerName+serviceName,os);
496 set.add(os);
497
498 //Applikations-Server dem Dienst hinzufügen
499 if (!mapServiceAppServer.containsKey(serviceName)){
500 mapServiceAppServer.put(serviceName,new HashSet());
501 }
502 set = (HashSet) mapServiceAppServer.get(serviceName);
503 ObserverAppServer appS = getAppServer(appServerName);
504 if (appS == null) {
505 throw new NoSuchElementException("Applikations-"+
506 "Server existiert nicht");
507 }
508 synchronized (set) {
509 set.add(appS);
510 }
511 }
512 }
513
514
515 public void unregisterAppServerService(String serviceName,
516 String appServerName) throws RemoteException {
517
518 HashSet set = (HashSet) mapAppServerServices.get(
519 appServerName);
520 if (services.containsKey(appServerName+serviceName)) {
521 Object service = services.get(appServerName +
522 serviceName);
523 synchronized(set) {
524 set.remove(service);
525 }
526 services.remove(appServerName+serviceName);
527 }
528
529 set = (HashSet) mapServiceAppServer.get(serviceName);
530 ObserverAppServer appS = getAppServer(appServerName);
531 synchronized(set) {
532 set.remove(appS);
533 }
534 log(" Applikations-Server: "+ appServerName +
535 " ---> Dienst "+serviceName+"\n");
536 }
537
538
Quellcode der Beobachter Komponente 283

539 public synchronized boolean isAvailable(String


540 appServerName) throws RemoteException {
541
542 return availableAppServer.containsKey(appServerName);
543 }
544
545
546 public synchronized boolean isAvailable(ObserverAppServer
547 appS) throws RemoteException {
548
549 return availableAppServer.containsKey(appS.getName());
550 }
551
552
553 public synchronized boolean isRegistered(String
554 appServerName) throws RemoteException {
555
556 return getAppServer(appServerName) != null;
557 }
558
559
560 private void setCheckSpeedTimer() throws RemoteException {
561
562 if (checkSpeedTimer != null) checkSpeedTimer.cancel();
563 checkSpeedTimer = new Timer(true);
564 checkSpeedTimerTask = new TimerTask() {
565 public void run() {
566 try {
567 Iterator iter = getAvailableAppServer().values().
568 iterator();
569 while (iter.hasNext()) {
570 ObserverAppServer appS = (ObserverAppServer)
571 iter.next();
572 double speed = checkAppServerSpeed(appS);
573 log("Geschwindigkeit des Applikations-" +
574 "Server "+appS+ " :\t\t");
575 log(((Double.isNaN(speed))? "NaN" : (""+speed)));
576 log("\n");
577 }
578 } catch (RemoteException exc) {
579 log(exc);
580 }
581 }
582 };
583 checkSpeedTimer.schedule(checkSpeedTimerTask,
Quellcode der Beobachter Komponente 284

584 checkSpeedInterval, checkSpeedInterval);


585 }
586
587
588 private void setLookupServiceTimer()throws RemoteException{
589
590 lookupServiceTimer = new Timer();
591 lookupServiceTimerTask = new TimerTask() {
592 public void run() {
593 try {
594 Iterator iter = getServices().keySet().iterator();
595
596 while (iter.hasNext()) {
597 String appServerName = (String) iter.next();
598 ObserverService service = (ObserverService)
599 services.get(appServerName);
600
601 appServerName=appServerName.substring(0,
602 appServerName.length()-service.getName().
603 length());
604 log("Lookup Dienst : " + service +
605 "\nApplikations-Server : "+
606 appServerName + "\n");
607
608 boolean succeeded = lookupService(appServerName,
609 service);
610 log( (succeeded)? "erfolgreich" : "gescheitert");
611 log("\n");
612 }
613 } catch (RemoteException exc) {
614 log(exc);
615 }
616 }
617 };
618 lookupServiceTimer.schedule(lookupServiceTimerTask,
619 lookupServiceInterval, lookupServiceInterval);
620 }
621
622
623 private void setReconnectBenchTimer() throws
624 RemoteException{
625 reconnectBenchTimer = new Timer();
626 reconnectBenchTimerTask = new TimerTask() {
627 public void run() {
628 try {
Quellcode der Beobachter Komponente 285

629 Iterator iter =


630 getNotAvailableAppServer().values().iterator();
631 while (iter.hasNext()) {
632 ObserverAppServer appS = (ObserverAppServer)
633 iter.next();
634 log("Lookup ApplikationsServer: " +
635 appS + " ... \n");
636 boolean succeeded = lookupAppServer(appS);
637 log( (succeeded)? "erfolgreich" : "gescheitert");
638 log("\n");
639 }
640 } catch (RemoteException exc) {
641 log(exc);
642 }
643 }
644 };
645 reconnectBenchTimer.schedule(reconnectBenchTimerTask,
646 reconnectBenchInterval, reconnectBenchInterval);
647 }
648
649
650 public long getCheckSpeedInterval()throws RemoteException {
651
652 return checkSpeedInterval;
653 }
654
655
656 public long getLookupServiceInterval()
657 throws RemoteException {
658
659 return lookupServiceInterval;
660 }
661
662
663 public long getReconnectBenchInterval()
664 throws RemoteException {
665
666 return this.reconnectBenchInterval;
667 }
668
669
670 public long getMaxCheckSpeedTime() throws RemoteException {
671
672 return maxCheckSpeedTime;
673 }
Quellcode der Beobachter Komponente 286

674
675
676 public long getMaxServiceLookupTime()
677 throws RemoteException {
678
679 return maxServiceLookupTime;
680 }
681
682
683 public long getMaxBenchLookupTime()throws RemoteException {
684
685 return maxBenchLookupTime;
686 }
687
688
689 public void setCheckSpeedInterval(long checkSpeedInterval)
690 throws RemoteException {
691
692 this.checkSpeedInterval = checkSpeedInterval;
693 setCheckSpeedTimer();
694 }
695
696
697 public void setLookupServiceInterval(long serviceInterval)
698 throws RemoteException {
699
700 this.lookupServiceInterval = serviceInterval;
701 setLookupServiceTimer();
702 }
703
704
705 public void setReconnectBenchInterval(long interv)
706 throws RemoteException {
707
708 this.reconnectBenchInterval = interv;
709 setReconnectBenchTimer();
710 }
711
712
713 public void setMaxCheckSpeedTime(long maxCheckSpeedTime)
714 throws RemoteException {
715
716 this.maxCheckSpeedTime = maxCheckSpeedTime;
717 }
718
Quellcode der Beobachter Komponente 287

719
720 public void setMaxServiceLookupTime(long maxLookupTime)
721 throws RemoteException {
722
723 this.maxServiceLookupTime = maxLookupTime;
724 }
725
726
727 public void setMaxReconnectLookupTime(long maxLookupTime)
728 throws RemoteException {
729
730 this.maxBenchLookupTime = maxLookupTime;
731 }
732
733
734 public synchronized void addAppServerListener(String
735 appServerName, IAppServerListener l)
736 throws RemoteException {
737
738 LinkedList list = (LinkedList)
739 appServerListeners.get(appServerName);
740 if (list == null) {
741 list = new LinkedList();
742 appServerListeners.put(appServerName, list);
743 }
744 list.add(l);
745 }
746
747
748 public void removeHostMachineListener(String
749 appServerName, IAppServerListener l)
750 throws RemoteException {
751
752 LinkedList list = (LinkedList)
753 appServerListeners.get(appServerName);
754 if (list != null) {
755 list.remove(l);
756 }
757 }
758
759
760 public synchronized void addServiceListener(String
761 appServerName, String serviceName, IServiceListener l)
762 throws RemoteException {
763
Quellcode der Beobachter Komponente 288

764 LinkedList list = (LinkedList)


765 serviceListeners.get(appServerName+serviceName);
766 if (list == null) {
767 list = new LinkedList();
768 String key = appServerName+serviceName;
769 serviceListeners.put(key, list);
770 }
771 list.add(l);
772 }
773
774
775 public void removeServiceListener(String
776 appServerName, String serviceName, IServiceListener l)
777 throws RemoteException {
778
779 LinkedList list = (LinkedList)
780 serviceListeners.get(appServerName+serviceName);
781 if (list != null) {
782 list.remove(l);
783 }
784 }
785
786
787 /*
788 **********************************************************
789 * Private Methoden des Observers
790 **********************************************************
791 */
792
793
794 /**
795 * Erzeugt ein AppServerEvent und schickt es an die
796 * entsprechenden Interessenten. Da die Interessenten
797 * selbst alle auf entfernten Rechnern liegen könnten,
798 * wird der Vorgang in einem separaten Thread
799 * abgearbeitet.
800 * @param appS Das ObserverAppServer Objekt des
801 * Applikations-Servers.
802 * @param type Der Typ des Ereignisses.
803 */
804 private void fireAppServerEvent(final ObserverAppServer
805 appS, final int type) {
806
807 LinkedList list = (LinkedList) appServerListeners.get(
808 appS.getName());
Quellcode der Beobachter Komponente 289

809 if (list != null) {


810 for (int i=0; i<list.size(); i++) {
811 final IAppServerListener l =
812 (IAppServerListener) list.get(i);
813
814 Runnable runner = new Runnable() {
815 public void run() {
816 try {
817 l.notify(new AppServerEvent(observerName,
818 appS.getName(), type));
819 } catch(Throwable t) {
820 log("Ein Listener des Applikations-Servers "+
821 appS.getName() +" konnte nicht benachrichtigt"+
822 " werden\n");
823 log(t);
824 } }
825 };
826
827 Thread t = new Thread(runner);
828 t.setDaemon(true);
829 t.start();
830 }
831 }
832 }
833
834
835 /**
836 * Erzeugt ein ServiceEvent und schickt es an die
837 * entsprechenden Interessenten. Da die Interessenten
838 * selbst alle auf entfernten Rechnern liegen könnten,
839 * wird der Vorgang in einem separaten Thread
840 * abgearbeitet.
841 * @param appS Das ObserverAppServer Objekt des
842 * Applikations-Servers.
843 * @param service Das ObserverService Objekt des Dienstes.
844 * @param type Der Typ des Ereignisses.
845 */
846 private void fireServiceEvent(final ObserverAppServer
847 appS, final ObserverService service, final int type) {
848
849 //System.out.println(
850 // "FIRE service :" + service.getName()+ " =" + type );
851 String key = appS.getName()+service.getName();
852 LinkedList list = (LinkedList) serviceListeners.get(key);
853
Quellcode der Beobachter Komponente 290

854 if (list != null) {


855 for (int i=0; i<list.size(); i++) {
856 final IServiceListener l =
857 (IServiceListener) list.get(i);
858
859 Runnable runner = new Runnable() {
860 public void run() {
861 try {
862 if (type == ServiceEvent.DISABLED){
863 synchronized(service){;
864 if (!service.isAvailable()){
865 l.notify(new ServiceEvent(observerName,
866 appS.getName(), service.getName(),type));
867 }
868 }
869 }else{
870 l.notify(new ServiceEvent(observerName,
871 appS.getName(), service.getName(),type));
872 }
873 } catch(Throwable t) {
874 log("Ein Listener des Dienst :"+
875 service.getName() +"\n" +
876 "Applikation-Server :" + appS.getName() + "\n"+
877 "konnte nicht benachrichtigt werden\n");
878 log(t);
879 } }
880 };
881
882 Thread t = new Thread(runner);
883 t.setDaemon(true);
884 t.start();
885 }
886 }
887 }
888
889
890 /**
891 * Ausgabe einer Fehlermeldung auf den Standard-Ausgang.
892 * Die Ausgabe erfolgt nur, wenn das verbose Flag gesetzt
893 * ist.
894 * @param t Das Throwable Objekt.
895 */
896 private void log(Throwable t) {
897
898 if (showException) {
Quellcode der Beobachter Komponente 291

899 t.printStackTrace();
900 }
901 }
902
903
904 /**
905 * Ausgabe eines Strings auf den Standard-Ausgang.
906 * Die Ausgabe erfolgt nur, wenn das verbose Flag gesetzt
907 * ist.
908 * @param s Der String, der ausgegeben werden soll.
909 */
910 private void log(String s) {
911
912 if (verbose) {
913 System.out.print(s);
914 }
915 }
916
917
918 /**
919 * Macht einen Lookup auf einen RMI Dienst. Wenn der
920 * Lookup erfolgreich ist, wird das Attribut available des
921 * Dienstes (ObserverService) auf true gesetzt. Falls der
922 * Lookup scheitert (durch eine RemoteExcecption), wird
923 * das Attribut auf false gesetzt.
924 * @param appServerName Der Name des Applikations-Servers
925 * des entsprechenden Dienstes.
926 * @throws MalformedURLException Wenn die URL, die mit
927 * Hilfe der Attribute des ObserverService Objektes
928 * erstellt wird, ungültig ist.
929 * @throws NotBoundException Wenn der Dienst nicht unter
930 * dem angegeben Namen (ObserverService)in der RMI-
931 * Registry registriert ist.
932 */
933 private boolean lookupRMI(final String appServerName,
934 final ObserverService service)
935 throws MalformedURLException, NotBoundException {
936
937 try {
938 Object obj = Naming.lookup("rmi://"+service.
939 getLookupHost() + ":" + service.getLookupPort() +"/" +
940 service.getLookupName());
941
942 ObserverAppServer appS= getAppServer(appServerName);
943 ObserverBenchMachine bm=appS.getObserverBenchMachine();
Quellcode der Beobachter Komponente 292

944
945 // Disable -> Enable,Overload
946 if (!service.isAvailable()){
947 service.setAvailable(true);
948 if ((bm!=null) && ( (bm.getLastSpeed()==Double.NaN)||
949 (bm.getLastSpeed()<=
950 service.getMinSpeed()))){
951 fireServiceEvent(appS, service,
952 ServiceEvent.OVERLOAD);
953 service.setOverload(true);
954 }else{
955 fireServiceEvent(appS, service,
956 ServiceEvent.ENABLED);
957 service.setOverload(false);
958 }
959 }
960 // Enable -> Overload
961 if ((bm!=null) && ( (bm.getLastSpeed()==Double.NaN) ||
962 (bm.getLastSpeed()<=
963 service.getMinSpeed()))){
964 if (!service.isOverload()){
965 fireServiceEvent(appS, service,
966 ServiceEvent.OVERLOAD);
967 service.setOverload(true);
968 }
969 }
970 // Overload -> Enable
971 if (service.isOverload()){
972 if ((bm!=null) &&
973 (bm.getLastSpeed()!=Double.NaN)&&
974 (bm.getLastSpeed()>=service.getMinSpeed())){
975 service.setOverload(false);
976 fireServiceEvent(appS, service,
977 ServiceEvent.ENABLED);
978 }
979 }
980 return true;
981 } catch (RemoteException exc) {
982 log(exc);
983 // Enable -> Disable
984 if (service.isAvailable()){
985 ObserverAppServer appS= getAppServer(appServerName);
986 service.setAvailable(false);
987 fireServiceEvent(appS,service,ServiceEvent.DISABLED);
988 }
Quellcode der Beobachter Komponente 293

989 }
990 return false;
991 }
992
993
994 /**
995 * Macht einen Lookup auf einen EJB Dienst. Wenn der
996 * Lookup erfolgreich ist, wird das Attribut available des
997 * Dienstes (ObserverService) auf true gesetzt. Falls der
998 * Lookup scheitert (durch eine RemoteExcecption), wird
999 * das Attribut auf false gesetzt.
1000 * @param appServerName Der Name des Applikations-Servers
1001 * des entsprechenden Dienstes.
1002 * @throws MalformedURLException Wenn die URL, die mit
1003 * Hilfe der Attribute des ObserverService Objektes
1004 * erstellt wird, ungültig ist.
1005 * @throws NotBoundException Wenn der Dienst nicht unter
1006 * dem angegeben Namen (ObserverService) registriert ist.
1007 */
1008 private boolean lookupEJB(final String appServerName,
1009 final ObserverService service)
1010 throws MalformedURLException, NotBoundException {
1011
1012 Context initial = null;
1013 boolean successful=false;
1014 try{
1015 if (service.getServiceType().getType() ==
1016 ServiceType.EJB_SUN){
1017
1018 Properties env = new Properties();
1019 env.put("java.naming.factory.initial",
1020 "com.sun.jndi.cosnaming.CNCtxFactory");
1021 env.put("java.naming.provider.url","iiop://"+
1022 service.getLookupHost()+":"+service.getLookupPort());
1023 initial = new InitialContext(env);
1024 }else
1025 if (service.getServiceType().getType() ==
1026 ServiceType.EJB_JBOSS){
1027 Hashtable p = new Hashtable();
1028 p.put(Context.INITIAL_CONTEXT_FACTORY,
1029 "org.jnp.interfaces.NamingContextFactory");
1030 p.put(Context.PROVIDER_URL,service.getLookupHost() +
1031 ":" + service.getLookupPort());
1032 p.put(Context.URL_PKG_PREFIXES,
1033 "org.jboss.naming:org.jnp.interfaces");
Quellcode der Beobachter Komponente 294

1034 p.put("jnp.socketFactory",
1035 "org.jnp.interfaces.TimedSocketFactory");
1036 p.put("jnp.timeout","0");
1037 p.put("jnp.sotimeout","0");
1038 initial = new InitialContext(p);
1039 }
1040 Object objref =initial.lookup(service.getLookupName());
1041 successful=true;
1042
1043 } catch (CommunicationException e){
1044 if (e.getRootCause()instanceof ClassNotFoundException){
1045 successful=true;
1046 }else{
1047 log(e);
1048 if (service.isAvailable()){
1049 ObserverAppServer appS= getAppServer(appServerName);
1050 service.setAvailable(false);
1051 fireServiceEvent(appS,service,ServiceEvent.DISABLED);
1052 }
1053 }
1054 } catch (NamingException exc) {
1055 log(exc);
1056 log(exc);
1057 // Enable -> Disable
1058 if (service.isAvailable()){
1059 ObserverAppServer appS= getAppServer(appServerName);
1060 service.setAvailable(false);
1061 fireServiceEvent(appS,service,ServiceEvent.DISABLED);
1062 }
1063 }
1064 if (successful){
1065 ObserverAppServer appS= getAppServer(appServerName);
1066 ObserverBenchMachine bm=appS.getObserverBenchMachine();
1067
1068 // Disable -> Enable,Overload
1069 if (!service.isAvailable()){
1070 service.setAvailable(true);
1071 if ((bm!=null) && ( (bm.getLastSpeed()==Double.NaN)||
1072 (bm.getLastSpeed()<=
1073 service.getMinSpeed()))){
1074 fireServiceEvent(appS, service,
1075 ServiceEvent.OVERLOAD);
1076 service.setOverload(true);
1077 }else{
1078 fireServiceEvent(appS, service,
Quellcode der Beobachter Komponente 295

1079 ServiceEvent.ENABLED);
1080 service.setOverload(false);
1081 }
1082 }
1083 // Enable -> Overload
1084 if ((bm!=null) && ( (bm.getLastSpeed()==Double.NaN) ||
1085 (bm.getLastSpeed()<=
1086 service.getMinSpeed()))){
1087 if (!service.isOverload()){
1088 fireServiceEvent(appS, service,
1089 ServiceEvent.OVERLOAD);
1090 service.setOverload(true);
1091 }
1092 }
1093 // Overload -> Enable
1094 if (service.isOverload()){
1095 if ((bm!=null) &&
1096 (bm.getLastSpeed()!=Double.NaN)&&
1097 (bm.getLastSpeed()>=service.getMinSpeed())){
1098 service.setOverload(false);
1099 fireServiceEvent(appS, service,
1100 ServiceEvent.ENABLED);
1101 }
1102 }
1103 return true;
1104 }else{
1105 return false;
1106 }
1107 }
1108
1109
1110 /**
1111 * Macht einen Lookup auf ein BenchMachine Objekt. Wenn
1112 * der Lookup erfolgreich ist, wird der Applikations-
1113 * Server in die verfügbare Liste eingetragen. Falls der
1114 * Lookup scheitert (durch eine RemoteExcecption), wird
1115 * der Applikations-Server in die nicht verfügbare Liste
1116 * eingetragen.
1117 * @param appS Das ObserverAppServer Objekt mit den
1118 * Attributen des zu suchenden BenchMachine Objektes:
1119 * @throws MalformedURLException Wenn die URL, die mit
1120 * Hilfe der Attribute des ObserverBenchMachine Objektes
1121 * erstellt wird, ungültig ist.
1122 * @throws NotBoundException Wenn das BenchMachine Objekt
1123 * nicht unter dem angegeben Namen (ObserverBenchMachine)
Quellcode der Beobachter Komponente 296

1124 * in der RMI-Registry registriert ist.


1125 */
1126 private boolean lookupBench(ObserverAppServer appS) throws
1127 MalformedURLException, NotBoundException {
1128
1129 try {
1130 ObserverBenchMachine bm=appS.getObserverBenchMachine();
1131 IBenchMachine m = (IBenchMachine) Naming.lookup(
1132 "rmi://"+ bm.host +":"+ bm.regPort +"/"+ bm.regName);
1133 m.getAverageSpeed();
1134 enableAppServer(appS, m);
1135 return true;
1136 } catch (RemoteException exc) {
1137 log(exc);
1138 disableAppServer(appS);
1139 }
1140 return false;
1141 }
1142
1143
1144 /**
1145 * Bewegt ein Applikations-Server Objekt von der
1146 * verfügbaren zur nicht verfügbaren Liste.
1147 * @param appS Das ObserverAppServer Objekt des
1148 * Applikations-Servers.
1149 */
1150 private synchronized void disableAppServer(
1151 ObserverAppServer appS) {
1152
1153 if (availableAppServer.containsKey(appS.getName())) {
1154 availableAppServer.remove(appS.getName());
1155 log("Stelle Applikations-Server " + appS +" in die L"+
1156 "iste,\nder nicht verfügbaren BenchMachine Objekte\n");
1157 appS.getObserverBenchMachine().setBenchMachine(null);
1158 appS.getObserverBenchMachine().
1159 setLastSpeed(Double.NaN);
1160 notAvailableAppServer.put(appS.getName(), appS);
1161 fireAppServerEvent(appS, AppServerEvent.DISABLED);
1162 } else {
1163 //log("Konnte Applikations-Server " + appS.getName() +
1164 //" nicht ausschalten, da er entweder schon "+
1165 //"ausgeschaltet ist oder bereits entfernt wurde\n");
1166 }
1167 }
1168
Quellcode der Beobachter Komponente 297

1169
1170 /**
1171 * Bewegt ein Applikations-Server Objekt von der
1172 * verfügbaren zur nicht verfügbaren Liste.
1173 * @param appS Das ObserverAppServer Objekt des
1174 * Applikations-Servers.
1175 * @param bm Die Referenz zum BenchMachine Objekt.
1176 */
1177 private synchronized void enableAppServer(
1178 ObserverAppServer appS, IBenchMachine bm) {
1179
1180 appS.getObserverBenchMachine().setBenchMachine(bm);
1181 if (notAvailableAppServer.containsKey(appS.getName())) {
1182 notAvailableAppServer.remove(appS.getName());
1183
1184 log("Stelle Applikations-Server " + appS+" in die "+
1185 "Liste,\nder verfügbaren BenchMachine Objekte\n");
1186 availableAppServer.put(appS.getName(), appS);
1187 checkAppServerSpeed(appS);
1188 fireAppServerEvent(appS, AppServerEvent.ENABLED);
1189 } else {
1190 //log("Konnte Applikations-Server " + appS.getName() +
1191 //" nicht wieder einsetzen, da er entweder schon "+
1192 //"im Einsatz ist oder bereits entfernt wurde\n");
1193 }
1194 }
1195
1196
1197 /**
1198 * Führt einen überwachten Lookup durch. Diese Methode
1199 * ruft die lookupEJB() bzw. lookupRMI() Methode je nach
1200 * Typ des Dienstes auf. Falls ein Fehler auftaucht oder
1201 * in der spezifizierten Zeit keine Rückmeldung erfolgt,
1202 * wird der Service auf nicht verfügbar gesetzt und false
1203 * zurückgeliefert. Der Thread zum Testen wird nicht ge-
1204 * stoppt. Das kann dazu führen, dass false zurückliefert
1205 * wird, obwohl der Dienst verfügbar ist. Es bedeutet
1206 * lediglich, dass der aktuelle Lookup nicht in der
1207 * spezifizierten Zeit erfolgreich war.
1208 * @param appS Das ObserverAppServer Objekt des
1209 * Applikations-Servers.
1210 * @param service Das ObserverService Objekt des zu
1211 * untersuchenden Dienstes.
1212 * @return true Wenn der Lookup erfolgreich war.
1213 */
Quellcode der Beobachter Komponente 298

1214 private boolean lookupService(final String appServerName,


1215 final ObserverService service) {
1216
1217 if (service.isChecking()) {
1218 return false;
1219 }
1220
1221 Runnable checker = new Runnable() {
1222 public void run() {
1223 service.setChecking(true);
1224 try {
1225 if (service.getServiceType().getType()==
1226 ServiceType.RMI){
1227 lookupRMI(appServerName,service);
1228 }else{
1229 lookupEJB(appServerName,service);
1230 }
1231 } catch (Exception exc) {
1232 log(exc);
1233 // enable - disable
1234 if (service.isAvailable()){
1235 ObserverAppServer appS= getAppServer(
1236 appServerName);
1237 service.setAvailable(false);
1238 fireServiceEvent(appS,service,
1239 ServiceEvent.DISABLED);
1240 }
1241 } finally {
1242 service.setChecking(false);
1243 }
1244 }
1245 };
1246
1247 Thread checkThread = new Thread(checker);
1248 checkThread.setDaemon(true);
1249 checkThread.start();
1250
1251 try {
1252 checkThread.join(maxServiceLookupTime);
1253 if (checkThread.isAlive()) {
1254 if (service.isAvailable()){
1255 ObserverAppServer appS= getAppServer(
1256 appServerName);
1257 service.setAvailable(false);
1258 fireServiceEvent(appS,service,
Quellcode der Beobachter Komponente 299

1259 ServiceEvent.DISABLED);
1260 }
1261 }
1262 } catch (InterruptedException exc) {
1263
1264 }
1265 return service.isAvailable();
1266 }
1267
1268
1269 /**
1270 * Führt einen überwachten Lookup durch. Diese Methode
1271 * ruft die lookupBench() Methode auf. Falls ein Fehler
1272 * auftaucht, wird der Applikations-Server in die nicht
1273 * verfügbare Liste eingetragen. Wenn diese Methode false
1274 * zurückliefert, bedeutet das nicht automatisch, dass der
1275 * Applikations-Server nicht verfügbar ist. Es bedeutet
1276 * lediglich, dass der aktuelle Lookup nicht in der
1277 * spezifizierten Zeit erfolgreich war.
1278 * @param appS Das ObserverAppServer Objekt des
1279 * Applikations-Servers.
1280 * @return true Wenn der Lookup erfolgreich war.
1281 */
1282 private boolean lookupAppServer(final
1283 ObserverAppServer appS) {
1284
1285 final ObserverBenchMachine bm =
1286 appS.getObserverBenchMachine();
1287 if (bm.isChecking()) {
1288 return false;
1289 }
1290
1291 Runnable checker = new Runnable() {
1292 public void run() {
1293 bm.setChecking(true);
1294 try {
1295 lookupBench(appS);
1296 } catch (Exception exc) {
1297 log(exc);
1298 disableAppServer(appS);
1299 } finally {
1300 bm.setChecking(false);
1301 }
1302 }
1303 };
Quellcode der Beobachter Komponente 300

1304
1305 Thread checkThread = new Thread(checker);
1306 checkThread.setDaemon(true);
1307 checkThread.start();
1308
1309 try {
1310 checkThread.join(maxBenchLookupTime);
1311 if (checkThread.isAlive()) {
1312 disableAppServer(appS);
1313 }
1314 } catch (InterruptedException exc) {
1315
1316 }
1317
1318 return appS.getObserverBenchMachine().getBenchMachine()
1319 != null;
1320 }
1321
1322
1323 /**
1324 * Prüft die Geschwindigkeit des entfernten Rechners und
1325 * gibt diese zurück. Zudem wird die Geschwindigkeit im
1326 * Attribut lastSpeed des BenchMachine Objektes abgelegt.
1327 * Es wird NaN zurückgeliefert, wenn die aktuelle
1328 * Geschwindigkeit nicht in der spezifizierten
1329 * Geschwindigkeit (maxCheckSpeedTime) ermittelt werden
1330 * konnte. Konnte die Geschwindigkeit nicht ermittelt
1331 * werden, wird der Applikations-Server in die nicht
1332 * verfügbare Liste übernommen. Der Thread zur Ermittlung
1333 * der Geschwindigkeit wird nicht gestoppt. Versucht
1334 * bereits ein anderer Thread die Geschwindigkeit zu
1335 * ermitteln, wird keine entfernte Methode aufgerufen und
1336 * NaN zurückgeliefert.
1337 * @param appS Das ObserverAppServer Objekt des
1338 * Applikations-Servers.
1339 * @return Die aktuelle Geschwindigkeit des Applikations-
1340 * Servers.
1341 */
1342 private double checkAppServerSpeed(final
1343 ObserverAppServer appS) {
1344
1345 final ObserverBenchMachine bm =
1346 appS.getObserverBenchMachine();
1347 if (bm.isChecking()) {
1348 return Double.NaN;
Quellcode der Beobachter Komponente 301

1349 }
1350
1351 Runnable checker = new Runnable() {
1352 public void run() {
1353 bm.setChecking(true);
1354 try {
1355 double speed = appS.getObserverBenchMachine().
1356 getBenchMachine().getAverageSpeed();
1357 appS.getObserverBenchMachine().setLastSpeed(speed);
1358 } catch (RemoteException exc) {
1359 log(exc);
1360 disableAppServer(appS);
1361 } catch (NullPointerException exc) {
1362 // Könnte auftreten wenn ein anderer Thread das
1363 // ObserverAppServer in die nicht verfügbare Liste
1364 // einträgt.
1365 log("Konnte Geschwindigkeit nicht messen da kein"+
1366 " BenchMachine Objekt für Applikations-Server "+
1367 appS.getName() +" zur Verfügung steht\n");
1368 disableAppServer(appS);
1369 } finally {
1370 bm.setChecking(false);
1371 }
1372 }
1373 };
1374
1375 Thread checkThread = new Thread(checker);
1376 checkThread.setDaemon(true);
1377 checkThread.start();
1378
1379 try {
1380 checkThread.join(maxCheckSpeedTime);
1381 checkThread.interrupt();
1382 } catch (InterruptedException exc) {
1383
1384 }
1385 double r = appS.getObserverBenchMachine().getLastSpeed();
1386
1387 if (checkThread.isAlive()) {
1388 r = Double.NaN;
1389 disableAppServer(appS);
1390 }
1391 return r;
1392 }
1393
Quellcode der Beobachter Komponente 302

1394
1395 /**
1396 * Diese Methode liefert das ObserverAppServer Objekt
1397 * eines Applikations-Servers zurück.
1398 * @param appServerName Der Name des Applikations-Servers.
1399 * @return Das ObserverAppServer Objekt des Applikations-
1400 * Servers.
1401 */
1402 private ObserverAppServer getAppServer(String
1403 appServerName) {
1404
1405 ObserverAppServer appS = (ObserverAppServer)
1406 allAppServer.get(appServerName);
1407 return appS;
1408 }
1409
1410
1411 /*
1412 **********************************************************
1413 * main() Methode
1414 **********************************************************
1415 */
1416
1417 /**
1418 * Initialisert das Observer Objekt mit den Daten aus
1419 * der KonfigurationsDatei (Observer.properties).
1420 */
1421 private void mainInitProperties(){
1422
1423 ResourceBundle rb = null;
1424 String observerRegName ="";
1425 String observerRegHost ="";
1426 int observerRegPort =0;
1427
1428 /*
1429 * Daten aus der Konfigurationsdatei lesen. Sind nicht
1430 * alle Daten angegeben, wird die Initialisierung
1431 * abgebrochen.
1432 */
1433 try {
1434 rb = ResourceBundle.getBundle("Observer");
1435 observerRegName =rb.getString("OBSERVER_REG_NAME");
1436 observerRegHost =rb.getString("OBSERVER_REG_HOSTNAME");
1437
1438 verbose =((rb.getString("VERBOSE").equalsIgnoreCase(
Quellcode der Beobachter Komponente 303

1439 "true"))?true:false);
1440 showException =((rb.getString("SHOWEXCEPTION").
1441 equalsIgnoreCase("true"))?true:false);
1442
1443 try {
1444 observerRegPort = Integer.parseInt(rb.getString(
1445 "OBSERVER_REG_PORT"));
1446 } catch (NumberFormatException exc) {
1447 System.out.println("Konnte den Wert für das Feld "+
1448 "OBSERVER_REG_PORT nicht in einen Integer casten. "+
1449 "Es wird der Standardport von "+observerRegPort+
1450 " verwendet");
1451 }
1452
1453 try {
1454 checkSpeedInterval = Long.parseLong(rb.getString(
1455 "CHECK_SPEED_INTERVAL"));
1456 } catch (NumberFormatException exc) {
1457 System.out.println("Konnte den Wert für das Feld "+
1458 "CHECK_SPEED_INTERVAL nicht in einen Long casten. "+
1459 "Es wird der Standardwert von "+checkSpeedInterval +
1460 "ms verwendet");
1461 }
1462
1463 try {
1464 lookupServiceInterval = Long.parseLong(rb.getString(
1465 "LOOKUP_SERVICE_INTERVAL"));
1466 } catch (NumberFormatException exc) {
1467 System.out.println("Konnte den Wert für das Feld "+
1468 "LOOKUP_SERVICE_INTERVAL nicht in einen Long casten"+
1469 ". Es wird der Standardwert von "+
1470 lookupServiceInterval +" ms verwendet");
1471 }
1472
1473 try {
1474 reconnectBenchInterval = Long.parseLong(rb.getString(
1475 "RECONNECT_BENCH_INTERVAL"));
1476 } catch (NumberFormatException exc) {
1477 System.out.println("Konnte den Wert für das Feld "+
1478 "RECONNECT_BENCH_INTERVAL nicht in einen Long "+
1479 "casten. Es wird der Standardwert von "+
1480 reconnectBenchInterval +" ms verwendet");
1481 }
1482
1483 try {
Quellcode der Beobachter Komponente 304

1484 maxCheckSpeedTime = Long.parseLong(rb.getString(


1485 "MAX_CHECK_SPEED_TIME"));
1486 } catch (NumberFormatException exc) {
1487 System.out.println("Konnte den Wert für das Feld "+
1488 "MAX_CHECK_SPEED_TIME nicht in einen Long "+
1489 "casten. Es wird der Standardwert von "+
1490 maxCheckSpeedTime +" ms verwendet");
1491 }
1492
1493 try {
1494 maxServiceLookupTime = Long.parseLong(rb.getString(
1495 "MAX_SERVICE_LOOKUP_TIME"));
1496 } catch (NumberFormatException exc) {
1497 System.out.println("Konnte den Wert für das Feld "+
1498 "MAX_SERVICE_LOOKUP_TIME nicht in einen Long "+
1499 "casten. Es wird der Standardwert von "+
1500 maxServiceLookupTime +" ms verwendet");
1501 }
1502
1503 try {
1504 maxBenchLookupTime = Long.parseLong(rb.getString(
1505 "MAX_BENCH_LOOKUP_TIME"));
1506 } catch (NumberFormatException exc) {
1507 System.out.println("Konnte den Wert für das Feld "+
1508 "MAX_BENCH_LOOKUP_TIME nicht in einen Long "+
1509 "casten. Es wird der Standardwert von "+
1510 maxBenchLookupTime +" ms verwendet");
1511 }
1512
1513 } catch(MissingResourceException exc) {
1514 System.out.println(exc);
1515 System.out.println("Konfigurationsdatei konnte nicht"+
1516 " gefunden werden");
1517 System.out.println("Observer angehalten");
1518 System.exit(-1);
1519 }
1520
1521 try{
1522
1523 /*
1524 * Neue RMI-Registry erzeugen, falls noch keine
1525 * gestartet wurde
1526 */
1527 Registry registry = LocateRegistry.getRegistry(
1528 observerRegPort);
Quellcode der Beobachter Komponente 305

1529 try {
1530 registry.list();
1531 } catch (RemoteException exc) {
1532 LocateRegistry.createRegistry(observerRegPort);
1533 System.err.println("Neue RMI-Registry auf Port " +
1534 observerRegPort + " gestartet");
1535 }
1536 } catch (RemoteException exc) {
1537 exc.printStackTrace();
1538 }
1539 try{
1540 Naming.rebind("rmi://"+observerRegHost+":"+
1541 observerRegPort+"/"+observerRegName, this);
1542 System.out.println("--------------------------------");
1543 System.out.println("Der Observer wurde unter dem ");
1544 System.out.println("Namen : " + observerRegName);
1545 System.out.println("in der folgenden RMI-Registry "+
1546 "registriert.");
1547 System.out.println("Rechner: "+observerRegHost);
1548 System.out.println("Port : "+observerRegPort);
1549 System.out.println("--------------------------------");
1550 System.out.print ("checkSpeedInterval :\t\t");
1551 System.out.println(checkSpeedInterval);
1552 System.out.print ("lookupServiceInterval :\t\t");
1553 System.out.println(lookupServiceInterval);
1554 System.out.print ("reconnectBenchInterval :\t");
1555 System.out.println(reconnectBenchInterval);
1556 System.out.print ("maxCheckSpeedTime :\t\t");
1557 System.out.println(maxCheckSpeedTime);
1558 System.out.print ("maxServiceLookupTime: \t\t");
1559 System.out.println(maxServiceLookupTime);
1560 System.out.print ("maxBenchLookupTime :\t\t");
1561 System.out.println(maxBenchLookupTime);
1562 System.out.print ("showException :\t\t\t");
1563 System.out.println(showException);
1564 System.out.print ("verbose :\t\t\t");
1565 System.out.println(verbose);
1566 System.out.println("--------------------------------");
1567 System.out.println("Observer erfolgreich "+
1568 "initialisiert");
1569 System.out.println("--------------------------------");
1570 observerName = "OBSERVER:"+ observerRegHost;
1571 } catch (Exception exc) {
1572 System.out.println(exc);
1573 System.out.println("Während der Registrierung trat "+
Quellcode der Beobachter Komponente 306

1574 "ein Fehler auf");


1575 System.out.println("Observer angehalten!");
1576 System.exit(-1);
1577 }
1578 }
1579
1580 /**
1581 * Startet den Observer.
1582 */
1583 public static void main(String[] args) throws Exception{
1584 Observer obs=null;
1585 BufferedReader in = null;
1586
1587 obs = new Observer();
1588 obs.mainInitProperties();
1589
1590 System.out.println("Der Befehl <help> zeigt "+
1591 "Optionen und Interaktionen !");
1592 in =new BufferedReader(new InputStreamReader(System.in));
1593 String input = null;
1594 do {
1595 input = in.readLine();
1596 // Speed
1597 if (input.startsWith("speed")) {
1598 String appServerName = input.substring(5).trim();
1599 Object[] appS = new Object[0];
1600 if (appServerName.equals("")) {
1601 appS = obs.getAvailableAppServer().
1602 keySet().toArray();
1603 } else {
1604 appS = new Object[1];
1605 appS[0] = appServerName;
1606 }
1607
1608 StringBuffer sb = new StringBuffer();
1609 sb.append("********************* Geschwindig"+
1610 "keit-Tabelle ********************\n");
1611 for (int i=0; i<appS.length; i++) {
1612 ObserverAppServer oAS = obs.
1613 getAppServer((String)appS[i]);
1614 if (oAS == null){
1615 sb.append("Applikations-Server "+ appS[i] +
1616 " existiert nicht\n");
1617 }else{
1618 if(oAS.getObserverBenchMachine()==null){
Quellcode der Beobachter Komponente 307

1619 sb.append("Applikations-Server "+ appS[i] +


1620 " unterstützt kein BenchMachine Objekt.\n");
1621 }else{
1622 double speed = oAS.getObserverBenchMachine().
1623 getLastSpeed();
1624 sb.append(appS[i] + "\t\t" + speed + "\n");
1625 }
1626 }
1627 }
1628 sb.append("***************************************"+
1629 "***************************************\n");
1630 System.out.println(sb);
1631 } else
1632
1633 // appS
1634 if (input.equalsIgnoreCase("appS")) {
1635 Object[] appS = obs.getAllAppServer().
1636 values().toArray();
1637 StringBuffer sb = new StringBuffer();
1638 sb.append("****************** registrierte "+
1639 "Applikations-Server ******************\n");
1640 for (int i=0; i<appS.length; i++) {
1641 sb.append(appS[i]+ "\n");
1642 }
1643 sb.append("***************************************"+
1644 "***************************************\n");
1645 System.out.println(sb);
1646 }else
1647
1648 // availAppS
1649 if (input.equalsIgnoreCase("availAppS")) {
1650 Object[] appS = obs.getAvailableAppServer().
1651 values().toArray();
1652 StringBuffer sb = new StringBuffer();
1653 sb.append("******************* verfügbare Appli"+
1654 "kations-Server *******************\n");
1655 for (int i=0; i<appS.length; i++) {
1656 sb.append(appS[i]+ "\n");
1657 }
1658 sb.append("****************************************"+
1659 "**************************************\n");
1660 System.out.println(sb);
1661 }else
1662
1663 // notAvailAppS
Quellcode der Beobachter Komponente 308

1664 if (input.equalsIgnoreCase("notAvailAppS")) {
1665 Object[] appS = obs.getNotAvailableAppServer().
1666 values().toArray();
1667 StringBuffer sb = new StringBuffer();
1668 sb.append("**************** nicht verfügbare "+
1669 "Applikations-Server ****************\n");
1670 for (int i=0; i<appS.length; i++) {
1671 sb.append(appS[i]+ "\n");
1672 }
1673 sb.append("****************************************"+
1674 "**************************************\n");
1675 System.out.println(sb);
1676 }else
1677
1678 // Services
1679 if (input.startsWith("services")) {
1680 String appServerName = input.substring(8).trim();
1681 Object[] appS = new Object[0];
1682 if (appServerName.equals("")) {
1683 appS = obs.getAllAppServer().values().toArray();
1684 } else {
1685 appS = new Object[1];
1686 appS[0] = appServerName;
1687 }
1688
1689 StringBuffer sb = new StringBuffer();
1690 sb.append("****************************** D"+
1691 "ienste ***************************\n");
1692 for (int i=0; i<appS.length; i++) {
1693 sb.append(appS[i] + "\n");
1694
1695 Collection c = obs.getServices(appS[i].toString());
1696 if ((c== null)||(c.size()==0)) {
1697 sb.append("\t kein registrierter Dienst oder Ap"+
1698 "plikations-Server nicht registriert.\n");
1699 continue;
1700 }
1701 Object s[] = c.toArray();
1702 for (int j=0; j<s.length; j++) {
1703 sb.append("\t" + s[j] + "\n");
1704 }
1705 }
1706 sb.append("****************************************"+
1707 "**************************************\n");
1708 System.out.println(sb);
Quellcode der Beobachter Komponente 309

1709 } else
1710
1711 // provService
1712 if (input.startsWith("provService")) {
1713 StringBuffer sb = new StringBuffer();
1714 String serviceName = input.substring(11).trim();
1715 Object[] serv = new Object[0];
1716 if (serviceName.equals("")) {
1717 serv = obs.mapServiceAppServer.keySet().toArray();
1718 } else {
1719 serv = new Object[1];
1720 serv[0] = serviceName;
1721 }
1722
1723 sb.append("********************** Dienst / Appl"+
1724 "ikations-Server *******************\n");
1725 for (int i=0; i<serv.length; i++) {
1726 sb.append(serv[i] + "\n");
1727
1728 HashSet set = (HashSet) obs.mapServiceAppServer.
1729 get(serv[i]);
1730
1731 if (set == null) {
1732 sb.append("\t kein registrierter Dienst\n");
1733 }else{
1734 Iterator iter = set.iterator();
1735 while(iter.hasNext()){
1736 ObserverAppServer appS = (ObserverAppServer)
1737 iter.next();
1738 sb.append("\t" + appS.toString()+ "\n");
1739 }
1740 }
1741 }
1742 sb.append("****************************************"+
1743 "***************************************\n");
1744 System.out.println(sb);
1745 }else
1746
1747 // provService
1748 if (input.startsWith("availService")) {
1749 StringBuffer sb = new StringBuffer();
1750 String serviceName = input.substring(12).trim();
1751 Object[] serv = new Object[0];
1752 if (serviceName.equals("")) {
1753 serv = obs.mapServiceAppServer.keySet().toArray();
Quellcode der Beobachter Komponente 310

1754 } else {
1755 serv = new Object[1];
1756 serv[0] = serviceName;
1757 }
1758
1759 sb.append("**************** Dienst / verfügbare "+
1760 "Applikations-Server ****************\n");
1761 for (int i=0; i<serv.length; i++) {
1762 sb.append(serv[i] + "\n");
1763
1764 Collection coll = obs.
1765 getAvailableAppServerForService((String)serv[i]);
1766 if (coll == null) {
1767 sb.append("\t kein registrierter Dienst\n");
1768 }else{
1769 Iterator iter = coll.iterator();
1770 while(iter.hasNext()){
1771 ObserverAppServer appS = (ObserverAppServer)
1772 iter.next();
1773 sb.append("\t" + appS.toString()+ "\n");
1774 }
1775 }
1776 }
1777 sb.append("****************************************"+
1778 "***************************************\n");
1779 System.out.println(sb);
1780 }else
1781
1782 // verbose
1783 if (input.equalsIgnoreCase("verbose")||(input.
1784 equalsIgnoreCase("-v"))) {
1785 obs.verbose = true;
1786 System.out.println("verbose Modus eingeschaltet.");
1787 } else
1788
1789 // noVerbose
1790 if (input.equalsIgnoreCase("noVerbose")||(input.
1791 equalsIgnoreCase("-noV"))) {
1792 obs.verbose = false;
1793 System.out.println("verbose Modus ausgeschaltet.");
1794 } else
1795
1796 // showExc
1797 if (input.equalsIgnoreCase("showExc")||(input.
1798 equalsIgnoreCase("-s"))) {
Quellcode der Beobachter Komponente 311

1799 obs.showException = true;


1800 System.out.println(
1801 "Fehlermeldungen werden angezeigt.");
1802 } else
1803
1804 // noShowExc
1805 if (input.equalsIgnoreCase("noShowExc")||(input.
1806 equalsIgnoreCase("-noS"))) {
1807 obs.showException = false;
1808 System.out.println("Fehlermeldungen werden nicht "+
1809 "angezeigt.");
1810 } else
1811
1812 //threads
1813 if (input.equalsIgnoreCase("threads")) {
1814 System.out.println("Momentan sind " +
1815 Thread.activeCount() + " Threads aktiv.");
1816 } else {
1817 help();
1818 }
1819
1820 } while(!(input.equalsIgnoreCase("quit") ||
1821 input.equalsIgnoreCase("exit")));
1822 System.exit(0);
1823 }
1824
1825 /**
1826 * Gibt die Hilfe auf dem Standardausgang aus.
1827 */
1828 private static void help() {
1829 System.out.println(
1830 "******************************************************"+
1831 "************************");
1832 System.out.println("Optionen :");
1833 System.out.println("-v, verbose " +
1834 "verbose Modus eingeschaltet.");
1835
1836 System.out.println("-noV, noVerbose " +
1837 "verbose Modus ausgeschaltet.");
1838
1839 System.out.println("-s, showExc " +
1840 "Fehlermeldungen werden nicht angezeigt,");
1841
1842 System.out.println("-noS, noShowExc " +
1843 "Fehlermeldungen werden angezeigt,");
Quellcode der Beobachter Komponente 312

1844
1845 System.out.println("h, help " +
1846 "zeigt diese Hilfe.");
1847
1848 System.out.println("\nInteraktionen :");
1849 System.out.println("exit, quit " +
1850 "zum Beenden des Programms.");
1851 System.out.println("speed [APPS] " +
1852 "zeigt die letzte Geschwindigkeit des Applikations-");
1853 System.out.println(" " +
1854 "Servers [APPS] an.");
1855 System.out.println("appS " +
1856 "zeigt alle registrierten Applikations-Server an");
1857 System.out.println("availAppS " +
1858 "zeigt alle verfügbaren Applikations-Server an.");
1859 System.out.println("notAvailAppS " +
1860 "zeigt alle nicht verfügbaren Applikations-Server an.");
1861 System.out.println(" [APPS] an.");
1862 System.out.println("services [APPS] " +
1863 "zeigt alle registrierten Dienste des Applikations-");
1864 System.out.println(" Server an.");
1865 System.out.println("provServices [NAME] " +
1866 "zeigt alle Applikations-Server, die den Dienst [NAME]");
1867 System.out.println(" "+
1868 "bereitstellen, an.");
1869 System.out.println("availService [NAME] " +
1870 "zeigt alle Applikations-Server, dessen Dienst [NAME]");
1871 System.out.println(" " +
1872 "verfügbar ist, an.");
1873 System.out.println("threads " +
1874 "zeigt die Anzahl der momentan verwendeten Threads.");
1875 System.out.println(
1876 "******************************************************"+
1877 "************************");
1878 }
1879 }

B.15. Beispiel für die Konfigurationsdatei des Observer

1 # ###########################################################
2 # Daten für den Observer
3 # ###########################################################
Quellcode der Beobachter Komponente 313

4
5 # ###########################################################
6 # Daten für die zu benutzende RMI REGISTRY
7 # ###########################################################
8
9 # Der Name unter dem das Observer Objekt in der RMI-
10 # Registry abgelegt werden soll.
11 OBSERVER_REG_NAME = ObserverObj
12
13 # Der Name, des Rechners, der die RMI-Registry bereitstellt,
14 # wo das Observer Objekt abgelegt werden soll. Dies wird im
15 # Allgemeinen localhost sein. Es ist aber möglich, dass das
16 # System die Variable localhost nicht kennt. In diesem Fall
17 # muss der Rechnername oder die IP Adresse den Rechners
18 # genutzt werden.
19 OBSERVER_REG_HOSTNAME = hope0
20
21 # Die Portadresse der genutzten RMI-Registry. Der
22 # Standardport der RMI-Registry ist 1099.
23 OBSERVER_REG_PORT = 1099
24
25 # ###########################################################
26 # Konfiguration des Observers
27 # ###########################################################
28
29 # Wahr bedeutet, dass alle Daten der Messungen auf den
30 # Standardausgang geschrieben werden. Der Standardwert ist
31 # false.
32 VERBOSE = false
33
34 # Wahr bedeutet, dass alle Exception auf den Standard-Error-
35 # Ausgang geschrieben werden. Der Standardwert ist false.
36 SHOWEXCEPTION = false
37
38 # Das Intervall der Geschwindigkeitsprüfung. Der
39 # Standardwert beträgt 5000 ms (Millisekunden).
40 CHECK_SPEED_INTERVAL = 5000
41
42 # Das Intervall des lookups für angebotene Dienste. Der
43 # Standardwert beträgt 10000 ms (Millisekunden)
44 LOOKUP_SERVICE_INTERVAL = 10000
45
46 # Das Intervall des lookups für nicht verfügbarer
47 # ObsererAppServer Objekte. Nicht zur Verfügung stehend
48 # bedeutet, dass keine Verbindung zum BenchServer Objekt
Quellcode der Beobachter Komponente 314

49 # aufgebaut werden kann. Der Standardwert beträgt 12000 ms


50 # (Millisekunden)
51 RECONNECT_BENCH_INTERVAL = 12000
52
53 # Zeit, die ein checkMachineSpeed() Aufruf benötigen darf.
54 # Wenn ein Aufruf schneller erledigt ist, wird direkt mit
55 # der Verarbeitung fortgesetzt. Der Standardwert beträgt
56 # 200 ms (Millisekunden).
57 MAX_CHECK_SPEED_TIME = 200
58
59 # Zeit, die ein lookupService() Aufruf benötigen darf.
60 # Wenn ein Aufruf schneller erledigt ist, wird direkt mit der
61 # Verarbeitung fortgesetzt. Der Standardwert beträgt 600 ms
62 # (Millisekunden).
63 MAX_SERVICE_LOOKUP_TIME = 1000
64
65 # Zeit, die ein lookupAppServer() Aufruf benötigen darf. Wenn
66 # ein Aufruf schneller erledigt ist, wird direkt mit der
67 # Verarbeitung fortgesetzt. Der Standardwert beträgt 400 ms
68 # (Millisekunden).
69 MAX_BENCH_LOOKUP_TIME = 400
Quellcode der Verwaltungs Server Komponente 315

C. Quellcode der Verwaltungs Server


Komponente

Die folgenden Quellcodes zeigen die konkrete Umsetzung des


Verwaltungs-Servers aus Kapitel 11.2. Der vollständige Quellcode kann auch
unter [34] heruntergeladen werden. Die einzelnen Methoden, Klassen und
Schnittstellen sind im Quellcode dokumentiert und werden hier nicht
erläutert.

C.1. Quellcode der Schnittstelle ServerAdminHome

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: ServerAdminHome
9 * Date: 13.03.2003
10 * Version: 1.0
11 */
12
13 package serverAdmin;
14
15 import javax.ejb.*;
16 import java.rmi.*;
17
18 /**
19 * Dies ist das home Interface für die ServerAdminBean. Alle
20 * Methoden, die hier deklariert sind, können später von
21 * einem remote Client aufgerufen werden.
22 */
23 public interface ServerAdminHome extends EJBHome {
24
25 public ServerAdmin create() throws CreateException,
26 RemoteException;
27 }
Quellcode der Verwaltungs Server Komponente 316

C.2. Quellcode der Schnittstelle ServerAdmin

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: ServerAdmin
9 * Date: 13.03.2003
10 * Version: 1.0
11 */
12
13 package serverAdmin;
14
15 import javax.ejb.*;
16 import java.rmi.*;
17 import java.util.*;
18 import serverAdmin.entity.*;
19 import serverAdmin.entity.clone.*;
20 import observer.*;
21
22 /**
23 * Dies ist das remote Interface für die ServerAdminBean.
24 * Alle Methoden, die hier deklariert sind, können später von
25 * einem remote Client aufgerufen werden.
26 */
27 public interface ServerAdmin extends EJBObject {
28
29
30 /**
31 * Erzeugt einen neuen Administrator in der Datenbank.
32 * @param admin Der Administrator, der eingelagert werden
33 * soll.
34 */
35 public void addAdministrator(AdministratorClone admin)
36 throws CreateException, RemoteException;
37
38
39 /**
40 * Entfernt einen Administrator aus der Datenbank. Ein
41 * Administrator wird über die ID eindeutig identifiziert.
42 * @param admin Der Administrator, der gelöscht werden
43 * soll.
Quellcode der Verwaltungs Server Komponente 317

44 */
45 public void removeAdministrator(AdministratorClone admin)
46 throws FinderException, RemoveException, RemoteException;
47
48
49 /**
50 * Ändert die Daten eines Administrators. Ein
51 * Administrator wird über die ID eindeutig identifiziert.
52 * @param admin Der Administrator, mit den geänderten
53 * Daten.
54 */
55 public void changeAdministrator(AdministratorClone admin)
56 throws FinderException, RemoteException;
57
58
59 /**
60 * Sucht nach einem Administrator, der als ID den
61 * übergebenen Parameter id besitzt.
62 * @param id ID (Primärschlüssel) nach der gesucht werden
63 * soll.
64 *
65 * @return Ein AdministratorClone Objekt, des
66 * Administrators.
67 */
68 public AdministratorClone getAdministratorByPrimaryKey(
69 long id) throws FinderException, RemoteException;
70
71
72 /**
73 * Liefert alle Datensätze der Administrator-Tabelle
74 * zurück.
75 * @return Alle Datensätze der Administrator-Tabelle.
76 */
77 public Collection getAllAdministrator()
78 throws FinderException, RemoteException;
79
80
81 /**
82 * Sucht nach Administrator Datensätzen, die den
83 * übergebenen Parameter name als Namen besitzen.
84 * @param name Der Name der Administratoren.
85 *
86 * @return Eine Liste von AdministratorClone Objekten.
87 */
88 public Collection getAdministratorByName(String name)
Quellcode der Verwaltungs Server Komponente 318

89 throws FinderException, RemoteException;


90
91
92 /**
93 * Sucht nach Administrator Datensätze, die den übergebenen
94 * Parameter eMail als E-Mail besitzen.
95 * @param eMail Die eMail Adresse der Administratoren.
96 *
97 * @return Eine Liste von AdministratorClone Objekten.
98 */
99 public Collection getAdministratorByEMail(String eMail)
100 throws FinderException, RemoteException;
101
102
103 /**
104 * Erzeugt einen neuen AppServerStatus in der Datenbank.
105 * @param status Der Status, der erzeugt werden
106 * soll.
107 */
108 public void addAppServerStatus(AppServerStatusClone status)
109 throws CreateException, RemoteException;
110
111
112 /**
113 * Entfernt einen AppServerStatus aus der Datenbank. Ein
114 * AppServerStatus wird über die ID eindeutig
115 * identifiziert.
116 * @param status Der AppServerStatus, der gelöscht werden
117 * soll.
118 */
119 public void removeAppServerStatus(AppServerStatusClone
120 status) throws FinderException, RemoveException,
121 RemoteException;
122
123
124 /**
125 * Ändert die Daten eines AppServerStatus. Ein
126 * AppServerStatus wird über die ID eindeutig
127 * identifiziert.
128 * @param status Der AppServerStatus, mit den geänderten
129 * Daten.
130 */
131 public void changeAppServerStatus(AppServerStatusClone
132 status) throws FinderException, RemoteException;
133
Quellcode der Verwaltungs Server Komponente 319

134
135 /**
136 * Sucht nach einem AppServerStatus, der als ID den
137 * übergebenen Parameter id besitzt.
138 * @param id ID (Primärschlüssel) nach der gesucht werden
139 * soll.
140 *
141 * @return Ein AppServerStatusClone Objekt, des
142 * Administrators.
143 */
144 public AppServerStatusClone getAppServerStatusByPrimaryKey(
145 int id) throws FinderException, RemoteException;
146
147
148 /**
149 * Liefert alle Datensätze der AppServerStatus-Tabelle
150 * zurück.
151 * @return Alle Datensätze der AppServerStatus-Tabelle.
152 */
153 public Collection getAllAppServerStatus()
154 throws FinderException, RemoteException;
155
156
157 /**
158 * Sucht nach AppServerStatus Datensätze, die als Bedeutung
159 * den übergebenen Parameter interpretation besitzen.
160 * @param interpretation Die Bedeutung des AppServerStatus.
161 *
162 * @return Eine Liste von AppServerStatusClone Objekten.
163 */
164 public Collection getAppServerStatusByInterpretation(
165 String interpretation) throws FinderException,
166 RemoteException;
167
168
169 /**
170 * Erzeugt ein neuen ApplicationServer in der Datenbank.
171 * @param appServer Der ApplicationServer, der erzeugt
172 * werden soll.
173 */
174 public void addApplicationServer(ApplicationServerClone
175 appServer) throws CreateException,RemoteException;
176
177
178 /**
Quellcode der Verwaltungs Server Komponente 320

179 * Entfernt einen ApplicationServer aus der Datenbank. Ein


180 * AppServerStatus wird über den Namen eindeutig
181 * identifiziert.
182 * @param appServer Der ApplicationServer, der gelöscht
183 * werden soll.
184 */
185 public void removeApplicationServer(ApplicationServerClone
186 appServer) throws FinderException, RemoveException,
187 RemoteException;
188
189
190 /**
191 * Ändert die Daten eines ApplicationServers. Ein
192 * AppServerStatus wird über den Namen eindeutig
193 * identifiziert.
194 * @param appServer Der ApplicationServer, mit den
195 * geänderten Daten.
196 */
197 public void changeApplicationServer(ApplicationServerClone
198 appServer) throws FinderException, RemoteException;
199
200
201 /**
202 * Sucht nach einem Applikations-Server, der den
203 * übergebenen Parameter name als Namen besitzt.
204 * @param name Name (Primärschlüssel), nach dem gesucht
205 * werden soll.
206 *
207 * @return Ein ApplicationServerClone Objekt, des
208 * Applikations-Servers.
209 */
210 public ApplicationServerClone
211 getApplicationServerByPrimaryKey(String name)
212 throws FinderException, RemoteException;
213
214
215 /**
216 * Liefert alle Datensätze der ApplilicationServer-Tabelle
217 * zurück.
218 * @return Alle Datensätze der ApplilicationServer-Tabelle.
219 */
220 public Collection getAllApplicationServer()
221 throws FinderException, RemoteException;
222
223
Quellcode der Verwaltungs Server Komponente 321

224 /**
225 * Sucht nach Applikations-Server, die den übergebenen
226 * Parameter id als Administrator besitzen.
227 * @param id Die ID des Administrators, nach dem gesucht
228 * werden soll.
229 *
230 * @return Eine Liste von ApplicationServerClone Objekten.
231 */
232 public Collection getApplicationServerByAdmin(long id)
233 throws FinderException, RemoteException;
234
235
236 /**
237 * Sucht nach einem Applikations-Server, der das
238 * Benchmark Objekt mit der ID id (Parameter) besitzt.
239 * @param id Die ID des Benchmark nach dem gesucht werden
240 * soll.
241 *
242 * @return Ein ApplicationServerClone Objekt des
243 * Applikations-Servers.
244
245 */
246 public ApplicationServerClone
247 getApplicationServerByBenchmark(long id)
248 throws FinderException, RemoteException;
249
250 /**
251 * Sucht nach Applikations-Servern, die den übergebenen
252 * Parameter host als Host besitzen.
253 * @param host Der Rechnername der Applikations-Server.
254 *
255 * @return Eine Liste von ApplicationServerClone Objekten.
256 */
257 public Collection getApplicationServerByHost(String host)
258 throws FinderException, RemoteException;
259
260
261 /**
262 * Sucht nach Applikations-Servern, die als Status
263 * den übergebenen Parameter id besitzen.
264 * @param id Die ID des Status, nach dem gesucht werden
265 * soll.
266 *
267 * @return Eine Liste von ApplicationServerClone Objekten.
268 */
Quellcode der Verwaltungs Server Komponente 322

269 public Collection getApplicationServerByStatus(int id)


270 throws FinderException, RemoteException;
271
272
273 /**
274 * Erzeugt ein neuen Benchmark in der Datenbank.
275 * @param bench Der Benchmark, der erzeugt werden soll.
276 */
277 public void addBenchmark(BenchmarkClone bench)
278 throws CreateException, RemoteException;
279
280
281 /**
282 * Entfernt einen Benchmark aus der Datenbank. Ein
283 * Benchmark wird über die ID eindeutig identifiziert.
284 * @param bench Der Benchmark, der gelöscht werden soll.
285 */
286 public void removeBenchmark(BenchmarkClone bench)
287 throws FinderException, RemoveException, RemoteException;
288
289
290 /**
291 * Ändert die Daten eines Benchmarks. Ein Benchmark wird
292 * über die ID eindeutig identifiziert.
293 * @param bench Der Benchmark, mit den geänderten Daten.
294 */
295 public void changeBenchmark(BenchmarkClone bench)
296 throws FinderException, RemoteException;
297
298
299 /**
300 * Sucht nach einem Benchmark, der als ID den
301 * übergebenen Parameter id besitzt.
302 * @param id ID (Primärschlüssel) nach der gesucht werden
303 * soll.
304 *
305 * @return Ein BenchmarkClone Objekt, des Benchmark.
306 */
307 public BenchmarkClone
308 getBenchmarkByPrimaryKey(long id)
309 throws FinderException, RemoteException;
310
311
312 /**
313 * Liefert alle Datensätze der Benchmark-Tabelle
Quellcode der Verwaltungs Server Komponente 323

314 * zurück.
315 * @return Alle Datensätze der Benchmark-Tabelle.
316 */
317 public Collection getAllBenchmark() throws FinderException,
318 RemoteException;
319
320
321 /**
322 * Sucht nach Benchmark Datensätzen, die als Rechnernamen
323 * den übergebenen Parameter host besitzen.
324 * @param host Der Rechnername der Benchmarks.
325 *
326 * @return Eine Liste von BenchmarkClone Objekten.
327 */
328 public Collection getBenchmarkByHost(String host)
329 throws FinderException, RemoteException;
330
331
332 /**
333 * Erzeugt ein neuen Observer in der Datenbank.
334 * @param observer Der Observer, der erzeugt werden soll.
335 */
336 public void addObserver(ObserverClone observer)
337 throws CreateException, RemoteException;
338
339
340 /**
341 * Entfernt einen Observer aus der Datenbank. Ein
342 * Observer wird über die ID eindeutig identifiziert.
343 * @param observer Der Observer, der gelöscht werden soll.
344 */
345 public void removeObserver(ObserverClone observer)
346 throws FinderException, RemoveException, RemoteException;
347
348
349 /**
350 * Ändert die Daten eines Observers. Ein Observer wird
351 * über die ID eindeutig identifiziert.
352 * @param observer Der Observer, mit den geänderten Daten.
353 */
354 public void changeObserver(ObserverClone observer)
355 throws FinderException, RemoteException;
356
357
358 /**
Quellcode der Verwaltungs Server Komponente 324

359 * Sucht nach einem Observer, der als ID den übergebenen


360 * Parameter id besitzt.
361 * @param id ID (Primärschlüssel) nach der gesucht werden
362 * soll.
363 *
364 * @return Ein BenchmarkClone Objekt, des Benchmark.
365 */
366 public ObserverClone
367 getObserverByPrimaryKey(long id)
368 throws FinderException, RemoteException;
369
370
371 /**
372 * Liefert alle Datensätze der Observer-Tabelle
373 * zurück.
374 * @return Alle Datensätze der Observer-Tabelle.
375 */
376 public Collection getAllObserver() throws FinderException,
377 RemoteException;
378
379
380 /**
381 * Sucht nach Observer Datensätzen, die als Rechnernamen
382 * den übergebenen Parameter host besitzen.
383 * @param host Der Name der Observer nach denen
384 * gesucht werden soll.
385 *
386 * @return Eine Liste von BenchmarkClone Objekten.
387 */
388 public Collection getObserverByHost(String host)
389 throws FinderException, RemoteException;
390
391
392 /**
393 * Erzeugt ein neuen Dienst in der Datenbank.
394 * @param service Der Dienst, der erzeugt werden soll.
395 */
396 public void addService(ServiceClone service)
397 throws CreateException, RemoteException;
398
399
400 /**
401 * Entfernt einen Dienst aus der Datenbank. Ein Dienst
402 * wird über den Namen eindeutig identifiziert.
403 * @param service Der Dienst, der gelöscht werden soll.
Quellcode der Verwaltungs Server Komponente 325

404 */
405 public void removeService(ServiceClone service)
406 throws FinderException, RemoveException, RemoteException;
407
408
409 /**
410 * Ändert die Daten eines Dienstes. Ein Dienst wird über
411 * den Namen eindeutig identifiziert.
412 * @param service Der Dienst, mit den geänderten Daten.
413 */
414 public void changeService(ServiceClone service)
415 throws FinderException, RemoteException;
416
417
418 /**
419 * Sucht nach einem Service, der als Namen den
420 * übergebenen Parameter name besitzt.
421 * @param name Name (Primärschlüssel) nach dem gesucht
422 * werden soll.
423 *
424 * @return Ein ServiceClone Objekt, des Dienstes.
425 */
426 public ServiceClone
427 getServiceByPrimaryKey(String name)
428 throws FinderException, RemoteException;
429
430
431 /**
432 * Liefert alle Datensätze der Service-Tabelle
433 * zurück.
434 * @return Alle Datensätze der Service-Tabelle.
435 */
436 public Collection getAllService() throws FinderException,
437 RemoteException;
438
439
440 /**
441 * Sucht nach Service Datensätzen, die als Beschreibung
442 * den übergebenen Parameter description besitzen.
443 * @param description Die Beschreibung des Service
444 * nach der gesucht werden soll.
445 *
446 * @return Eine Liste von ServiceClone Objekten.
447 */
448 public Collection getServiceByDescription(String
Quellcode der Verwaltungs Server Komponente 326

449 description) throws FinderException, RemoteException;


450
451
452 /**
453 * Erzeugt ein neuen ServiceStatus in der Datenbank.
454 * @param status Der ServiceStatus, der erzeugt werden
455 * soll.
456 */
457 public void addServiceStatus(ServiceStatusClone status)
458 throws CreateException, RemoteException;
459
460
461 /**
462 * Entfernt einen ServiceStatus aus der Datenbank. Ein
463 * Dienst wird über die ID eindeutig identifiziert.
464 * @param status Der ServiceStatus, der gelöscht werden
465 * soll.
466 */
467 public void removeServiceStatus(ServiceStatusClone status)
468 throws FinderException, RemoveException, RemoteException;
469
470
471 /**
472 * Ändert die Daten eines ServiceStatus. Ein ServiceStatus
473 * wird über die ID eindeutig identifiziert.
474 * @param status Der ServiceStatus, mit den geänderten
475 * Daten.
476 */
477 public void changeServiceStatus(ServiceStatusClone status)
478 throws FinderException, RemoteException;
479
480
481 /**
482 * Sucht nach einem ServiceStatus, der als ID den
483 * übergebenen Parameter id besitzt.
484 * @param id ID (Primärschlüssel), nach der gesucht werden
485 * soll.
486 *
487 * @return Ein ServiceStatusClone Objekt, des
488 * ServiceStatus.
489 */
490 public ServiceStatusClone getServiceStatusByPrimaryKey(
491 int id) throws FinderException, RemoteException;
492
493
Quellcode der Verwaltungs Server Komponente 327

494 /**
495 * Liefert alle Datensätze der ServiceStatus-Tabelle
496 * zurück.
497 * @return Alle Datensätze der ServiceStatus-Tabelle.
498 */
499 public Collection getAllServiceStatus()
500 throws FinderException, RemoteException;
501
502
503 /**
504 * Sucht nach ServiceStatus Datensätze, die als Bedeutung
505 * den übergebenen Parameter interpretation besitzen.
506 * @param interpretation Die Bedeutung des ServiceStatus,
507 * nach der gesucht werden soll.
508 *
509 * @return Eine Liste von ServiceStatusClone Objekten.
510 */
511 public Collection getServiceStatusByInterpretation(String
512 interpretation) throws FinderException, RemoteException;
513
514
515 /**
516 * Erzeugt einen neuen ServiceType in der Datenbank.
517 * @param type Der ServiceType, der erzeugt werden soll.
518 */
519 public void addServiceType(ServiceTypeClone type)
520 throws CreateException, RemoteException;
521
522
523 /**
524 * Entfernt einen ServiceType aus der Datenbank. Ein
525 * ServiceType wird über die ID eindeutig identifiziert.
526 * @param type Der ServiceType, der gelöscht werden
527 * soll.
528 */
529 public void removeServiceType(ServiceTypeClone type)
530 throws FinderException, RemoveException, RemoteException;
531
532
533 /**
534 * Ändert die Daten eines ServiceTypes. Ein ServiceType
535 * wird über die ID eindeutig identifiziert.
536 * @param type Der ServiceType, mit den geänderten Daten.
537 */
538 public void changeServiceType(ServiceTypeClone type)
Quellcode der Verwaltungs Server Komponente 328

539 throws FinderException, RemoteException;


540
541 /**
542 * Sucht nach einem ServiceType, der als ID den
543 * übergebenen Parameter id besitzt.
544 * @param id ID (Primärschlüssel), nach der gesucht werden
545 * soll.
546 *
547 * @return Ein ServiceTypeClone Objekt, des ServiceStatus.
548 */
549 public ServiceTypeClone getServiceTypeByPrimaryKey(int id)
550 throws FinderException, RemoteException;
551
552
553 /**
554 * Liefert alle Datensätze der ServiceType-Tabelle
555 * zurück.
556 * @return Alle Datensätze der ServiceType-Tabelle.
557 */
558 public Collection getAllServiceType()
559 throws FinderException, RemoteException;
560
561
562 /**
563 * Sucht nach ServiceType Datensätzen, die als Bedeutung
564 * den übergebenen Parameter interpretation besitzen.
565 * @param interpretation Die Bedeutung der ServiceTypes
566 * nach der gesucht werden soll.
567 *
568 * @return Eine Liste vonServiceTypeClone Objekten.
569 */
570 public Collection getServiceTypeByInterpretation(String
571 interpretation) throws FinderException, RemoteException;
572
573
574 /**
575 * Erzeugt eine neue ApplicationServer-Service Assoziation.
576 * @param appServerService Die ApplicationServer-Service
577 * Assoziation, die erzeugt werden soll.
578 */
579 public void addR_AppServerService(R_AppServerServiceClone
580 appServerService) throws CreateException, RemoteException;
581
582
583 /**
Quellcode der Verwaltungs Server Komponente 329

584 * Entfernt eine neue ApplicationServer-Service Assoziation


585 * aus der Datenbank. Eine ApplicationServer-Service
586 * Assoziation wird über die ID eindeutig identifiziert.
587 * @param appServerService Die ApplicationServer-Service
588 * Assoziation, die gelöscht werden soll.
589 */
590 public void removeR_AppServerService(
591 R_AppServerServiceClone appServerService)
592 throws FinderException, RemoveException, RemoteException;
593
594
595 /**
596 * Ändert die Daten eines ApplicationServer-Service
597 * Assoziation. Eine ApplicationServer-Service Assoziation
598 * wird über die ID eindeutig identifiziert.
599 * @param appServerService Die ApplicationServer-Service
600 * Assoziation, mit den geänderten Daten.
601 */
602 public void changeR_AppServerService(
603 R_AppServerServiceClone appServerService)
604 throws FinderException, RemoteException;
605
606
607 /**
608 * Sucht nach einem AppServer-Service Assoziation, die als
609 * ID den übergebenen Parameter id besitzt.
610 * @param id ID (Primärschlüssel) nach der gesucht werden
611 * soll.
612 *
613 * @return Ein R_AppServerServiceClone Objekt, der
614 * ApplicationServer-Service Assoziation.
615 */
616 public R_AppServerServiceClone
617 getR_AppServerServiceByPrimaryKey( long id)
618 throws FinderException, RemoteException;
619
620
621 /**
622 * Liefert alle Datensätze der R_AppServerService Tabelle
623 * zurück.
624 * @return Alle Datensätze der R_AppServerService Tabelle.
625 */
626 public Collection getAllR_AppServerService()
627 throws FinderException, RemoteException;
628
Quellcode der Verwaltungs Server Komponente 330

629
630 /**
631 * Sucht nach AppServer-Service Datensätzen, die den
632 * übergebenen Parameter name als ApplicationServer
633 * besitzen.
634 * @param name Der Primärschlüssel des ApplicationServers,
635 * nach dem gesucht werden soll.
636 *
637 * @return Eine Liste von R_AppServerServiceClone Objekten.
638 */
639 public Collection getR_AppServerServiceByAppServer(String
640 name) throws FinderException, RemoteException;
641
642
643 /**
644 * Sucht nach AppServer-Service Datensätzen, die den
645 * übergebenen Parameter name als Service besitzen.
646 * @param name Der Primärschlüssel des Service nach dem
647 * gesucht werden soll.
648 *
649 * @return Eine Liste von R_AppServerServiceClone Objekten.
650 */
651 public Collection getR_AppServerServiceByService(String
652 name) throws FinderException, RemoteException;
653
654
655
656 /**
657 * Sucht nach einer AppServer-Service Assoziation, die als
658 * Applikations-Server den übergebenen Parameter appServer
659 * und als Dienst den Parameter service besitzt.
660 * @param appServer Der Primärschlüssel des Applikations-
661 * Servers nach dem gesucht werden soll.
662 * @param service Der Primärschlüssel des Service, nach dem
663 * gesucht werden soll.
664 * @return Ein R_AppServerServiceClone Objekt, der
665 * ApplicationServer-Service Assoziation.
666 */
667 public R_AppServerServiceClone
668 getR_AppServerServiceByAppServerAndService(String
669 appServer, String service)
670 throws FinderException, RemoteException;
671
672
673 /**
Quellcode der Verwaltungs Server Komponente 331

674 * Sucht nach AppServer-Service Datensätzen, die als


675 * Rechnernamen den übergebenen Parameter host besitzen.
676 * @param host Der Rechnername, auf dem sich der Dienst
677 * befindet.
678 *
679 * @return Eine Liste von R_AppServerServiceClone Objekten.
680 */
681 public Collection getR_AppServerServiceByHost(String host)
682 throws FinderException, RemoteException;
683
684
685 /**
686 * Sucht nach AppServer-Service Datensätzen, die den
687 * übergebenen Parameter id als Status des Dienstes
688 * besitzen.
689 * @param id Der Status des Dienstes, nach dem gesucht
690 * werden soll.
691 *
692 * @return Eine Liste von R_AppServerServiceClone Objekten.
693 */
694 public Collection getR_AppServerServiceByStatus(int id)
695 throws FinderException, RemoteException;
696
697
698 /**
699 * Sucht nach AppServer-Service Datensätzen, die den
700 * übergebenen Parameter id als Typ des Dienstes besitzen.
701 * @param id Der Typ des Dienstes, nach dem gesucht werden
702 * soll.
703 *
704 * @return Eine Liste von R_AppServerServiceClone Objekten.
705 */
706 public Collection getR_AppServerServiceByType(int id)
707 throws FinderException, RemoteException;
708
709
710 /**
711 * Erzeugt eine neue Benchmark-Observer Assoziation.
712 * @param assoziation Die Benchmark-Observer Assoziation,
713 * die erzeugt werden soll.
714 */
715 public void registerBenchmarkToObserver(
716 R_BenchmarkObserverClone assoziation)
717 throws CreateException, RemoteException;
718
Quellcode der Verwaltungs Server Komponente 332

719 /**
720 * Entfernt eine neue Benchmark-Observer Assoziation
721 * aus der Datenbank. Eine Benchmark-Observer Assoziation
722 * wird über die ID eindeutig identifiziert.
723 * @param assoziation Die Benchmark-Observer Assoziation,
724 * die gelöscht werden soll.
725 */
726 public void unregisterBenchmarkFromObserver(
727 R_BenchmarkObserverClone assoziation)
728 throws FinderException, RemoveException, RemoteException;
729
730
731 /**
732 * Sucht nach einer Benchmark-Observer Assoziation, die als
733 * ID den übergebenen Parameter id besitzt.
734 * @param id ID (Primärschlüssel), nach der gesucht werden
735 * soll.
736 *
737 * @return Ein R_AppServerServiceClone Objekt, der
738 * Benchmark-Observer Assoziation.
739 */
740 public R_BenchmarkObserverClone
741 getR_BenchmarkObserverByPrimaryKey(
742 long id) throws FinderException, RemoteException;
743
744
745 /**
746 * Sucht nach Benchmark-Observer Datensätze, die den
747 * übergebenen Parameter id als Observer haben.
748 * @param id Die ID des Observer nach dem gesucht
749 * werden soll.
750 *
751 * @return Eine Liste von R_AppServerServiceClone Objekten.
752 */
753 public Collection
754 getR_BenchmarkObserverByObserver(long id)
755 throws FinderException, RemoteException;
756
757
758 /**
759 * Sucht nach Benchmark-Observer Datensätze, die den
760 * übergebenen Parameter id als Benchmark haben.
761 * @param id ID des Benchmark, nach dem gesucht
762 * werden soll.
763 *
Quellcode der Verwaltungs Server Komponente 333

764 * @return Eine Liste von R_AppServerServiceClone Objekten.


765 */
766 public Collection
767 getR_BenchmarkObserverByBenchmark(long id)
768 throws FinderException, RemoteException;
769
770
771
772
773 /**
774 * Liefert alle Datensätze der R_BenchmarkObserver Tabelle
775 * zurück.
776 * @return Alle Datensätze der R_BenchmarkObserver Tabelle.
777 */
778 public Collection getAllR_BenchmarkObserver()
779 throws FinderException, RemoteException;
780
781
782
783 /**
784 * Erzeugt eine neue AppServerService-Observer Assoziation.
785 * @param assoziation Die AppServerService-Observer
786 * Assoziation, die erzeugt werden soll.
787 */
788 public void registerAppServerServiceToObserver(
789 R_AppServerServiceObserverClone assoziation)
790 throws CreateException, RemoteException;
791
792
793 /**
794 * Entfernt eine neue AppServerService-Observer Assoziation
795 * aus der Datenbank. Eine AppServerService-Observer
796 * Assoziation wird über die ID eindeutig identifiziert.
797 * @param assoziation Die AppServerService-Observer
798 * Assoziation, die gelöscht werden soll.
799 */
800 public void unregisterAppServerServiceFromObserver(
801 R_AppServerServiceObserverClone assoziation)
802 throws FinderException, RemoveException, RemoteException;
803
804
805 /**
806 * Sucht nach einer AppServerService-Observer Assoziation,
807 * die als ID den übergebenen Parameter id besitzt.
808 * @param id ID (Primärschlüssel), nach der gesucht werden
Quellcode der Verwaltungs Server Komponente 334

809 * soll.
810 *
811 * @return Ein R_AppServerServiceClone Objekt, der
812 * AppServerService-Observer Assoziation.
813 */
814 public R_AppServerServiceObserverClone
815 getR_AppServerServiceObserverByPrimaryKey(
816 long id) throws FinderException, RemoteException;
817
818
819 /**
820 * Liefert alle Datensätze der R_AppServerServiceObserver
821 * Tabelle zurück.
822 * @return Alle Datensätze der R_AppServerServiceObserver
823 * Tabelle.
824 */
825 public Collection getAllR_AppServerServiceObserver()
826 throws FinderException, RemoteException;
827
828
829 /**
830 * Sucht nach AppServerService-Observer Datensätzen, die
831 * den übergebenen Parameter id als AppServerService haben.
832 * @param id Der ID des AppServerService nach dem gesucht
833 * werden soll.
834 *
835 * @return Eine Liste von R_AppServerServiceClone Objekten.
836 */
837 public Collection
838 getR_AppServerServiceObserverByAppServerService(long id)
839 throws FinderException, RemoteException;
840
841
842 /**
843 * Sucht nach AppServerService-Observer Datensätzen, die
844 * den übergebenen Parameter id als Observer haben.
845 * @param id Die ID des Observer nach dem gesucht
846 * werden soll.
847 *
848 * @return Eine Liste von R_AppServerServiceClone Objekten.
849 */
850 public Collection
851 getR_AppServerServiceObserverByObserver(long id)
852 throws FinderException, RemoteException;
853
Quellcode der Verwaltungs Server Komponente 335

854
855 /**
856 * Startet den entstprechenden Observer neu. Dabei werden
857 * alle zu überwachenden Applikations-Server und Dienste
858 * beim Observer registriert. Ist das Flag remoteControl
859 * des Observer gesetzt werden die zudem noch Intervalle
860 * und Zeitabständen mit den WErten aus der Datenbank
861 * überschrieben.
862 * @param id Die Id des Benchmarks, der neu gestartet
863 * werden soll.
864 *
865 * @return Wahr wenn der Observer neu gestartet werden
866 * konnte.
867 */
868 public boolean resetobserver (long id)
869 throws RemoteException, Exception;
870 }

C.3. Quellcode der Klasse ServerAdminBean

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: ServerAdminBean
9 * Date: 13.03.2003
10 * Version: 1.0
11 */
12
13 package serverAdmin;
14
15 import javax.ejb.*;
16 import serverAdmin.entity.clone.*;
17 import serverAdmin.entity.*;
18 import javax.naming.*;
19 import javax.rmi.*;
20 import java.util.*;
21 import observer.*;
22 import java.rmi.*;
Quellcode der Verwaltungs Server Komponente 336

23 import java.net.*;
24
25 /**
26 * Session Bean als Hülle um alle Entity Beans des
27 * serverAdmin.entity Paketes.
28 *
29 * Diese Bean ermöglicht es, auf Entity Beans des
30 * serverAdmin.entity Paketes zuzugreifen. Die Kommunikation
31 * erfolgt mit Hilfe von Kopien.
32 */
33 public class ServerAdminBean implements SessionBean {
34
35 SessionContext ctx;
36
37 /**
38 * Referenzen zu den entsprechenden Home Objekten.
39 */
40 AdministratorLocalHome adminHome;
41 ApplicationServerLocalHome appServerHome;
42 AppServerStatusLocalHome appServerStatusHome;
43 BenchmarkLocalHome benchmarkHome;
44 ObserverLocalHome observerHome;
45 ServiceLocalHome serviceHome;
46 ServiceStatusLocalHome serviceStatusHome;
47 ServiceTypeLocalHome serviceTypeHome;
48 R_AppServerServiceLocalHome r_AppServerServiceHome;
49 R_AppServerServiceObserverLocalHome
50 r_AppServerServiceObserverHome;
51 R_BenchmarkObserverLocalHome r_BenchmarkObserverHome;
52
53 String host;
54 String port;
55
56 // Benötitge Methoden einer jeden Session Bean
57 public void setSessionContext(SessionContext ctx){
58
59 this.ctx = ctx;
60 }
61
62
63 public void ejbRemove(){
64
65 }
66
67
Quellcode der Verwaltungs Server Komponente 337

68 public void ejbActivate(){


69
70 }
71
72
73 public void ejbPassivate(){
74
75 }
76
77
78 public void ejbCreate() throws CreateException{
79
80 InitialContext context;
81 Object obj;
82
83 // Administrator Home
84 try{
85 context = new InitialContext();
86 obj = context.lookup
87 ("java:comp/env/ejb/AdministratorLocalHome");
88 adminHome = (AdministratorLocalHome)
89 PortableRemoteObject.
90 narrow(obj, AdministratorLocalHome.class);
91
92 }catch(NamingException e){
93 e.printStackTrace();
94 throw new CreateException("Lookup auf java:comp/env/"+
95 "ejb/AdministratorLocalHome gescheitert!");
96 }
97 // ApplicationServer Home
98 try{
99 obj = context.lookup
100 ("java:comp/env/ejb/ApplicationServerLocalHome");
101 appServerHome = (ApplicationServerLocalHome)
102 PortableRemoteObject.
103 narrow(obj, ApplicationServerLocalHome.class);
104
105 }catch(NamingException e){
106 e.printStackTrace();
107 throw new CreateException("Lookup auf java:comp/env/"+
108 "ejb/ApplicationServerLocalHome gescheitert!");
109 }
110 // ApplicationServerStatus Home
111 try{
112 obj = context.lookup
Quellcode der Verwaltungs Server Komponente 338

113 ("java:comp/env/ejb/AppServerStatusLocalHome");
114 appServerStatusHome = (AppServerStatusLocalHome)
115 PortableRemoteObject.
116 narrow(obj, AppServerStatusLocalHome.class);
117
118 }catch(NamingException e){
119 e.printStackTrace();
120 throw new CreateException("Lookup auf java:comp/env/"+
121 "ejb/AppServerStatusLocalHome gescheitert!");
122 }
123 // ServiceStatus Home
124 try{
125 obj = context.lookup
126 ("java:comp/env/ejb/ServiceStatusLocalHome");
127 serviceStatusHome = (ServiceStatusLocalHome)
128 PortableRemoteObject.
129 narrow(obj, ServiceStatusLocalHome.class);
130
131 }catch(NamingException e){
132 e.printStackTrace();
133 throw new CreateException("Lookup auf java:comp/env/"+
134 "ejb/ServiceStatusLocalHome gescheitert!");
135 }
136 // ServiceType Home
137 try{
138 obj = context.lookup
139 ("java:comp/env/ejb/ServiceTypeLocalHome");
140 serviceTypeHome = (ServiceTypeLocalHome)
141 PortableRemoteObject.
142 narrow(obj, ServiceTypeLocalHome.class);
143
144 }catch(NamingException e){
145 e.printStackTrace();
146 throw new CreateException("Lookup auf java:comp/env/"+
147 "ejb/ServiceTypeLocalHome gescheitert!");
148 }
149 // Service Home
150 try{
151 obj = context.lookup
152 ("java:comp/env/ejb/ServiceLocalHome");
153 serviceHome = (ServiceLocalHome)
154 PortableRemoteObject.
155 narrow(obj, ServiceLocalHome.class);
156
157 }catch(NamingException e){
Quellcode der Verwaltungs Server Komponente 339

158 e.printStackTrace();
159 throw new CreateException("Lookup auf java:comp/env/"+
160 "ejb/ServiceLocalHome gescheitert!");
161 }
162 // Benchmark Home
163 try{
164 obj = context.lookup
165 ("java:comp/env/ejb/BenchmarkLocalHome");
166 benchmarkHome = (BenchmarkLocalHome)
167 PortableRemoteObject.
168 narrow(obj, BenchmarkLocalHome.class);
169
170 }catch(NamingException e){
171 e.printStackTrace();
172 throw new CreateException("Lookup auf java:comp/env/"+
173 "ejb/BenchmarkLocalHome gescheitert!");
174 }
175 // Observer Home
176 try{
177 obj = context.lookup
178 ("java:comp/env/ejb/ObserverLocalHome");
179 observerHome = (ObserverLocalHome)
180 PortableRemoteObject.
181 narrow(obj, ObserverLocalHome.class);
182
183 }catch(NamingException e){
184 e.printStackTrace();
185 throw new CreateException("Lookup auf java:comp/env/"+
186 "ejb/ObserverLocalHome gescheitert!");
187 }
188 // R_AppServerService Home
189 try{
190 obj = context.lookup
191 ("java:comp/env/ejb/R_AppServerServiceLocalHome");
192 r_AppServerServiceHome =(R_AppServerServiceLocalHome)
193 PortableRemoteObject.narrow
194 (obj, R_AppServerServiceLocalHome.class);
195
196 }catch(NamingException e){
197 e.printStackTrace();
198 throw new CreateException("Lookup auf java:comp/env/"+
199 "ejb/R_AppServerServiceLocalHome gescheitert!");
200 }
201 // R_AppServerServiceObserver Home
202 try{
Quellcode der Verwaltungs Server Komponente 340

203 obj = context.lookup("java:comp/env/ejb/"+


204 "R_AppServerServiceObserverLocalHome");
205 r_AppServerServiceObserverHome =
206 (R_AppServerServiceObserverLocalHome)
207 PortableRemoteObject.narrow(obj,
208 R_AppServerServiceObserverLocalHome.class);
209
210 }catch(NamingException e){
211 e.printStackTrace();
212 throw new CreateException("Lookup auf java:comp/env/"+
213 "ejb/R_AppServerServiceObserverLocalHome gescheitert!");
214 }
215 // R_BenchmarkObserver Home
216 try{
217 obj = context.lookup("java:comp/env/ejb/"+
218 "R_BenchmarkObserverLocalHome");
219 r_BenchmarkObserverHome =
220 (R_BenchmarkObserverLocalHome)
221 PortableRemoteObject.narrow(obj,
222 R_BenchmarkObserverLocalHome.class);
223
224 }catch(NamingException e){
225 e.printStackTrace();
226 throw new CreateException("Lookup auf java:comp/env/"+
227 "ejb/R_BenchmarkObserverLocalHome gescheitert!");
228 }
229
230 // Environment für die Listener
231 try{
232 obj = context.lookup("java:comp/env/host");
233 host =(String) PortableRemoteObject.narrow(obj,
234 String.class);
235
236 }catch(NamingException e){
237 e.printStackTrace();
238 throw new CreateException("Lookup auf java:comp/env/"+
239 "host gescheitert!");
240 }
241 // Environment für die Listener
242 try{
243 obj = context.lookup("java:comp/env/port");
244 port =(String) PortableRemoteObject.narrow(obj,
245 String.class);
246
247 }catch(NamingException e){
Quellcode der Verwaltungs Server Komponente 341

248 e.printStackTrace();
249 throw new CreateException("Lookup auf java:comp/env/"+
250 "port gescheitert!");
251 }
252 }
253
254
255 /*
256 **********************************************************
257 * Implementation des remote Interfaces
258 **********************************************************
259 */
260 public void addAdministrator(AdministratorClone admin)
261 throws CreateException{
262
263 if ((admin.getName() == null)||
264 (admin.getEMail() == null)){
265 throw new CreateException("Null Werte nicht erlaubt!!");
266 }else{
267 adminHome.create(admin.getID(),admin.getName(),
268 admin.getEMail());
269 }
270 }
271
272
273 public void removeAdministrator(AdministratorClone admin)
274 throws FinderException, RemoveException{
275
276 AdministratorLocal a;
277 a =adminHome.findByPrimaryKey(new Long(admin.getID()));
278 if (a==null){
279 throw new FinderException("Kein Administrator mit ID"+
280 admin.getID() + "gefunden.");
281 }else{
282 a.remove();
283 }
284 }
285
286
287 public void changeAdministrator(AdministratorClone admin)
288 throws FinderException{
289
290 if ((admin.getName() == null)||
291 (admin.getEMail() == null)){
292 throw new FinderException("Null Werte nicht erlaubt!!");
Quellcode der Verwaltungs Server Komponente 342

293 }else{
294 AdministratorLocal a;
295 a =adminHome.findByPrimaryKey(new Long(admin.getID()));
296 if (a==null){
297 throw new FinderException("Kein Administrator mit "+
298 "ID "+admin.getID() + "gefunden.");
299 }else{
300 a.setName(admin.getName());
301 a.setEMail(admin.getEMail());
302 }
303 }
304 }
305
306
307 public AdministratorClone getAdministratorByPrimaryKey(
308 long id) throws FinderException{
309
310 AdministratorLocal a;
311 a = adminHome.findByPrimaryKey(new Long(id));
312 if (a==null){
313 return null;
314 }
315 return createAdministratorClone(a);
316 }
317
318
319 public Collection getAllAdministrator()
320 throws FinderException{
321
322 AdministratorLocal a;
323 Collection coll = adminHome.findAllAdministrator();
324 Iterator iter = coll.iterator();
325 Vector v = new Vector();
326 while (iter.hasNext()){
327 a = (AdministratorLocal) iter.next();
328 v.add(createAdministratorClone(a));
329 }
330 return v;
331 }
332
333
334 public Collection getAdministratorByName(String name)
335 throws FinderException{
336
337 AdministratorLocal a;
Quellcode der Verwaltungs Server Komponente 343

338 Collection coll = adminHome.findByName(name);


339 Iterator iter = coll.iterator();
340 Vector v = new Vector();
341 while (iter.hasNext()){
342 a = (AdministratorLocal) iter.next();
343 v.add(createAdministratorClone(a));
344 }
345 return v;
346 }
347
348
349 public Collection getAdministratorByEMail(String eMail)
350 throws FinderException{
351
352 AdministratorLocal a;
353 Collection coll = adminHome.findByEMail(eMail);
354 Iterator iter = coll.iterator();
355 Vector v = new Vector();
356 while (iter.hasNext()){
357 a = (AdministratorLocal) iter.next();
358 v.add(createAdministratorClone(a));
359 }
360 return v;
361 }
362
363
364 //***********************************************************
365 // AppServerStatus
366 //***********************************************************
367 public void addAppServerStatus(AppServerStatusClone status)
368 throws CreateException{
369
370 if (status.getInterpretation() == null){
371 throw new CreateException("Null Werte nicht erlaubt!!");
372 }else{
373 appServerStatusHome.create(status.getID(),
374 status.getInterpretation());
375 }
376 }
377
378
379 public void removeAppServerStatus(AppServerStatusClone
380 status) throws FinderException, RemoveException{
381
382 AppServerStatusLocal a;
Quellcode der Verwaltungs Server Komponente 344

383 a = appServerStatusHome.findByPrimaryKey(
384 new Integer(status.getID()));
385 if (a==null){
386 throw new FinderException("Kein AppServerStatus mit "+
387 "ID "+status.getID() + "gefunden.");
388 }else{
389 a.remove();
390 }
391 }
392
393
394 public void changeAppServerStatus(AppServerStatusClone
395 status) throws FinderException{
396
397 if (status.getInterpretation() == null){
398 throw new FinderException("Null Werte nicht erlaubt!!");
399 }else{
400 AppServerStatusLocal a;
401 a = appServerStatusHome.findByPrimaryKey(
402 new Integer(status.getID()));
403 if (a==null){
404 throw new FinderException("Kein AppServerStatus mit"+
405 " ID "+status.getID() + "gefunden.");
406 }else{
407 a.setInterpretation(status.getInterpretation());
408 }
409 }
410 }
411
412
413 public AppServerStatusClone getAppServerStatusByPrimaryKey(
414 int id) throws FinderException{
415
416 AppServerStatusLocal a;
417 a =appServerStatusHome.findByPrimaryKey(new Integer(id));
418 if (a==null){
419 return null;
420 }
421 return createAppServerStatusClone(a);
422 }
423
424
425 public Collection getAllAppServerStatus()
426 throws FinderException{
427
Quellcode der Verwaltungs Server Komponente 345

428 AppServerStatusLocal a;
429 Collection coll = appServerStatusHome.
430 findAllAppServerStatus();
431 Iterator iter = coll.iterator();
432 Vector v = new Vector();
433 while (iter.hasNext()){
434 a = (AppServerStatusLocal) iter.next();
435 v.add(createAppServerStatusClone(a));
436 }
437 return v;
438 }
439
440
441 public Collection getAppServerStatusByInterpretation(
442 String interpretation) throws FinderException{
443
444 AppServerStatusLocal a;
445 Collection coll = appServerStatusHome.
446 findByInterpretation(interpretation);
447 Iterator iter = coll.iterator();
448 Vector v = new Vector();
449 while (iter.hasNext()){
450 a = (AppServerStatusLocal) iter.next();
451 v.add(createAppServerStatusClone(a));
452 }
453 return v;
454 }
455
456
457 //***********************************************************
458 // ApplicationServer
459 //***********************************************************
460 public void addApplicationServer(ApplicationServerClone
461 appServer) throws CreateException{
462
463 if ((appServer.getName() == null)||
464 (appServer.getHost() == null)){
465 throw new CreateException("Null Werte nicht erlaubt!!");
466 }else{
467
468 appServerHome.create(appServer.getName(),
469 appServer.getHost(),
470 appServer.getAdministrator(),
471 appServer.getAppServerStatus(),
472 appServer.getBenchmark());
Quellcode der Verwaltungs Server Komponente 346

473 }
474 }
475
476
477 public void removeApplicationServer(ApplicationServerClone
478 appServer) throws FinderException, RemoveException{
479
480 ApplicationServerLocal a;
481 a = appServerHome.findByPrimaryKey(appServer.getName());
482 if (a==null){
483 throw new FinderException("Kein ApplicationServer mit"+
484 " Name "+appServer.getName() + "gefunden.");
485 }else{
486 a.remove();
487 }
488 }
489
490
491 public void changeApplicationServer(ApplicationServerClone
492 appServer) throws FinderException{
493
494 if (appServer.getHost() == null){
495 throw new FinderException("Null Werte nicht erlaubt!!");
496 }else{
497 ApplicationServerLocal a;
498 a = appServerHome.
499 findByPrimaryKey(appServer.getName());
500 if (a==null){
501 throw new FinderException("Kein ApplicationServer "+
502 "mit Name "+appServer.getName() + "gefunden.");
503 }else{
504 a.setHost(appServer.getHost());
505 a.setAdministrator(appServer.getAdministrator());
506 a.setAppServerStatus(appServer.getAppServerStatus());
507 a.setBenchmark(appServer.getBenchmark());
508 }
509 }
510 }
511
512
513 public ApplicationServerClone
514 getApplicationServerByPrimaryKey(String name)
515 throws FinderException{
516
517 ApplicationServerLocal a;
Quellcode der Verwaltungs Server Komponente 347

518 a =appServerHome.findByPrimaryKey(name);
519 if (a==null){
520 return null;
521 }
522 return createApplicationServerClone(a);
523 }
524
525
526 public Collection getAllApplicationServer()
527 throws FinderException{
528
529 ApplicationServerLocal a;
530 Collection coll = appServerHome.
531 findAllApplicationServer();
532 Iterator iter = coll.iterator();
533 Vector v = new Vector();
534 while (iter.hasNext()){
535 a = (ApplicationServerLocal) iter.next();
536 v.add(createApplicationServerClone(a));
537 }
538 return v;
539 }
540
541
542 public Collection getApplicationServerByAdmin(long id)
543 throws FinderException{
544
545 ApplicationServerLocal a;
546 Collection coll = appServerHome.findByAdmin(id);
547 Iterator iter = coll.iterator();
548 Vector v = new Vector();
549 while (iter.hasNext()){
550 a = (ApplicationServerLocal) iter.next();
551 v.add(createApplicationServerClone(a));
552 }
553 return v;
554 }
555
556
557 public ApplicationServerClone
558 getApplicationServerByBenchmark(long id)
559 throws FinderException{
560
561 ApplicationServerLocal a;
562 Collection coll = appServerHome.findByBenchmark(id);
Quellcode der Verwaltungs Server Komponente 348

563 Iterator iter = coll.iterator();


564 if (iter.hasNext()){
565 a = (ApplicationServerLocal) iter.next();
566 return createApplicationServerClone(a);
567 }
568 return null;
569 }
570
571
572 public Collection getApplicationServerByHost(String host)
573 throws FinderException{
574
575 ApplicationServerLocal a;
576 Collection coll = appServerHome.findByHost(host);
577 Iterator iter = coll.iterator();
578 Vector v = new Vector();
579 while (iter.hasNext()){
580 a = (ApplicationServerLocal) iter.next();
581 v.add(createApplicationServerClone(a));
582 }
583 return v;
584 }
585
586 public Collection getApplicationServerByStatus(int id)
587 throws FinderException{
588
589 ApplicationServerLocal a;
590 Collection coll = appServerHome.findByStatus(id);
591 Iterator iter = coll.iterator();
592 Vector v = new Vector();
593 while (iter.hasNext()){
594 a = (ApplicationServerLocal) iter.next();
595 v.add(createApplicationServerClone(a));
596 }
597 return v;
598 }
599
600
601 //***********************************************************
602 // Benchmark
603 //***********************************************************
604 public void addBenchmark(BenchmarkClone bench)
605 throws CreateException{
606
607 if ((bench.getHost() == null)||
Quellcode der Verwaltungs Server Komponente 349

608 (bench.getLookupName() == null)){


609 throw new CreateException("Null Werte nicht erlaubt!!");
610 }else{
611 benchmarkHome.create(bench.getID(),
612 bench.getHost(),
613 bench.getPort(),
614 bench.getLookupName());
615 }
616 }
617
618
619 public void removeBenchmark(BenchmarkClone bench)
620 throws FinderException, RemoveException{
621
622 BenchmarkLocal b;
623 b = benchmarkHome.findByPrimaryKey(
624 new Long(bench.getID()));
625 if (b==null){
626 throw new FinderException("Kein Benchmark mit"+
627 " ID "+bench.getID() + "gefunden.");
628 }else{
629 b.remove();
630 }
631 }
632
633
634 public void changeBenchmark(BenchmarkClone bench)
635 throws FinderException{
636
637 if ((bench.getHost() == null)||
638 (bench.getLookupName() == null)){
639 throw new FinderException("Null Werte nicht erlaubt!!");
640 }else{
641 BenchmarkLocal b;
642 b = benchmarkHome.findByPrimaryKey(
643 new Long(bench.getID()));
644 if (b==null){
645 throw new FinderException("Kein Benchmark mit"+
646 " ID "+bench.getID() + "gefunden.");
647 }else{
648 b.setHost(bench.getHost());
649 b.setPort(bench.getPort());
650 b.setLookupName(bench.getLookupName());
651 }
652 }
Quellcode der Verwaltungs Server Komponente 350

653 }
654
655
656 public BenchmarkClone
657 getBenchmarkByPrimaryKey(long id)
658 throws FinderException{
659
660 BenchmarkLocal b;
661 b = benchmarkHome.findByPrimaryKey(new Long(id));
662 if (b==null){
663 return null;
664 }
665 return createBenchmarkClone(b);
666 }
667
668
669 public Collection getAllBenchmark() throws FinderException{
670
671 BenchmarkLocal b;
672 Collection coll = benchmarkHome.findAllBenchmark();
673 Iterator iter = coll.iterator();
674 Vector v = new Vector();
675 while (iter.hasNext()){
676 b = (BenchmarkLocal) iter.next();
677 v.add(createBenchmarkClone(b));
678 }
679 return v;
680 }
681
682
683 public Collection getBenchmarkByHost(String host)
684 throws FinderException{
685
686 BenchmarkLocal b;
687 Collection coll = benchmarkHome.findByHost(host);
688 Iterator iter = coll.iterator();
689 Vector v = new Vector();
690 while (iter.hasNext()){
691 b = (BenchmarkLocal) iter.next();
692 v.add(createBenchmarkClone(b));
693 }
694 return v;
695 }
696
697
Quellcode der Verwaltungs Server Komponente 351

698 //***********************************************************
699 // Observer
700 //***********************************************************
701 public void addObserver(ObserverClone observer)
702 throws CreateException{
703
704 if ((observer.getHost() == null)||
705 (observer.getLookupName() == null)){
706 throw new CreateException("Null Werte nicht erlaubt!!");
707 }else{
708 observerHome.create(observer.getID(),
709 observer.getHost(),
710 observer.getPort(),
711 observer.getLookupName(),
712 observer.getCheckSpeedInterval(),
713 observer.getLookupServiceInterval(),
714 observer.getReconnectBenchInterval(),
715 observer.getMaxCheckSpeedTime(),
716 observer.getMaxServiceLookupTime(),
717 observer.getMaxBenchLookupTime(),
718 observer.isRemoteControl());
719 }
720 }
721
722
723 public void removeObserver(ObserverClone observer)
724 throws FinderException, RemoveException{
725
726 ObserverLocal o;
727 o = observerHome.findByPrimaryKey(
728 new Long(observer.getID()));
729 if (o==null){
730 throw new FinderException("Kein Observer mit"+
731 " ID "+observer.getID() + "gefunden.");
732 }else{
733 o.remove();
734 }
735 }
736
737
738 public void changeObserver(ObserverClone observer)
739 throws FinderException{
740
741 if ((observer.getHost() == null)||
742 (observer.getLookupName() == null)){
Quellcode der Verwaltungs Server Komponente 352

743 throw new FinderException("Null Werte nicht erlaubt!!");


744 }else{
745 ObserverLocal o;
746 o = observerHome.findByPrimaryKey(
747 new Long(observer.getID()));
748 if (o==null){
749 throw new FinderException("Kein Observer mit"+
750 " ID "+observer.getID() + "gefunden.");
751 }else{
752 o.setHost(observer.getHost());
753 o.setPort(observer.getPort());
754 o.setLookupName(observer.getLookupName());
755 o.setCheckSpeedInterval(observer.
756 getCheckSpeedInterval());
757 o.setLookupServiceInterval(observer.
758 getLookupServiceInterval());
759 o.setReconnectBenchInterval(observer.
760 getReconnectBenchInterval());
761 o.setMaxCheckSpeedTime(observer.
762 getMaxCheckSpeedTime());
763 o.setMaxServiceLookupTime(observer.
764 getMaxServiceLookupTime());
765 o.setMaxBenchLookupTime(observer.
766 getMaxBenchLookupTime());
767 o.setRemoteControl(observer.isRemoteControl());
768 }
769 }
770 }
771
772
773 public ObserverClone
774 getObserverByPrimaryKey(long id)
775 throws FinderException{
776
777 ObserverLocal o;
778 o = observerHome.findByPrimaryKey(new Long(id));
779 if (o==null){
780 return null;
781 }
782 return createObserverClone(o);
783 }
784
785
786 public Collection getAllObserver() throws FinderException{
787
Quellcode der Verwaltungs Server Komponente 353

788 ObserverLocal o;
789 Collection coll = observerHome.findAllObserver();
790 Iterator iter = coll.iterator();
791 Vector v = new Vector();
792 while (iter.hasNext()){
793 o = (ObserverLocal) iter.next();
794 v.add(createObserverClone(o));
795 }
796 return v;
797 }
798
799
800 public Collection getObserverByHost(String host)
801 throws FinderException{
802
803 ObserverLocal o;
804 Collection coll = benchmarkHome.findByHost(host);
805 Iterator iter = coll.iterator();
806 Vector v = new Vector();
807 while (iter.hasNext()){
808 o = (ObserverLocal) iter.next();
809 v.add(createObserverClone(o));
810 }
811 return v;
812 }
813
814
815 //***********************************************************
816 // Service
817 //***********************************************************
818 public void addService(ServiceClone service)
819 throws CreateException{
820
821 if (service.getName() == null){
822 throw new CreateException("Null Werte nicht erlaubt!!");
823 }else{
824 serviceHome.create(service.getName(),
825 service.getDescription());
826 }
827 }
828
829
830 public void removeService(ServiceClone service)
831 throws FinderException, RemoveException{
832
Quellcode der Verwaltungs Server Komponente 354

833 ServiceLocal s;
834 s = serviceHome.findByPrimaryKey(service.getName());
835 if (s==null){
836 throw new FinderException("Kein Service mit"+
837 " Name "+service.getName() + "gefunden.");
838 }else{
839 s.remove();
840 }
841 }
842
843
844 public void changeService(ServiceClone service)
845 throws FinderException{
846
847 ServiceLocal s;
848 s = serviceHome.findByPrimaryKey(service.getName());
849 if (s==null){
850 throw new FinderException("Kein Service mit"+
851 " Name "+service.getName() + "gefunden.");
852 }else{
853 s.setDescription(service.getDescription());
854 }
855 }
856
857
858 public ServiceClone
859 getServiceByPrimaryKey(String name)
860 throws FinderException{
861
862 ServiceLocal s;
863 s = serviceHome.findByPrimaryKey(name);
864 if (s==null){
865 return null;
866 }
867 return createServiceClone(s);
868 }
869
870
871 public Collection getAllService() throws FinderException{
872
873 ServiceLocal s;
874 Collection coll = serviceHome.findAllService();
875 Iterator iter = coll.iterator();
876 Vector v = new Vector();
877 while (iter.hasNext()){
Quellcode der Verwaltungs Server Komponente 355

878 s = (ServiceLocal) iter.next();


879 v.add(createServiceClone(s));
880 }
881 return v;
882 }
883
884
885 public Collection getServiceByDescription(String
886 description) throws FinderException{
887
888 ServiceLocal s;
889 Collection coll = serviceHome.findByDescription(
890 description);
891 Iterator iter = coll.iterator();
892 Vector v = new Vector();
893 while (iter.hasNext()){
894 s = (ServiceLocal) iter.next();
895 v.add(createServiceClone(s));
896 }
897 return v;
898 }
899
900
901 //***********************************************************
902 // ServiceStatus
903 //***********************************************************
904 public void addServiceStatus(ServiceStatusClone status)
905 throws CreateException{
906
907 if (status.getInterpretation() == null){
908 throw new CreateException("Null Werte nicht erlaubt!!");
909 }else{
910 serviceStatusHome.create(status.getID(),
911 status.getInterpretation());
912 }
913 }
914
915
916 public void removeServiceStatus(ServiceStatusClone status)
917 throws FinderException, RemoveException{
918
919 ServiceStatusLocal s;
920 s = serviceStatusHome.findByPrimaryKey(
921 new Integer(status.getID()));
922 if (s==null){
Quellcode der Verwaltungs Server Komponente 356

923 throw new FinderException("Kein ServiceStatus mit "+


924 "ID "+status.getID() + "gefunden.");
925 }else{
926 s.remove();
927 }
928 }
929
930
931 public void changeServiceStatus(ServiceStatusClone status)
932 throws FinderException{
933
934 if (status.getInterpretation() == null){
935 throw new FinderException("Null Werte nicht erlaubt!!");
936 }else{
937 ServiceStatusLocal s;
938 s = serviceStatusHome.findByPrimaryKey(
939 new Integer(status.getID()));
940 if (s==null){
941 throw new FinderException("Kein ServiceStatus mit "+
942 "ID "+status.getID() + "gefunden.");
943 }else{
944 s.setInterpretation(status.getInterpretation());
945 }
946 }
947 }
948
949
950 public ServiceStatusClone getServiceStatusByPrimaryKey(
951 int id) throws FinderException{
952
953 ServiceStatusLocal s;
954 s = serviceStatusHome.findByPrimaryKey(new Integer(id));
955 if (s==null){
956 return null;
957 }
958 return createServiceStatusClone(s);
959 }
960
961
962 public Collection getAllServiceStatus()
963 throws FinderException{
964
965 ServiceStatusLocal s;
966 Collection coll = serviceStatusHome.
967 findAllServiceStatus();
Quellcode der Verwaltungs Server Komponente 357

968 Iterator iter = coll.iterator();


969 Vector v = new Vector();
970 while (iter.hasNext()){
971 s = (ServiceStatusLocal) iter.next();
972 v.add(createServiceStatusClone(s));
973 }
974 return v;
975 }
976
977
978 public Collection getServiceStatusByInterpretation(
979 String interpretation) throws FinderException{
980
981 ServiceStatusLocal s;
982 Collection coll = serviceStatusHome.
983 findByInterpretation(interpretation);
984 Iterator iter = coll.iterator();
985 Vector v = new Vector();
986 while (iter.hasNext()){
987 s = (ServiceStatusLocal) iter.next();
988 v.add(createServiceStatusClone(s));
989 }
990 return v;
991 }
992
993
994 //***********************************************************
995 // ServiceType
996 //***********************************************************
997 public void addServiceType(ServiceTypeClone type)
998 throws CreateException{
999
1000 if (type.getInterpretation() == null){
1001 throw new CreateException("Null Werte nicht erlaubt!!");
1002 }else{
1003 serviceTypeHome.create(type.getID(),
1004 type.getInterpretation());
1005 }
1006 }
1007
1008
1009 public void removeServiceType(ServiceTypeClone type)
1010 throws FinderException, RemoveException{
1011
1012 ServiceTypeLocal s;
Quellcode der Verwaltungs Server Komponente 358

1013 s = serviceTypeHome.findByPrimaryKey(
1014 new Integer(type.getID()));
1015 if (s==null){
1016 throw new FinderException("Kein ServiceType mit "+
1017 "ID "+type.getID() + "gefunden.");
1018 }else{
1019 s.remove();
1020 }
1021 }
1022
1023
1024 public void changeServiceType(ServiceTypeClone type)
1025 throws FinderException{
1026
1027 if (type.getInterpretation() == null){
1028 throw new FinderException("Null Werte nicht erlaubt!!");
1029 }else{
1030 ServiceTypeLocal s;
1031 s = serviceTypeHome.findByPrimaryKey(
1032 new Integer(type.getID()));
1033 if (s==null){
1034 throw new FinderException("Kein ServiceType mit "+
1035 "ID "+type.getID() + "gefunden.");
1036 }else{
1037 s.setInterpretation(type.getInterpretation());
1038 }
1039 }
1040 }
1041
1042
1043 public ServiceTypeClone getServiceTypeByPrimaryKey(
1044 int id) throws FinderException{
1045
1046 ServiceTypeLocal s;
1047 s = serviceTypeHome.findByPrimaryKey(new Integer(id));
1048 if (s==null){
1049 return null;
1050 }
1051 return createServiceTypeClone(s);
1052 }
1053
1054
1055 public Collection getAllServiceType()
1056 throws FinderException{
1057
Quellcode der Verwaltungs Server Komponente 359

1058 ServiceTypeLocal s;
1059 Collection coll = serviceTypeHome.findAllServiceType();
1060 Iterator iter = coll.iterator();
1061 Vector v = new Vector();
1062 while (iter.hasNext()){
1063 s = (ServiceTypeLocal) iter.next();
1064 v.add(createServiceTypeClone(s));
1065 }
1066 return v;
1067 }
1068
1069
1070 public Collection getServiceTypeByInterpretation(
1071 String interpretation) throws FinderException{
1072
1073 ServiceTypeLocal s;
1074 Collection coll = serviceTypeHome.
1075 findByInterpretation(interpretation);
1076 Iterator iter = coll.iterator();
1077 Vector v = new Vector();
1078 while (iter.hasNext()){
1079 s = (ServiceTypeLocal) iter.next();
1080 v.add(createServiceTypeClone(s));
1081 }
1082 return v;
1083 }
1084
1085
1086 //***********************************************************
1087 // R_AppServerService
1088 //***********************************************************
1089 public void addR_AppServerService(R_AppServerServiceClone
1090 appServerService) throws CreateException{
1091
1092 if ((appServerService.getHost() == null)||
1093 (appServerService.getLookupName() == null)||
1094 (appServerService.getAppServer() == null)||
1095 (appServerService.getService() == null)){
1096 throw new CreateException("Null Werte nicht erlaubt!!");
1097 }else{
1098 r_AppServerServiceHome.create(appServerService.getID(),
1099 appServerService.getMinSpeed(),
1100 appServerService.getHost(),
1101 appServerService.getPort(),
1102 appServerService.getLookupName(),
Quellcode der Verwaltungs Server Komponente 360

1103 appServerService.getAppServer(),
1104 appServerService.getService(),
1105 appServerService.getServiceStatus(),
1106 appServerService.getServiceType());
1107 }
1108 }
1109
1110
1111 public void removeR_AppServerService(
1112 R_AppServerServiceClone appServerService)
1113 throws FinderException, RemoveException{
1114
1115 R_AppServerServiceLocal a;
1116 a = r_AppServerServiceHome.findByPrimaryKey(
1117 new Long(appServerService.getID()));
1118 if (a==null){
1119 throw new FinderException("Kein R_AppServerService "+
1120 "mit ID "+appServerService.getID() + "gefunden.");
1121 }else{
1122 a.remove();
1123 }
1124 }
1125
1126
1127 public void changeR_AppServerService(
1128 R_AppServerServiceClone appServerService)
1129 throws FinderException{
1130
1131 if ((appServerService.getHost() == null)||
1132 (appServerService.getLookupName() == null)||
1133 (appServerService.getAppServer() == null)||
1134 (appServerService.getService() == null)){
1135 throw new FinderException("Null Werte nicht erlaubt!!");
1136 }else{
1137 R_AppServerServiceLocal a;
1138 a = r_AppServerServiceHome.findByPrimaryKey(
1139 new Long(appServerService.getID()));
1140 if (a==null){
1141 throw new FinderException("Kein R_AppServerService "+
1142 "mit ID "+appServerService.getID() + "gefunden.");
1143 }else{
1144 a.setHost(appServerService.getHost());
1145 a.setPort(appServerService.getPort());
1146 a.setLookupName(appServerService.getLookupName());
1147 a.setMinSpeed(appServerService.getMinSpeed());
Quellcode der Verwaltungs Server Komponente 361

1148 a.setServiceStatus(
1149 appServerService.getServiceStatus());
1150 }
1151 }
1152 }
1153
1154
1155 public R_AppServerServiceClone
1156 getR_AppServerServiceByPrimaryKey(
1157 long id) throws FinderException{
1158
1159 R_AppServerServiceLocal r;
1160 r =r_AppServerServiceHome.findByPrimaryKey(new Long(id));
1161 if (r==null){
1162 return null;
1163 }
1164 return createR_AppServerServiceClone(r);
1165 }
1166
1167
1168 public Collection getAllR_AppServerService()
1169 throws FinderException{
1170
1171 R_AppServerServiceLocal r;
1172 Collection coll = r_AppServerServiceHome.
1173 findAllR_AppServerService();
1174 Iterator iter = coll.iterator();
1175 Vector v = new Vector();
1176 while (iter.hasNext()){
1177 r = (R_AppServerServiceLocal) iter.next();
1178 v.add(createR_AppServerServiceClone(r));
1179 }
1180 return v;
1181 }
1182
1183
1184 public Collection getR_AppServerServiceByAppServer(String
1185 name) throws FinderException{
1186
1187 R_AppServerServiceLocal r;
1188 Collection coll = r_AppServerServiceHome.
1189 findByAppServer(name);
1190 Iterator iter = coll.iterator();
1191 Vector v = new Vector();
1192 while (iter.hasNext()){
Quellcode der Verwaltungs Server Komponente 362

1193 r = (R_AppServerServiceLocal) iter.next();


1194 v.add(createR_AppServerServiceClone(r));
1195 }
1196 return v;
1197 }
1198
1199
1200 public Collection getR_AppServerServiceByService(String
1201 name) throws FinderException{
1202
1203 R_AppServerServiceLocal r;
1204 Collection coll = r_AppServerServiceHome.
1205 findByService(name);
1206 Iterator iter = coll.iterator();
1207 Vector v = new Vector();
1208 while (iter.hasNext()){
1209 r = (R_AppServerServiceLocal) iter.next();
1210 v.add(createR_AppServerServiceClone(r));
1211 }
1212 return v;
1213 }
1214
1215
1216 public R_AppServerServiceClone
1217 getR_AppServerServiceByAppServerAndService(String
1218 appServer, String service)
1219 throws FinderException{
1220
1221 R_AppServerServiceLocal r;
1222 Collection coll = r_AppServerServiceHome.
1223 findByAppServerAndService(appServer,service);
1224 Iterator iter = coll.iterator();
1225 if (iter.hasNext()){
1226 r = (R_AppServerServiceLocal) iter.next();
1227 return createR_AppServerServiceClone(r);
1228 }
1229 return null;
1230 }
1231
1232
1233 public Collection getR_AppServerServiceByHost(String
1234 host) throws FinderException{
1235
1236 R_AppServerServiceLocal r;
1237 Collection coll = r_AppServerServiceHome.
Quellcode der Verwaltungs Server Komponente 363

1238 findByHost(host);
1239 Iterator iter = coll.iterator();
1240 Vector v = new Vector();
1241 while (iter.hasNext()){
1242 r = (R_AppServerServiceLocal) iter.next();
1243 v.add(createR_AppServerServiceClone(r));
1244 }
1245 return v;
1246 }
1247
1248
1249 public Collection getR_AppServerServiceByStatus(int id)
1250 throws FinderException{
1251
1252 R_AppServerServiceLocal r;
1253 Collection coll = r_AppServerServiceHome.
1254 findByServiceStatus(id);
1255
1256 Iterator iter = coll.iterator();
1257 Vector v = new Vector();
1258 while (iter.hasNext()){
1259 r = (R_AppServerServiceLocal) iter.next();
1260 v.add(createR_AppServerServiceClone(r));
1261 }
1262 return v;
1263 }
1264
1265
1266 public Collection getR_AppServerServiceByType(int id)
1267 throws FinderException{
1268
1269 R_AppServerServiceLocal r;
1270 Collection coll = r_AppServerServiceHome.
1271 findByServiceType(id);
1272
1273 Iterator iter = coll.iterator();
1274 Vector v = new Vector();
1275 while (iter.hasNext()){
1276 r = (R_AppServerServiceLocal) iter.next();
1277 v.add(createR_AppServerServiceClone(r));
1278 }
1279 return v;
1280 }
1281
1282
Quellcode der Verwaltungs Server Komponente 364

1283 //***********************************************************
1284 // R_BenchmarkObserver
1285 //***********************************************************
1286 public void registerBenchmarkToObserver(
1287 R_BenchmarkObserverClone assoziation)
1288 throws CreateException{
1289
1290 r_BenchmarkObserverHome.create(assoziation.getID(),
1291 assoziation.getBenchmark(),
1292 assoziation.getObserver());
1293 }
1294
1295
1296 public void unregisterBenchmarkFromObserver(
1297 R_BenchmarkObserverClone assoziation)
1298 throws FinderException, RemoveException{
1299
1300 R_BenchmarkObserverLocal b;
1301 b = r_BenchmarkObserverHome.findByPrimaryKey(
1302 new Long(assoziation.getID()));
1303 if (b==null){
1304 throw new FinderException("Kein R_BenchmarkObserver "+
1305 "mit ID "+assoziation.getID() + "gefunden.");
1306 }else{
1307 b.remove();
1308 }
1309 }
1310
1311
1312 public R_BenchmarkObserverClone
1313 getR_BenchmarkObserverByPrimaryKey(
1314 long id) throws FinderException{
1315
1316 R_BenchmarkObserverLocal r;
1317 r = r_BenchmarkObserverHome.
1318 findByPrimaryKey(new Long(id));
1319 if (r==null){
1320 return null;
1321 }
1322 return createR_BenchmarkObserverClone(r);
1323 }
1324
1325
1326 public Collection getAllR_BenchmarkObserver()
1327 throws FinderException{
Quellcode der Verwaltungs Server Komponente 365

1328
1329 R_BenchmarkObserverLocal r;
1330 Collection coll = r_BenchmarkObserverHome.
1331 findAllR_BenchmarkObserver();
1332 Iterator iter = coll.iterator();
1333 Vector v = new Vector();
1334 while (iter.hasNext()){
1335 r = (R_BenchmarkObserverLocal) iter.next();
1336 v.add(createR_BenchmarkObserverClone(r));
1337 }
1338 return v;
1339 }
1340
1341
1342 public Collection
1343 getR_BenchmarkObserverByBenchmark(long id)
1344 throws FinderException{
1345
1346 R_BenchmarkObserverLocal r;
1347 Collection coll = r_BenchmarkObserverHome.
1348 findByBenchmark(id);
1349 Iterator iter = coll.iterator();
1350 Vector v = new Vector();
1351 while (iter.hasNext()){
1352 r = (R_BenchmarkObserverLocal) iter.next();
1353 v.add(createR_BenchmarkObserverClone(r));
1354 }
1355 return v;
1356 }
1357
1358
1359 public Collection
1360 getR_BenchmarkObserverByObserver(long id)
1361 throws FinderException{
1362
1363 R_BenchmarkObserverLocal r;
1364 Collection coll = r_BenchmarkObserverHome.
1365 findByObserver(id);
1366 Iterator iter = coll.iterator();
1367 Vector v = new Vector();
1368 while (iter.hasNext()){
1369 r = (R_BenchmarkObserverLocal) iter.next();
1370 v.add(createR_BenchmarkObserverClone(r));
1371 }
1372 return v;
Quellcode der Verwaltungs Server Komponente 366

1373 }
1374
1375
1376 //***********************************************************
1377 // R_AppServerServiceObserver
1378 //***********************************************************
1379 public void registerAppServerServiceToObserver(
1380 R_AppServerServiceObserverClone assoziation)
1381 throws CreateException{
1382
1383 r_AppServerServiceObserverHome.
1384 create(assoziation.getID(),
1385 assoziation.getAppServerService(),
1386 assoziation.getObserver());
1387 }
1388
1389
1390 public void unregisterAppServerServiceFromObserver(
1391 R_AppServerServiceObserverClone assoziation)
1392 throws FinderException, RemoveException{
1393
1394 R_AppServerServiceObserverLocal b;
1395 b = r_AppServerServiceObserverHome.findByPrimaryKey(
1396 new Long(assoziation.getID()));
1397 if (b==null){
1398 throw new FinderException("Kein R_AppServerServiceObs"+
1399 "erver mit ID "+assoziation.getID() + "gefunden.");
1400 }else{
1401 b.remove();
1402 }
1403 }
1404
1405
1406 public R_AppServerServiceObserverClone
1407 getR_AppServerServiceObserverByPrimaryKey(
1408 long id) throws FinderException{
1409
1410 R_AppServerServiceObserverLocal r;
1411 r = r_AppServerServiceObserverHome.
1412 findByPrimaryKey(new Long(id));
1413 if (r==null){
1414 return null;
1415 }
1416 return createR_AppServerServiceObserverClone(r);
1417 }
Quellcode der Verwaltungs Server Komponente 367

1418
1419
1420 public Collection getAllR_AppServerServiceObserver()
1421 throws FinderException{
1422
1423 R_AppServerServiceObserverLocal r;
1424 Collection coll = r_AppServerServiceObserverHome.
1425 findAllR_AppServerServiceObserver();
1426 Iterator iter = coll.iterator();
1427 Vector v = new Vector();
1428 while (iter.hasNext()){
1429 r = (R_AppServerServiceObserverLocal) iter.next();
1430 v.add(createR_AppServerServiceObserverClone(r));
1431 }
1432 return v;
1433 }
1434
1435
1436 public Collection
1437 getR_AppServerServiceObserverByAppServerService(long id)
1438 throws FinderException{
1439
1440 R_AppServerServiceObserverLocal r;
1441 Collection coll = r_AppServerServiceObserverHome.
1442 findByAppServerService(id);
1443 Iterator iter = coll.iterator();
1444 Vector v = new Vector();
1445 while (iter.hasNext()){
1446 r = (R_AppServerServiceObserverLocal) iter.next();
1447 v.add(createR_AppServerServiceObserverClone(r));
1448 }
1449 return v;
1450 }
1451
1452
1453 public Collection
1454 getR_AppServerServiceObserverByObserver(long id)
1455 throws FinderException{
1456
1457 R_AppServerServiceObserverLocal r;
1458 Collection coll = r_AppServerServiceObserverHome.
1459 findByObserver(id);
1460 Iterator iter = coll.iterator();
1461 Vector v = new Vector();
1462 while (iter.hasNext()){
Quellcode der Verwaltungs Server Komponente 368

1463 r = (R_AppServerServiceObserverLocal) iter.next();


1464 v.add(createR_AppServerServiceObserverClone(r));
1465 }
1466 return v;
1467 }
1468
1469
1470 /*
1471 **********************************************************
1472 * Private Methoden der Sesssion Bean
1473 **********************************************************
1474 */
1475
1476 /**
1477 * Erzeugt ein AdministratorClone Objekt für ein
1478 * AdministatorLocal Objekt (remote Objekt).
1479 * @param admin Das AdministatorLocal Objekt, das
1480 * umgewandelt werden soll.
1481 * @return Das neu erzeugte AdministratorClone Objekt.
1482 */
1483 private AdministratorClone createAdministratorClone
1484 (AdministratorLocal admin){
1485
1486 return new AdministratorClone(admin.getID(),
1487 admin.getName(),
1488 admin.getEMail());
1489 }
1490
1491
1492 /**
1493 * Erzeugt ein AppServerStatusClone Objekt für ein
1494 * AppServerStatusLocal Objekt (remote Objekt).
1495 * @param appServerStatus Das AppServerStatusLocal Objekt,
1496 * das umgewandelt werden soll.
1497 * @return Das neu erzeugte AppServerStatusClone Objekt.
1498 */
1499 private AppServerStatusClone createAppServerStatusClone
1500 (AppServerStatusLocal appServerStatus){
1501
1502 return new AppServerStatusClone(appServerStatus.getID(),
1503 appServerStatus.getInterpretation());
1504
1505 }
1506
1507
Quellcode der Verwaltungs Server Komponente 369

1508 /**
1509 * Erzeugt ein ApplicationServerClone Objekt für ein
1510 * ApplicationServerLocal Objekt (remote Objekt).
1511 * @param appServer Das ApplicationServerLocal Objekt,
1512 * das umgewandelt werden soll.
1513 * @return Das neu erzeugte ApplicationServerClone Objekt.
1514 */
1515 private ApplicationServerClone createApplicationServerClone
1516 (ApplicationServerLocal appServer){
1517
1518 return new ApplicationServerClone(appServer.getName(),
1519 appServer.getHost(),
1520 appServer.getAdministrator(),
1521 appServer.getAppServerStatus(),
1522 appServer.getBenchmark());
1523 }
1524
1525
1526 /**
1527 * Erzeugt ein BenchmarkClone Objekt für ein BenchmarkLocal
1528 * Objekt (remote Objekt).
1529 * @param benchmark Das BenchmarkLocal Objekt, das
1530 * umgewandelt werden soll.
1531 * @return Das neu erzeugte BenchmarkClone Objekt.
1532 */
1533 private BenchmarkClone createBenchmarkClone
1534 (BenchmarkLocal benchmark){
1535
1536 return new BenchmarkClone(benchmark.getID(),
1537 benchmark.getHost(),
1538 benchmark.getPort(),
1539 benchmark.getLookupName());
1540 }
1541
1542
1543 /**
1544 * Erzeugt ein ObserverClone Objekt für ein ObserverLocal
1545 * Objekt (remote Objekt).
1546 * @param benchmark Das ObserverLocal Objekt, das
1547 * umgewandelt werden soll.
1548 * @return Das neu erzeugte ObserverClone Objekt.
1549 */
1550 private ObserverClone createObserverClone
1551 (ObserverLocal observer){
1552
Quellcode der Verwaltungs Server Komponente 370

1553 return new ObserverClone(observer.getID(),


1554 observer.getHost(),
1555 observer.getPort(),
1556 observer.getLookupName(),
1557 observer.getCheckSpeedInterval(),
1558 observer.getLookupServiceInterval(),
1559 observer.getReconnectBenchInterval(),
1560 observer.getMaxCheckSpeedTime(),
1561 observer.getMaxServiceLookupTime(),
1562 observer.getMaxBenchLookupTime(),
1563 observer.isRemoteControl());
1564 }
1565
1566
1567 /**
1568 * Erzeugt ein ServiceClone Objekt für ein ServiceLocal
1569 * Objekt (remote Objekt).
1570 * @param service Das ServiceLocal Objekt, das
1571 * umgewandelt werden soll.
1572 * @return Das neu erzeugte createServiceClone Objekt.
1573 */
1574 private ServiceClone createServiceClone
1575 (ServiceLocal service){
1576
1577 return new ServiceClone(service.getName(),
1578 service.getDescription());
1579 }
1580
1581
1582 /**
1583 * Erzeugt ein ServiceStatusClone Objekt für ein
1584 * ServiceStatusLocal Objekt (remote Objekt).
1585 * @param status Das ServiceStatusLocal Objekt, das
1586 * umgewandelt werden soll.
1587 * @return Das neu erzeugte ServiceStatusClone Objekt.
1588 */
1589 private ServiceStatusClone createServiceStatusClone
1590 (ServiceStatusLocal status){
1591
1592 return new ServiceStatusClone(status.getID(),
1593 status.getInterpretation());
1594 }
1595
1596
1597 /**
Quellcode der Verwaltungs Server Komponente 371

1598 * Erzeugt ein ServiceTypeClone Objekt für ein


1599 * ServiceTypeLocal Objekt (remote Objekt).
1600 * @param servicetype Das ServiceTypeLocal Objekt, das
1601 * umgewandelt werden soll.
1602 * @return Das neu erzeugte ServiceTypeClone Objekt.
1603 */
1604 private ServiceTypeClone createServiceTypeClone
1605 (ServiceTypeLocal servicetype){
1606
1607 return new ServiceTypeClone(servicetype.getID(),
1608 servicetype.getInterpretation());
1609 }
1610
1611
1612 /**
1613 * Erzeugt ein R_AppServerServiceClone Objekt für ein
1614 * R_AppServerServiceLocal Objekt (remote Objekt).
1615 * @param assoziation Das R_AppServerServiceLocal Objekt,
1616 * das umgewandelt werden soll.
1617 * @return Das neu erzeugte R_AppServerServiceClone Objekt.
1618 */
1619 private R_AppServerServiceClone
1620 createR_AppServerServiceClone
1621 (R_AppServerServiceLocal assoziation){
1622
1623 return new R_AppServerServiceClone(assoziation.getID(),
1624 assoziation.getMinSpeed(),
1625 assoziation.getHost(),
1626 assoziation.getPort(),
1627 assoziation.getLookupName(),
1628 assoziation.getAppServer(),
1629 assoziation.getService(),
1630 assoziation.getServiceStatus(),
1631 assoziation.getServiceType());
1632 }
1633
1634
1635 /**
1636 * Erzeugt ein R_AppServerServiceObserverClone Objekt für
1637 * ein R_AppServerServiceObserverLocal Objekt (remote
1638 * Objekt).
1639 * @param assoziation Das R_AppServerServiceObserverLocal
1640 * Objekt, das umgewandelt werden soll.
1641 * @return Das neu erzeugte R_AppServerServiceObserverClone
1642 * Objekt.
Quellcode der Verwaltungs Server Komponente 372

1643 */
1644 private R_AppServerServiceObserverClone
1645 createR_AppServerServiceObserverClone
1646 (R_AppServerServiceObserverLocal assoziation){
1647
1648 return new R_AppServerServiceObserverClone(
1649 assoziation.getID(),
1650 assoziation.getAppServerService(),
1651 assoziation.getObserver());
1652 }
1653
1654
1655 /**
1656 * Erzeugt ein R_BenchmarkObserverClone Objekt für ein
1657 * R_BenchmarkObserverLocal Objekt (remote Objekt).
1658 * @param assoziation Das R_BenchmarkObserverLocal Objekt,
1659 * das umgewandelt werden soll.
1660 * @return Das neu erzeugte R_BenchmarkObserverClone
1661 * Objekt.
1662 */
1663 private R_BenchmarkObserverClone
1664 createR_BenchmarkObserverClone
1665 (R_BenchmarkObserverLocal assoziation){
1666
1667 return new R_BenchmarkObserverClone(assoziation.getID(),
1668 assoziation.getBenchmark(),
1669 assoziation.getObserver());
1670 }
1671
1672
1673
1674 public boolean resetobserver (long id) throws Exception{
1675
1676 ObserverLocal o = observerHome.findByPrimaryKey(
1677 new Long (id));
1678 if (o == null){
1679 return false;
1680 }
1681 AppServerListener appListener = new AppServerListener(
1682 // demnächst aus
1683
1684 host,port,"ServerAdminObserver");
1685 ServiceListener serviceListener = new ServiceListener(
1686 host,port,"ServerAdminObserver");
1687
Quellcode der Verwaltungs Server Komponente 373

1688 IObserver obs = (IObserver) java.rmi.Naming.lookup(


1689 "rmi://" + o.getHost() + ":" + o.getPort()+"/"+
1690 o.getLookupName());
1691
1692 obs.setCheckSpeedInterval(o.getCheckSpeedInterval());
1693 obs.setLookupServiceInterval(o.
1694 getLookupServiceInterval());
1695 obs.setReconnectBenchInterval(o.
1696 getReconnectBenchInterval());
1697 obs.setMaxCheckSpeedTime(o.getMaxCheckSpeedTime());
1698 obs.setMaxServiceLookupTime(o.getMaxServiceLookupTime());
1699 obs.setMaxReconnectLookupTime(o.getMaxBenchLookupTime());
1700
1701 // Applikations-Server
1702 Collection coll = r_BenchmarkObserverHome.
1703 findByObserver(o.getID());
1704 Iterator iter = coll.iterator();
1705 while (iter.hasNext()){
1706 R_BenchmarkObserverLocal r =(R_BenchmarkObserverLocal)
1707 iter.next();
1708 ApplicationServerClone a =
1709 getApplicationServerByBenchmark(r.getBenchmark());
1710 BenchmarkLocal b = benchmarkHome.
1711 findByPrimaryKey(new Long(r.getBenchmark()));
1712 obs.addAppServerListener(a.getName(),appListener);
1713 obs.registerAppServer(a.getName(),b.getHost(),""+
1714 b.getPort(),b.getLookupName());
1715 }
1716 //Service;
1717 coll = r_AppServerServiceObserverHome.findByObserver(
1718 o.getID());
1719 iter = coll.iterator();
1720 while (iter.hasNext()){
1721 R_AppServerServiceObserverLocal r2 =
1722 (R_AppServerServiceObserverLocal) iter.next();
1723 R_AppServerServiceLocal s = r_AppServerServiceHome.
1724 findByPrimaryKey(new Long(r2.getAppServerService()));
1725
1726 obs.addServiceListener(s.getAppServer(),
1727 s.getService(),serviceListener);
1728 obs.registerAppServer(s.getAppServer());
1729 obs.registerAppServerService(s.getAppServer(),
1730 s.getService(),
1731 s.getMinSpeed(),
1732 s.getHost(),
Quellcode der Verwaltungs Server Komponente 374

1733 s.getPort(),
1734 s.getLookupName(),
1735 s.getServiceType());
1736 }
1737 return true;
1738 }
1739 }

C.4. Quellcode der Schnittstelle ServerAdminClientHome

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: ServerAdminClientHome
9 * Date: 14.03.2003
10 * Version: 1.0
11 */
12
13 package serverAdmin;
14
15 import javax.ejb.*;
16 import java.rmi.*;
17
18 /**
19 * Dies ist das home Interface für die
20 * ServerAdminClientHome. Alle Methoden, die hier
21 * deklariert sind, können später von einem remote Client
22 * aufgerufen werden.
23 */
24 public interface ServerAdminClientHome extends EJBHome {
25
26 public ServerAdminClient create() throws CreateException,
27 RemoteException;
28 }
Quellcode der Verwaltungs Server Komponente 375

C.5. Quellcode der Schnittstelle ServerAdminClient

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: ServerAdminClient
9 * Date: 14.03.2003
10 * Version: 1.0
11 */
12
13 package serverAdmin;
14
15 import javax.ejb.*;
16 import java.rmi.*;
17 import java.util.*;
18
19 /**
20 * Dies ist das remote Interface für die
21 * ServerAdminClientBean.
22 * Alle Methoden, die hier deklariert sind, können später von
23 * einem remote Client aufgerufen werden.
24 */
25 public interface ServerAdminClient extends EJBObject {
26
27
28 /**
29 * Gibt eine Liste von verfügbaren Applikations-Servern
30 * für den angeforderten Dienst zurück.
31 * @param serviceName Der Name des Dienstes, nach dem
32 * gesucht werden soll.
33 */
34 public Collection getApplicationServer(String serviceName)
35 throws FinderException, RemoteException;
36 }

C.6. Quellcode der Klasse ServerAdminClientBean

1 /*
Quellcode der Verwaltungs Server Komponente 376

2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: ServerAdminClientBean
9 * Date: 15.03.2003
10 * Version: 1.0
11 */
12
13 package serverAdmin;
14
15 import javax.ejb.*;
16 import serverAdmin.entity.clone.*;
17 import serverAdmin.entity.*;
18 import javax.naming.*;
19 import javax.rmi.*;
20 import java.util.*;
21 import java.rmi.*;
22
23
24 /**
25 * Session Bean, die von den Clients aufgerufen wird, um
26 * verfügbare Applikations-Server für einen Dienst zu
27 * bekommen. Der Client erhält eine Kopie der Daten aus
28 * der Datenbank.
29 */
30 public class ServerAdminClientBean implements SessionBean {
31
32 SessionContext ctx;
33
34 // Benötitge Methoden einer jeden Session Bean
35 public void setSessionContext(SessionContext ctx){
36
37 this.ctx = ctx;
38 }
39
40
41 public void ejbRemove(){
42
43 }
44
45
46 public void ejbActivate(){
Quellcode der Verwaltungs Server Komponente 377

47
48 }
49
50
51 public void ejbPassivate(){
52
53 }
54
55
56 public void ejbCreate() throws CreateException{
57
58 }
59
60 /**
61 * Initialisierung der Session Bean. Es wird eine Referenz
62 * zum ServerAdminHome Objekt erstellt.
63 */
64 private ServerAdmin init() {
65
66 InitialContext context;
67 Object obj;
68 try{
69 context = new InitialContext();
70 obj = context.lookup
71 ("java:comp/env/ejb/ServerAdminHome");
72 ServerAdminHome serverAdminHome = (ServerAdminHome)
73 PortableRemoteObject.
74 narrow(obj, ServerAdminHome.class);
75 return serverAdminHome.create();
76 }catch(Exception e){
77 e.printStackTrace();
78 return null;
79 }
80 }
81
82 public Collection getApplicationServer(String serviceName)
83 throws FinderException{
84
85 ServerAdmin server = init();
86 Collection coll =null;
87 try{
88 coll = server.
89 getR_AppServerServiceByService(serviceName);
90 }catch(RemoteException e){
91 // Sollte nie auftreten da lokal !!
Quellcode der Verwaltungs Server Komponente 378

92 e.printStackTrace();
93 }
94 Iterator iter = coll.iterator();
95 Vector v = new Vector();
96 R_AppServerServiceClone r;
97 AppServerServiceClone clone;
98 while (iter.hasNext()){
99 r = (R_AppServerServiceClone) iter.next();
100 if (r.getServiceStatus()==1){
101 ApplicationServerClone a=null;
102 try{
103 a = server.getApplicationServerByPrimaryKey(
104 r.getAppServer());
105 }catch(RemoteException e){
106 // Sollte nie auftreten da lokal !!
107 e.printStackTrace();
108 }
109 if (a.getAppServerStatus() == 1){
110 clone = new AppServerServiceClone(r.getHost(),
111 r.getPort(),r.getLookupName(),
112 r.getAppServer(),r.getServiceType());
113 v.add(clone);
114 }
115 }
116 }
117 return v;
118 }
119 }

C.7. Quellcode der Klasse AppServerServiceClone

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: AppServerServiceClone
9 * Date: 15.03.2003
10 * Version: 1.0
11 */
Quellcode der Verwaltungs Server Komponente 379

12
13 package serverAdmin;
14
15 import java.io.Serializable;
16
17
18 /**
19 * Diese Klasse repräsentiert einen Dienst auf einem
20 * Applikations-Server. Die Daten der Dienste und
21 * Applikations-Server sind in einer Datenbank eingetragen.
22 * Mit Hilfe von EJBs kann auf die Daten zugegriffen werden.
23 * Diese Klasse dient als Mittelsmann zwischen einem Client
24 * und dem Verwaltungs-Server. Ein Client ist lediglich daran
25 * interessiert, verfügbare Applikations-Server für einen
26 * Dienst zu bekommen. Aus diesem Grund stellt diese Klasse
27 * nur get... Methoden bereit. Ein Objekt dieser Klasse wird
28 * von einer Session Bean erzeugt und als Ergebnis einer
29 * Methode dem Client geliefert.
30 */
31 public class AppServerServiceClone implements Serializable{
32
33
34 private String host;
35 private int port;
36 private String lookupName;
37 private String appServer;
38 private int serviceType;
39
40
41 /**
42 * Spezifiziert einen RMI Dienst.
43 */
44 public static final int RMI = 1;
45
46 /**
47 * Spezifiziert einen EJB Dienst auf einem SUN
48 * Applikations-Server.
49 */
50 public static final int EJB_SUN = 2;
51
52 /**
53 * Spezifiziert einen EJB Dienst auf einem JBoss
54 * Applikations-Server.
55 */
56 public static final int EJB_JBOSS = 3;
Quellcode der Verwaltungs Server Komponente 380

57
58 /**
59 * Erstellt ein AppServerServiceClone Objekt
60 * @param host Der Rechnername der genutzen RMI-Registry.
61 * @param port Die Adresses der genutzen RMI-Registry.
62 * @param lookupName Der Lookup-Name, unter dem das
63 * Dienst Objekt in der genutzen RMI-Registry abgelegt
64 * ist.
65 * @param appServer Der Applikation-Server des Dienstes.
66 * @param serviceType Der Typ des Dienstes.
67 */
68 public AppServerServiceClone(String host, int port, String
69 lookupName, String appServer, int serviceType){
70
71 this.host = host;
72 this.port = port;
73 this.lookupName = lookupName;
74 this.appServer=appServer;
75 this.serviceType = serviceType;
76 }
77
78
79 /**
80 * Gibt den Rechnernamen des Dienstes zurück.
81 * @return Rechnernamen des Dienstes.
82 */
83 public String getHost(){
84
85 return host;
86 }
87
88
89 /**
90 * Gibt die Adresse der RMI-Registry, auf dem sich das
91 * Dienst Objekt befindet, zurück.
92 * @return Adresse der RMI-Registry.
93 */
94 public int getPort(){
95
96 return port;
97 }
98
99
100 /**
101 * Gibt den Namen, unter dem der Dienst in der
Quellcode der Verwaltungs Server Komponente 381

102 * RMI-Registry abgelegt ist, zurück.


103 * @return Lookup-Name des Dienst Objektes.
104 */
105 public String getLookupName(){
106
107 return lookupName;
108 }
109
110
111 /**
112 * Gibt den Applikations-Server des Dienstes zurück.
113 * @return Applikations-Server der Dienstes.
114 */
115 public String getAppServer(){
116
117 return appServer;
118 }
119
120
121 /**
122 * Gibt den genauen Typ des Dienstes zurück.
123 * @return Typ des Dienstes.
124 */
125 public int getServiceType(){
126
127 return serviceType;
128 }
129 }

C.8. Quellcode der Schnittstelle ServerAdminObserverHome

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: ServerAdminObserverHome
9 * Date: 14.03.2003
10 * Version: 1.0
11 */
Quellcode der Verwaltungs Server Komponente 382

12
13 package serverAdmin;
14
15 import javax.ejb.*;
16 import java.rmi.*;
17
18 /**
19 * Dies ist das home Interface für die
20 * ServerAdminObserverBean. Alle Methoden, die hier
21 * deklariert sind, können später von einem remote Client
22 * aufgerufen werden.
23 */
24 public interface ServerAdminObserverHome extends EJBHome {
25
26 public ServerAdminObserver create() throws CreateException,
27 RemoteException;
28 }

C.9. Quellcode der Schnittstelle ServerAdminObserver

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: ServerAdminObserver
9 * Date: 14.03.2003
10 * Version: 1.0
11 */
12
13 package serverAdmin;
14
15 import javax.ejb.*;
16 import java.rmi.*;
17 import observer.*;
18
19 /**
20 * Dies ist das remote Interface für die
21 * ServerAdminObserverBean.
22 * Alle Methoden, die hier deklariert sind, können später von
Quellcode der Verwaltungs Server Komponente 383

23 * einem remote Client aufgerufen werden.


24 */
25 public interface ServerAdminObserver extends EJBObject {
26
27 /**
28 * Ändert den Status eines Applikations-Servers in der
29 * Datenbank.
30 * @param e AppServerEvent mit dem neuen Status des
31 * Applikations-Servers.
32 */
33 public void notify(AppServerEvent e)throws RemoteException;
34
35
36 /**
37 * Ändert den Status eines Dienstes in der Datenbank.
38 * @param e ServiceEvent mit dem neuen Status des Dienstes.
39 */
40 public void notify(ServiceEvent e)throws RemoteException;
41 }

C.10. Quellcode der Klasse ServerAdminObserverBean

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: ServerAdminObserverBean
9 * Date: 14.03.2003
10 * Version: 1.0
11 */
12
13 package serverAdmin;
14
15 import javax.ejb.*;
16 import serverAdmin.entity.clone.*;
17 import serverAdmin.entity.*;
18 import javax.naming.*;
19 import javax.rmi.*;
20 import java.util.*;
Quellcode der Verwaltungs Server Komponente 384

21 import observer.*;
22
23 /**
24 * Session Bean, die von den Beobachter aufgerufen wird wenn
25 * sich der Status eines Dienstes oder Applikations-Server
26 * ändert. Diese Bean ändert daraufhin den Status des
27 * jeweiligen Dienstes oder Applikations-Servers.
28 */
29 public class ServerAdminObserverBean implements SessionBean {
30
31 SessionContext ctx;
32
33 // Benötitge Methoden einer jeden Session Bean
34 public void setSessionContext(SessionContext ctx){
35
36 this.ctx = ctx;
37 }
38
39
40 public void ejbRemove(){
41
42 }
43
44
45 public void ejbActivate(){
46
47 }
48
49
50 public void ejbPassivate(){
51
52 }
53
54
55 public void ejbCreate() throws CreateException{
56
57 }
58
59 /**
60 * Initialisierung der Session Bean. Es wird eine Referenz
61 * zum ServerAdminHome Objekt erstellt.
62 */
63 private ServerAdmin init() {
64 InitialContext context;
65 Object obj;
Quellcode der Verwaltungs Server Komponente 385

66
67 try{
68 context = new InitialContext();
69 obj = context.lookup
70 ("java:comp/env/ejb/ServerAdminHome");
71 ServerAdminHome serverAdminHome = (ServerAdminHome)
72 PortableRemoteObject.
73 narrow(obj, ServerAdminHome.class);
74 return serverAdminHome.create();
75 }catch(Exception e){
76 e.printStackTrace();
77 return null;
78 }
79 }
80
81
82 public void notify(AppServerEvent e){
83
84 try{
85 ServerAdmin server = init();
86 ApplicationServerClone a = server.
87 getApplicationServerByPrimaryKey(
88 e.getApplikationServerName());
89
90 if (a.getAppServerStatus() != e.getType()){
91 a.setAppServerStatus(e.getType());
92 server.changeApplicationServer(a);
93 }
94 }catch(Exception exp){
95 exp.printStackTrace();
96 }
97 }
98
99 public void notify(ServiceEvent e){
100
101 try{
102 ServerAdmin server = init();
103 R_AppServerServiceClone r = server.
104 getR_AppServerServiceByAppServerAndService(
105 e.getApplikationServerName(),e.getServiceName());
106
107 if (r.getServiceStatus() != e.getType()){
108 r.setServiceStatus(e.getType());
109 server.changeR_AppServerService(r);
110 }
Quellcode der Verwaltungs Server Komponente 386

111 }catch(Exception exp){


112 exp.printStackTrace();
113 }
114 }
115 }

C.11. Quellcode der Klasse AppServerListener

1 /*
2 *
3 * Fachhochschule Oldenburg/ Ostfriesland/ Wilhelmshaven
4 * Fachbereich Technik
5 * Labor für Parallele Prozesse
6 *
7 * Author: Karsten Wolke (mail@Karsten-Wolke.de)
8 * Class: AppServerListener
9 * Date: 14.03.2003
10 * Version: 1.0
11 */
12
13 package serverAdmin;
14
15 import observer.*;
16 import java.rmi.RemoteException;
17 import java.io.*;
18 import javax.ejb.*;
19 import serverAdmin.entity.clone.*;
20 import serverAdmin.entity.*;
21 import javax.naming.*;
22 import javax.rmi.*;
23 import java.util.*;
24
25
26 /**
27 * Die Implementierung der IAppServerListener Schnittstelle
28 * aus dem observer Paket. Mit Objekten dieser Klasse können
29 * Verwaltungs-Server Interesse bei den entsprechenden
30 * Beobachtern anmelden.
31 */
32 public class AppServerListener implements IAppServerListener,
33 Serializable{
34
Quellcode der Verwaltungs Server Komponente 387

35 String host;
36 String port;
37 String lookup;
38 InitialContext ctx;
39 ServerAdminObserverHome home=null;
40 ServerAdminObserver server;
41
42 public AppServerListener(