You are on page 1of 697

eclipse_v01.book Seite 1 Montag, 30.

Januar 2006 12:02 12

Kai Brüssau (Hrsg.)


Oliver Widder (Hrsg.)
Herbert Brückner
Martin Lippert
Matthias Lübken
Birgit Schwartz-Reinken
Lars Wunderlich

Eclipse – die Plattform


eclipse_v01.book Seite 2 Montag, 30. Januar 2006 12:02 12
eclipse_v01.book Seite 3 Montag, 30. Januar 2006 12:02 12

Kai Brüssau (Hrsg.)


Oliver Widder (Hrsg.)
Herbert Brückner
Martin Lippert
Matthias Lübken
Birgit Schwartz-Reinken
Lars Wunderlich

– die Plattform
Enterprise-Java mit Eclipse 3.1
eclipse_v01.book Seite 4 Montag, 30. Januar 2006 12:02 12

Kai Büssau / Oliver Widder (Hrsg.): Eclipse – die Plattform


Enterprise-Java mit Eclipse 3.1
Frankfurt, 2006
ISBN 3-935042-82-5

© 2006 entwickler.press, ein Imprint der Software & Support Verlag GmbH

http://www.entwickler-press.de
http://www.software-support.biz

Ihr Kontakt zum Lektorat und dem Verlag: lektorat@entwickler-press.de

Bibliografische Information Der Deutschen Bibliothek


Die Deutsche Bibliothek verzeichnet diese Publikation in der Deutschen Nationalbiblio-
grafie; detaillierte bibliografische Daten sind im Internet über http://dnb.ddb.de abrufbar.

Korrektorat: Petra Kienle


Satz: text & form GbR, Carsten Kienle
Umschlaggestaltung: Melanie Hahn
Belichtung, Druck und Bindung: M.P. Media-Print Informationstechnologie GmbH,
Paderborn.
Alle Rechte, auch für Übersetzungen, sind vorbehalten. Reproduktion jeglicher Art (Foto-
kopie, Nachdruck, Mikrofilm, Erfassung auf elektronischen Datenträgern oder andere Ver-
fahren) nur mit schriftlicher Genehmigung des Verlags. Jegliche Haftung für die Richtig-
keit des gesamten Werks kann, trotz sorgfältiger Prüfung durch Autor und Verlag, nicht
übernommen werden. Die im Buch genannten Produkte, Warenzeichen und Firmennamen
sind in der Regel durch deren Inhaber geschützt.
eclipse_v01.book Seite 5 Montag, 30. Januar 2006 12:02 12

Inhaltsverzeichnis

1 Einleitung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.1 Was ist Eclipse? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.2 Warum ein Buch über Eclipse?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.3 An wen richtet sich dieses Buch? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.4 Was erwartet Sie in diesem Buch? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.5 Was benötigen Sie?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.6 Anforderungen an eine moderne IDE. . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.7 Open Source . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2 Wegweiser durch Eclipse. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.1 Perspektiven und Views von Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.1.1 Perspektiven . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.1.2 Perspektiven auswählen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.1.3 Die Java-Perspektive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.1.4 Neues Java-Projekt erzeugen . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.1.5 Package Explorer und View-Funktionen . . . . . . . . . . . . . . . . . . 21
2.1.6 Das Eclipse-Editorfenster . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.1.7 Outline View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.1.8 Tasks/Problems View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
2.1.9 To be done – Arbeiten mit Tasks. . . . . . . . . . . . . . . . . . . . . . . . . 25
2.1.10 Eigene Tasks definieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
2.2 Hilfe in Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.3 Eclipse-Projekte individuell konfigurieren . . . . . . . . . . . . . . . . . . . . . . . . 31
2.3.1 Vorhandenen Sourcecode ins Projekt Link to Source . . . . . . . . . 35
2.3.2 Projekteigenschaften definieren . . . . . . . . . . . . . . . . . . . . . . . . . 36
2.4 Sourcecode-Editor-Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
2.4.1 Quick Fix auf Variablen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
2.4.2 Automatische Getter/Setter-Erzeugung . . . . . . . . . . . . . . . . . . . 43
2.4.3 Sourcecode-Vervollständigung (Code completion) . . . . . . . . . . 44
2.4.4 Try-Catch-Block-Generierung . . . . . . . . . . . . . . . . . . . . . . . . . . 46
2.4.5 Sourcecode-Templates verwenden . . . . . . . . . . . . . . . . . . . . . . . 48
2.4.6 Gimmicks des Editors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
2.4.7 Immer ein Undo für die Vergangenheit. . . . . . . . . . . . . . . . . . . . 51
2.5 Refactoring mit Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
2.5.1 Überschreiben/Implementieren von Methoden. . . . . . . . . . . . . . 56
2.5.2 Auslagern von Zeichenketten/Mehrsprachigkeit . . . . . . . . . . . . 57
2.6 Erstellung eines JUnit-Tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59

5
eclipse_v01.book Seite 6 Montag, 30. Januar 2006 12:02 12

Inhaltsverzeichnis

2.7 Debuggen in Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62


2.7.1 Breakpoints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
2.7.2 Aufsetzen einer Launch configuration . . . . . . . . . . . . . . . . . . . . 64
2.7.3 Die Debug-Perspektive im Überblick . . . . . . . . . . . . . . . . . . . . 67
2.7.4 Grundfunktionen des Debuggens . . . . . . . . . . . . . . . . . . . . . . . . 68
2.7.5 Allgemeine Hinweise zum Debuggen in Eclipse. . . . . . . . . . . . 69
2.7.6 Debug View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
2.7.7 Breakpoints, Watchpoints und Exceptioncatching. . . . . . . . . . . 75
2.7.8 Hot Code Replacement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
2.8 Navigation im Sourcecode. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
2.8.1 Javadoc View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
2.8.2 Navigation über die Toolbar. . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
2.8.3 Suchfunktionen in Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
2.8.4 Open-Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
2.9 Weitere Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
2.9.1 Hierarchy View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
2.9.2 Call Hierarchy View. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
2.9.3 Declaration View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
2.9.4 Navigator View. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
2.9.5 Progress View. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
2.10 Java 5 – Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
2.10.1 Java 5 on board. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
2.10.2 Migration Java 1.4 nach 5.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
2.11 Customizing Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
2.11.1 Customizing perspective . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
2.11.2 Eclipse individuell starten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
2.12 Eclipse Preferences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
3 Eclipse als Modellierungswerkzeug. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
3.1 Einleitung ins Thema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
3.2 OMONDO EclipseUML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
3.2.1 Versionen von EclipseUML . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
3.2.2 Download und Installation von Omondo EclipseUML . . . . . . . 111
3.2.3 Klassendiagramme erstellen. . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
3.3 EMF/GEF und UML2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
3.3.1 Eclipse Modeling Framework (EMF) . . . . . . . . . . . . . . . . . . . . 118
3.3.2 Graphical Editing Framework (GEF). . . . . . . . . . . . . . . . . . . . . 119
3.3.3 UML2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
4 Qualitätssicherung in Eclipse. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
4.1 Einführung ins Thema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121

6
eclipse_v01.book Seite 7 Montag, 30. Januar 2006 12:02 12

Inhaltsverzeichnis

4.2 Wie äußert sich ein Mangel an Qualität? . . . . . . . . . . . . . . . . . . . . . . . . . 121


4.2.1 Mangelnde Sourcecodetests . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
4.2.2 Mangelnde Tests der fachlichen Funktionalität . . . . . . . . . . . . . 122
4.2.3 Eingeschränkte Funktionalität. . . . . . . . . . . . . . . . . . . . . . . . . . . 122
4.2.4 Begrenzte Sourcecode-Qualität. . . . . . . . . . . . . . . . . . . . . . . . . . 123
4.2.5 Reviews und Softwareunterstützung. . . . . . . . . . . . . . . . . . . . . . 123
4.3 Eclipse zur Qualitätssicherung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
4.3.1 Testen mit JUnit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
4.3.2 Audits mit Checkstyle. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
4.3.3 Sourcecode-Metrics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
4.3.4 Metrics 1.3.6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
4.3.5 Metrikreports und Graphen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
4.3.6 Ant-Integration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
4.3.7 Bleiben Sie kritisch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
5 Build Management mit Eclipse. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
5.1 Aufgaben des Build Managements. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
5.2 Vom Quellcode zum Programm: Build. . . . . . . . . . . . . . . . . . . . . . . . . . . 138
5.3 Einstellungen des Eclipse-Build . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
5.3.1 Build Path-Einstellungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
5.3.2 Compiler und JavaDoc-Einstellungen. . . . . . . . . . . . . . . . . . . . . 142
5.3.3 Automatische und manuelle Builds . . . . . . . . . . . . . . . . . . . . . . 143
5.3.4 Weitere Konfigurationen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
5.3.5 Erzeugung anderer Ausgabetypen . . . . . . . . . . . . . . . . . . . . . . . 146
5.4 Build Management und Versionierung . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
5.4.1 Exkurs: Versionierung mit CVS in Eclipse. . . . . . . . . . . . . . . . . 148
5.4.2 Versionierung und Build Management . . . . . . . . . . . . . . . . . . . . 158
5.5 Exkurs: Build Management mit Ant. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
5.5.1 Ant Projects, Targets und Tasks . . . . . . . . . . . . . . . . . . . . . . . . . 161
5.5.2 Typische Ziele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
5.5.3 Ein Ant-Beispielskript. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
5.5.4 Ant in Eclipse: externe Builds . . . . . . . . . . . . . . . . . . . . . . . . . . 169
5.6 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
6 GUI-Design mit Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
6.1 Allgemeines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
6.2 Der Visual Editor für Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
6.2.1 Installation und Aufruf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
6.2.2 Hinzufügen von Komponenten . . . . . . . . . . . . . . . . . . . . . . . . . . 178
6.2.3 Entfernen von Komponenten . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
6.2.4 Ändern von Eigenschaften einer Komponente . . . . . . . . . . . . . . 179

7
eclipse_v01.book Seite 8 Montag, 30. Januar 2006 12:02 12

Inhaltsverzeichnis

6.2.5 Layouteinstellungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180


6.2.6 Ereignisverarbeitung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
6.3 Besonderheiten bei der Erstellung eines GUI mit Swing-Komponenten 182
6.3.1 Unterstützte Komponenten. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
6.3.2 Layoutmanager. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
6.3.3 Codegenerierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
6.3.4 Beispiel. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
6.4 Das SWT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
6.4.1 Aufbau einer SWT-Applikation . . . . . . . . . . . . . . . . . . . . . . . . . 206
6.4.2 SWT-Komponenten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
6.4.3 Layoutmanager. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
6.4.4 Ereignisverarbeitung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
6.5 Besonderheiten bei der Erstellung eines GUI mit SWT-Komponenten . 235
6.5.1 Unterstützte Komponenten. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
6.5.2 Layoutmanager. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
6.5.3 Codegenerierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
6.5.4 Beispiel. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
6.6 Der WindowBuilder. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
6.6.1 Installation und Aufruf. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
6.6.2 Hinzufügen von Komponenten . . . . . . . . . . . . . . . . . . . . . . . . . 244
6.6.3 Entfernen von Komponenten . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
6.6.4 Ändern von Eigenschaften einer Komponente. . . . . . . . . . . . . . 245
6.6.5 Layouteinstellungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
6.6.6 Ereignisverarbeitung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
6.6.7 Codegenerierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
6.6.8 Beispiel. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
7 Eclipse und J2EE. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263
7.1 XML. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264
7.1.1 Einführung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264
7.1.2 Bestandteile eines XML-Dokuments . . . . . . . . . . . . . . . . . . . . . 265
7.1.3 Plug-Ins für das Bearbeiten von XML-Dokumenten . . . . . . . . . 270
7.2 Webanwendungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275
7.2.1 Grundlagen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275
7.2.2 Beispiel einer Webanwendung mit Servlets. . . . . . . . . . . . . . . . 276
7.2.3 Beispiel einer Webanwendung mit Java Server Pages . . . . . . . . 281
7.3 Eclipse-Projekt Web Tools Platform (WTP) . . . . . . . . . . . . . . . . . . . . . . 283
7.3.1 Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
7.3.2 Editoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
7.3.3 Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
7.3.4 Einstellungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286

8
eclipse_v01.book Seite 9 Montag, 30. Januar 2006 12:02 12

Inhaltsverzeichnis

7.3.5 Entwicklung einer Webanwendung. . . . . . . . . . . . . . . . . . . . . . . 287


7.3.6 Beispielanwendung Kreditkartenprüfer . . . . . . . . . . . . . . . . . . . 292
7.3.7 Server Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298
7.3.8 Lomboz Plug-In und WTP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299
7.4 J2EE-Anwendungen mit Enterprise Java Beans. . . . . . . . . . . . . . . . . . . . 299
7.4.1 Grundlegende Begriffe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299
7.4.2 Enterprise Java Bean (EJB). . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302
7.4.3 Zugriff eines Clients auf eine EJB . . . . . . . . . . . . . . . . . . . . . . . 304
7.5 Session Beans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306
7.5.1 Bean-Klasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306
7.5.2 Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308
7.5.3 Deployment-Deskriptor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310
7.6 Konfiguration der WTP für einen neuen Server . . . . . . . . . . . . . . . . . . . . 312
7.6.1 Server-Definitionsdatei jboss4.serverdef . . . . . . . . . . . . . . . . . . 313
7.6.2 7.6.2 Konfiguration der plugin.xml . . . . . . . . . . . . . . . . . . . . . . . 321
7.6.3 Konfiguration der plugin.properties . . . . . . . . . . . . . . . . . . . . . . 325
7.7 Erstellung eines EJB-Projekts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
7.7.1 Anlegen des Projekts. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
7.7.2 Erzeugen des EJB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327
7.7.3 Bean-Klasse und XDoclet. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328
7.7.4 Einfügen einer neuen Methode in die Bean-Klasse . . . . . . . . . . 328
7.8 Client für den Remote-Zugriff auf eine EJB . . . . . . . . . . . . . . . . . . . . . . 332
7.8.1 Herstellung einer Verbindung zum JBoss-Namensdienst . . . . . . 332
7.8.2 Beispiel eines Java-Client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334
7.8.3 WTP zur Erstellung eines Java-Clients. . . . . . . . . . . . . . . . . . . . 336
7.9 Webclient für den Zugriff auf eine EJB . . . . . . . . . . . . . . . . . . . . . . . . . . 336
7.9.1 Lokaler Zugriff innerhalb derselben virtuellen Maschine . . . . . 338
7.9.2 Zugriff bei unterschiedlichen virtuellen Maschinen . . . . . . . . . . 344
7.10 Datenbankzugriff aus einer EJB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
7.10.1 Einstellungen für den Zugriff auf die MySQL-Datenbank . . . . . 345
7.10.2 Beispiel Onlineshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347
7.11 Entity Beans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
7.11.1 Grundlagen und Persistenz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
7.11.2 Finder-Methoden. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353
7.11.3 Entity Bean (Container Managed Persistence) . . . . . . . . . . . . . . 354
7.11.4 Entity Bean (BMP) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 370
7.12 Java Messaging Service und Message Driven Beans . . . . . . . . . . . . . . . . 379
7.12.1 Grundlagen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379
7.12.2 Message Driven Beans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 381
7.12.3 Senden einer Nachricht an eine Message Driven Bean . . . . . . . 383

9
eclipse_v01.book Seite 10 Montag, 30. Januar 2006 12:02 12

Inhaltsverzeichnis

8 Web Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389


8.1 Grundlagen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389
8.2 Begriffe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
8.2.1 SOAP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
8.2.2 WSDL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391
8.2.3 JAX. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391
8.3 Entwicklung eines Web Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 392
8.3.1 Aufbau eines WSDL-Dokuments . . . . . . . . . . . . . . . . . . . . . . . 392
8.3.2 Erzeugen eines Web Service aus einem WSDL-Dokument. . . . 397
8.3.3 Entwicklung eines Clients für den Web Service . . . . . . . . . . . . 404
8.3.4 Erzeugen eines WSDL-Dokuments aus einer Java-Klasse . . . . 405
9 Plug-In-Entwicklung für die Eclipse Workbench . . . . . . . . . . . . . . . . . . . . . 411
9.1 Everything is a plug-in. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 411
9.2 In die Tiefen der Oberfläche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 411
9.3 Eine Outline View für den Texteditor . . . . . . . . . . . . . . . . . . . . . . . . . . . 412
9.3.1 Adapter: Dynamische Erweiterung von Klassen . . . . . . . . . . . . 413
9.3.2 Ein eigener Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 415
9.3.3 Alternative 1: Überschreiben der Methode getAdapter . . . . . . . 423
9.3.4 Alternative 2: der Adapter Manager. . . . . . . . . . . . . . . . . . . . . . 425
9.3.5 Ein Extension Point wird geboren . . . . . . . . . . . . . . . . . . . . . . . 441
9.3.6 Schritt 1: Definition des Interface für den Extension Point . . . . 442
9.3.7 Schritt 2: Definition des Extension Point. . . . . . . . . . . . . . . . . . 442
9.3.8 Schritt 3: Implementierung des Extension Point . . . . . . . . . . . . 448
9.3.9 Eine Extension für den neuen Extension Point . . . . . . . . . . . . . 450
9.4 Der eigene Editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 452
9.4.1 Nichts einfacher als ein eigener Texteditor . . . . . . . . . . . . . . . . 452
9.4.2 Wir erweitern den Editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 458
9.5 Und Action ...: Actions in der Eclipse Workbench (Teil 1). . . . . . . . . . . 464
9.6 ... Einschub: Aufbau der Eclipse-Oberfläche . . . . . . . . . . . . . . . . . . . . . 469
9.7 Und Action ...: Actions in der Eclipse Workbench (Teil 2). . . . . . . . . . . 487
9.7.1 Ein Button mit persistentem Status . . . . . . . . . . . . . . . . . . . . . . 488
9.8 Page Book View: ein Buch mit sieben Seiten . . . . . . . . . . . . . . . . . . . . . 495
9.8.1 Action Delegates oder „Wie kommt man von außen
in das Buch?“ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 497
9.9 Markers und Annotations. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 511
9.9.1 Marker für Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 518
9.10 OSGi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 537
9.10.1 Bundles. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 538
9.10.2 Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 545
9.10.3 Die Benutzung eines Service . . . . . . . . . . . . . . . . . . . . . . . . . . . 550
9.11 Fazit und Ausblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 555

10
eclipse_v01.book Seite 11 Montag, 30. Januar 2006 12:02 12

Inhaltsverzeichnis

10 Plug-In-Entwicklung für das JDT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 557


10.1 Soap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 557
10.2 Apache Axis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 559
10.2.1 Installation von Tomcat. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 559
10.2.2 Installation von Axis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 561
10.3 Das Web Service Deployment Plug-In . . . . . . . . . . . . . . . . . . . . . . . . . . . 562
10.4 Den Rahmen erzeugen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 563
10.5 Die Funktionalität implementieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 569
10.6 Eingabe der Konfigurationsparameter . . . . . . . . . . . . . . . . . . . . . . . . . . . 574
10.6.1 Speichern und Laden der Konfigurationsparameter . . . . . . . . . . 583
10.7 Generierung von service.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 586
10.8 Generierung eines Ant-Skripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 589
10.9 Ausführen des Ant-Skripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 592
10.10 Das fertige Plug-In . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 592
10.11 Ein Extension Point für die Generierung von Clients . . . . . . . . . . . . . . . 596
10.12 Einschub: Code-Analyse über AST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 597
10.13 Eine View zur Anzeige des Syntaxbaums . . . . . . . . . . . . . . . . . . . . . . . . 598
10.14 Die Erzeugung von Java-Code über AST. . . . . . . . . . . . . . . . . . . . . . . . . 608
10.15 Ein Extension Point für die Generierung von Clients (Fortsetzung) . . . . 611
10.16 Die Implementierung des Extension Point . . . . . . . . . . . . . . . . . . . . . . . . 613
10.17 Ein Generator für SWT Clients . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 621
10.18 Das SWT-Client-Generator-Plug-In . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 629
10.19 Die Erzeugung von Java Code über das Java Element API . . . . . . . . . . . 629
10.20 So ganz nebenbei zum Schluss . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 633
11 Eclipse als Rich Client Platform . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 635
11.1 Kapitelüberblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 635
11.1.1 Themenüberblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 635
11.1.2 Kapitelstruktur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 635
11.1.3 Ziele des Kapitels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 635
11.2 Anwendungen auf Basis der Plug-In-Technologie . . . . . . . . . . . . . . . . . . 636
11.2.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 636
11.2.2 Sidebar: Bundles versus Plug-Ins . . . . . . . . . . . . . . . . . . . . . . . . 637
11.2.3 Hello World. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 637
11.2.4 Strukturierung von Anwendungen . . . . . . . . . . . . . . . . . . . . . . . 641
11.2.5 Tipps, Tricks und mögliche Fallen . . . . . . . . . . . . . . . . . . . . . . . 642
11.3 Die Eclipse Rich Client Platform . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 645
11.3.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 645
11.3.2 Hello World. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 645
11.3.3 Workbench . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 649
11.3.4 Menüs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 650
11.3.5 Views und Editors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 653

11
eclipse_v01.book Seite 12 Montag, 30. Januar 2006 12:02 12

Inhaltsverzeichnis

11.4 Weitere Themen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 655


11.4.1 Swing und/oder SWT. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 655
11.4.2 Eclipse im Client-Server-Umfeld. . . . . . . . . . . . . . . . . . . . . . . . 656
11.4.3 Eclipse in der Projektpraxis . . . . . . . . . . . . . . . . . . . . . . . . . . . . 657
11.4.4 Ausblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 659
11.5 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 659
12 Refactoring to Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 661
12.1 Kapitelüberblick. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 661
12.1.1 Themenüberblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 661
12.1.2 Kapitelstruktur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 661
12.2 Die Refactorings im Überblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 662
12.3 Refactoring: Konvertiere Standalone-Anwendung in eine
Single-Plug-In-Eclipse-Anwendung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 662
12.3.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 663
12.3.2 Mechanics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 663
12.3.3 Beispiel. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 663
12.4 Refactoring: Extrahiere Bibliotheks-Plug-In . . . . . . . . . . . . . . . . . . . . . . 666
12.4.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 666
12.4.2 Mechanics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 666
12.4.3 Beispiel. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 667
12.5 Refactoring: Extrahiere Erweiterungs-Plug-In . . . . . . . . . . . . . . . . . . . . 667
12.5.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 668
12.5.2 Mechanics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 668
12.5.3 Beispiel. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 668
12.6 Refactoring: Verallgemeinere Extension-Point . . . . . . . . . . . . . . . . . . . . 671
12.6.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 671
12.6.2 Mechanics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 672
12.6.3 Beispiel. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 672
12.7 Refactoring: Konvertiere Anwendung in RCP-Anwendung . . . . . . . . . . 673
12.7.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 673
12.7.2 Mechanics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 673
12.7.3 Beispiel. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 674
12.8 Refactoring: Ersetze Swing-Fenster durch RCP-View . . . . . . . . . . . . . . 675
12.8.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 676
12.8.2 Mechanics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 676
12.8.3 Beispiel. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 676
12.9 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 677
Die Autoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 679
Stichwortverzeichnis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 683

12
eclipse_v01.book Seite 13 Montag, 30. Januar 2006 12:02 12

1 Einleitung

von Lars Wunderlich

1.1 Was ist Eclipse?


Die Antwort auf die Frage, was Eclipse ist, lässt sich je nach Blickwinkel des Benutzers
unterschiedlich beantworten. An dieser Stelle wollen wir die Eclipse-Gründer zitieren:
„Eclipse ist eine Open Source Community, deren Projekte sich auf die Bereitstellung einer
erweiterbaren Entwicklungsplattform und die Erstellung von Applikationsframeworks
zur Softwareentwicklung konzentrieren.“1 Diese Beschreibung zeigt deutlich, dass man
unter dem Begriff „Eclipse“ mehr als eine IDE und Plattform versteht. Mittlerweile haben
sich unter dem Label Eclipse eine ganze Reihe namhafter Softwarefirmen zusammenge-
schlossen, um auf einer gemeinsamen Basis sowohl Entwicklungsumgebungen als auch
Richclient-Anwendungen und Plug-Ins für die unterschiedlichsten Aufgabengebiete zu
entwickeln. Sie werden unterstützt von einer inzwischen riesigen Community, die Eclipse
für eigene Zwecke erweitert, anpasst und in neue Produkte integriert.

1.2 Warum ein Buch über Eclipse?


Eclipse hat in den letzten Jahren das Nischendasein einer einfachen Open Source-Entwick-
lungsumgebung verlassen. Immer mehr Entwickler in Unternehmen oder hobbymäßig zu
Hause entdeckten Eclipse und mit Eclipse teilweise auch erstmalig die Programmierspra-
che Java. Als im Januar 2005 die erste Auflage dieses Buchs mit großem Erfolg eine inte-
ressierte Leserschaft fand, bewog uns das, dieses Buch schnell einem gründlichen Update
zu unterziehen. Eclipse und Java entwickeln sich so schnell weiter, dass nur aktuell bleiben
kann, wer sich mit bewegt. Gleiches gilt natürlich auch für Eclipse selbst, dessen erste 3.2
Milestones Ende 2005 das Licht der Welt erblickten.
Wir möchten Eclipse in diesem Buch sowohl als flexible Oberfläche für die Entwicklung
von Unternehmensanwendungen als auch als Plattform für eigene Erweiterungen vorstel-
len (Rich Client Platform). Erweiterungen sind dabei Plug-Ins oder selbst erstellte IDEs.
Dieses Buch deckt dabei den objektorientierten Softwareentwicklungszyklus vom Design
und der Modellierung in Eclipse, über die Implementierung (Java, EJB, XML, JSP...) bis
hin zum Testen, Debuggen und dem endgültigen Deployment der Anwendung ab. Dabei
werden dediziert Tools und Frameworks vorgestellt, die auch eine Nutzung im professio-
nellen Umfeld erlauben. Eines der herausragendsten Merkmale der Version 3.1 ist zweifels-
ohne die Unterstützung für JDK 5.0, die wir auch in diesem Buch behandeln werden.
Eclipse als Open Source-Produkt unterscheidet sich nicht nur im Preis von einer Reihe
anderer kommerzieller Produkte, sondern in vielen Fällen auch im Leistungsumfang. In
Unternehmen werden oftmals Enterprise-Produkte verwendet, die der Normalnutzer aus

1. Einleitungssatz auf der Eclipse-Homepage http://www.eclipse.org

13
eclipse_v01.book Seite 14 Montag, 30. Januar 2006 12:02 12

An wen richtet sich dieses Buch?

Kostengesichtspunkten nicht nutzen kann. In diesem Buch liegt der Fokus auf Tools und
Plug-Ins, die als Open Source-Produkte sowohl beruflich als auch privat genutzt werden
können. Viele dieser Werkzeuge lassen sich im Bereich der Evaluation, als reine Entwick-
lungsplattform, aber auch im produktiven Umfeld einsetzen. Manchmal werden auch kom-
merzielle Produkte vorgestellt. In diesen Fällen muss entschieden werden, ob der zusätzli-
che Nutzen die entsprechenden Kosten rechtfertigt.

1.3 An wen richtet sich dieses Buch?


Das vorliegende Buch richtet sich an Leserinnen und Leser, die Eclipse 3.1 im Wesentli-
chen in größeren Projekten, d.h. zumeist im Unternehmen, einsetzen oder darüber nachden-
ken, dies zu tun. Wir gehen davon aus, dass bereits eine grobe Vorstellung von Eclipse vor-
handen ist, also z.B. bekannt ist, wie über die Eclipse-Webseite die IDE heruntergeladen
werden kann, wie sie zu installieren ist und wie man einfache Aufgaben (Erstellung von
Java-Projekten, Compile und Debugging) damit durchführen kann.
Implizit bedeutet dies auch, dass entsprechende Java-Kenntnisse (gegebenenfalls auch in
den Java 5-Spracherweiterungen) vorausgesetzt werden, so dass beispielsweise der Unter-
schied zwischen einer abstrakten Klasse, einem Interface und einer Instanzvariablen keine
Schwierigkeiten bereitet.
Gleichzeitig kann dieses Buch aber auch als Entscheidungshilfe bei der Frage verstanden
werden, unter welcher Oberfläche zukünftig implementiert werden soll.
Jenem Entwicklerkreis, dem Eclipse noch nicht bekannt ist, sei ein Blick auf die Homepage
des Eclipse-Projekts1 empfohlen, um diese Umgebung, seine Community und die angebo-
tenen Frameworks kennen zu lernen und sich mit den Basisfeatures vertraut zu machen. Für
jene, die sich leicht in neuen Softwareumgebungen zurechtfinden, aber noch nie Eclipse be-
nutzt und keine Berührungsängste beim Testen und Herumprobieren haben, ist dieses Buch
gleichzeitig eine Art „Jump Start“. Es streift die einfachen Entwicklungsfeatures im ersten
Kapitel, um sich mit Fragen professioneller Softwareentwicklung zu beschäftigen.
Im Gegensatz zu einer technischen Referenz, die den Anspruch erhebt, jede Checkbox in
jedem Dialog detailliert zu dokumentieren, möchten wir die recht gute Online-Hilfe, die bei
Eclipse als Dokumentation mitgeliefert wird, nicht noch einmal „abschreiben“. Vielmehr
wollen wir interessante Punkte vertiefen oder die Möglichkeiten beim Einsatz von Plug-Ins
bei der Entwicklung beschreiben.
In jedem Fall empfiehlt sich ein Blick auf den aktuellen Stand von Eclipse auf der Eclipse-
Webseite.

1.4 Was erwartet Sie in diesem Buch?


Dieses Buch ist in intensiver Zusammenarbeit einer Reihe von Autoren entstanden. Jeder
von ihnen hat sein jeweiliges Know-how eingebracht und dabei Eclipse von unterschiedli-
chen Blickwinkeln aus präsentiert.

1. http://www.eclipse.org

14
eclipse_v01.book Seite 15 Montag, 30. Januar 2006 12:02 12

1 – Einleitung

Teil 1 – führt in die grundsätzliche Verwendung von Eclipse ein. Es wird die IDE vor-
gestellt. Dies ist erforderlich, um sich die Unterstützung von Eclipse für ein bestimmtes
Themengebiet in den nachfolgenden Kapiteln genauer erarbeiten zu können.
Teil 2 – geht speziell auf Aufgabenstellungen in verschiedenen Arbeitsfeldern wie UML-
Modellierung, Model Driven Architecture, Versionsverwaltung, Konfigurations- und
Buildmanagement, GUI-Design oder J2EE-Features ein.
Teil 3 – rundet das Buch ab, indem wir von der Entwicklung mit Eclipse zur Eigenentwick-
lung an Eclipse kommen. Wir untersuchen, wie man Eclipse als „Startframework“ für ei-
gene Clients benutzen kann oder eigene Plug-Ins für Eclipse zur Verfügung stellt.

1.5 Was benötigen Sie?


Zum Zeitpunkt der Erstellung dieses Buchs existiert Eclipse in Version 3.1.1 und in einer
Milestone-Version 3.2. Da sich bei Milestones allerdings noch viel im Verlaufe der Ent-
wicklung bis zum endgültigen Release ändern kann, konzentrieren wir uns in diesem Buch
schwerpunktmäßig auf die 3.1.x-Version.
Für die Durchführung und den Test der Beispiele in diesem Buch ist Version 3.1.1 (bzw. hö-
her – wenn Sie selbst dies wünschen) der Eclipse-Entwicklungsumgebung erforderlich.
Der Einsatz eines J2SE Entwicklungskits (JDK) in Version 1.4.2 oder höher ist sowohl für
die IDE selbst als auch für die meisten Beispiele dabei ebenfalls empfehlenswert, wobei
Eclipse mit der älteren 1.3-Version auch meist den Start verweigert. Wir werden in diesem
Buch allerdings auch immer wieder auf die speziellen Java 5.0-Sprachfeatures eingehen,
weshalb wir für den Einsatz von Eclipse 3.1.1 auch ein entsprechendes JDK empfehlen.
Weitere Tools und Plug-Ins werden in den folgenden Kapiteln verwendet und an der Stelle
jeweils angegeben.
Wir haben die englische Version von Eclipse genutzt, auf der auch die Beispiele und die Do-
kumentation aufbauen. Die Verwendung von Translation Packages zur Arbeit mit Eclipse
z.B. in Deutsch steht dem Leser frei. Allerdings erschweren Oberflächen und Bücher, die
englische Begriffe ins Deutsche übersetzen, oft das Verständnis.

1.6 Anforderungen an eine moderne IDE


Die Frage, welche Anforderungen es bei der Auswahl einer Entwicklungsumgebung gibt,
orientiert sich idealerweise an einer Liste von Features. Abhängig vom Budget wird so je-
doch häufig die Freiheit erkauft, die Zahl der Features derart zu vergrößern, dass eine teure
Entwicklungsumgebung zur Verfügung steht, von der aber nur ein Bruchteil genutzt wird.
Viele Entwickler haben bei der Auswahl einer Entwicklungsumgebung die Abb. des kom-
pletten Entwicklungszyklus vor Augen. Dazu zählen die Analysephase und die Dokumen-
tation (soweit dies das Tool unterstützen kann), das objektorientierte Design bis hin zur
Programmierung sowie die Themen Qualitätssicherung oder Deployment. Am besten posi-
tionieren sich bei dieser Aufgabe zumeist jene Entwicklungsumgebungen, die in einem
konkreten Produkt all diese Anforderungen abdecken oder – um den Wünschen des Nutzers
noch näher zu kommen – sich diesbezüglich beliebig erweitern lassen. Der Markt der Ent-

15
eclipse_v01.book Seite 16 Montag, 30. Januar 2006 12:02 12

Open Source

wicklungsumgebungen ist recht groß, angefangen von kommerziellen Produkten bis hin zu
reinen Open Source-Produkten wie Eclipse.
Den Ansatz der individuellen Anpassbarkeit für die Bedürfnisse (z.B. durch ein Plug-In-
Konzept) hat vermutlich Eclipse bisher als einziges Produkt vervollständigt. Viele Produkte
lassen sich mehr oder minder durch Plug-Ins erweitern. Im Vordergrund stehen aber oft
Produktlinien wie „Personal“-, „Professional“- und „Enterprise“-Versionen. Jemand, der
beispielsweise bei der Entwicklung einer Java EE-Anwendung Unterstützung sucht, soll
mehr bezahlen als derjenige, der ausschließlich Standalone-Anwendungen erstellt. Open
Source-Produkte verwenden diese Form der Marktsegmentierung in der Regel nicht.
Der Ansatz bei der Entwicklung von Eclipse durch OTI/IBM im Gegensatz zu anderen
IDEs war, das Gesamtprodukt als Bausteinsystem zu konzipieren, in das sich alle Funktio-
nen als Komponenten integrieren lassen (siehe Kapitel „Eclipse-Architektur“).
Wer eine Entscheidung über die Auswahl der zukünftigen Entwicklungsumgebung treffen
möchte, sollte stets die Wahl der Architektur und der Werkzeuge, die er dafür benötigt, be-
rücksichtigen. Eclipse wandelte sich mit der Zeit auch immer mehr zur IDE für weitere
Sprachen wie C/C++, COBOL, Fortran, Groovy, Nice, LISP, C#, Jython/Python usw., um
nur einige zu nennen. Die am besten unterstützte Sprache bleibt derzeit aber noch Java.
Wenn es also um Java geht und wenn sich Komponenten und Funktionen der IDE an die je-
weiligen Bedürfnisse anpassen lassen sollen, ist Eclipse sicherlich ein Kandidat der enge-
ren Wahl.
Die Anforderungen an eine moderne IDE hängen primär von den ganz persönlichen Be-
dürfnissen ab und ob ein Produkt alles können soll oder ob Medienbrüche (Wechsel zwi-
schen mehreren Produkten bei der Entwicklung) vom Benutzer als akzeptabel empfunden
werden oder nicht. Am vorteilhaftesten ist es vermutlich, wenn man selbst entscheiden
kann, welche Funktionen über den Basisrhythmus wie Programmierung-Compile-Deploy-
ment hinaus für das eigene Vorgehen noch benötigt werden. Wenn dies mit einer kosten-
losen IDE (wie Eclipse) funktioniert, umso besser.

1.7 Open Source


Entgegen der Meinung vieler Projektverantwortlicher sind nicht unbedingt immer die teu-
ersten Produkte die besten. Besonders die unter der Apache Group laufenden Jakarta-Sub-
projekte1 sprechen eine ganz andere Sprache und haben Produkte wie LOG4J, Struts, Hive-
mind oder Tomcat zur Welt gebracht, die auch in einer Reihe von kommerziellen Produkten
als Subframeworks Einsatz finden. Der große Vorteil für Unternehmen liegt dabei weniger
in der preislichen Attraktivität als mehr im Vorhandensein des Produkt-Sourcecodes und
Quasi-Standards. Besonders bei Problemen, bei denen die Dokumentation nicht mehr wei-
terhilft, kann die Untersuchung des Sourcecodes Support-Anfragen auch zuweilen erset-
zen. Für Unternehmen ist oft der Zeitfaktor im Projektmanagement erheblich drängender
als der Kostenfaktor. Beide lassen sich ganz erheblich reduzieren, wenn eine Open Source-
Lösung gewählt wird.

1. http://jakarta.apache.org

16
eclipse_v01.book Seite 17 Montag, 30. Januar 2006 12:02 12

1 – Einleitung

Probleme mit Open Source-Produkten können dadurch entstehen, dass sie im frühen Sta-
dium ihres Lebenszyklus noch nicht ausgereift sind. Das Risiko besteht dabei, dass die Wei-
terentwicklung nicht oder nur langsam erfolgt. Außerdem kann es vorkommen, dass ein
Open Source-Produkt nach dem Erreichen eines Stadiums, in dem es schließlich effektiv
eingesetzt werden kann, nur noch kommerziell vertrieben wird.
Letztendlich muss jeder selbst entscheiden, welches Framework oder Produkt für ihn am
sinnvollsten erscheint. Die Mächtigkeit von Open Source-Produkten (auch professionellen)
im Java-Sektor ist allerdings schon recht beeindruckend.
Lassen Sie sich nun mitnehmen auf die Reise durch eine weltweit prämierte Open Source-
Entwicklungsumgebung, durch die Tools und Utilities, die dafür geschrieben wurden,
durch die Welt von objektorientierter Softwareentwicklung und Enterprise-Technologien.

Willkommen in der Welt von Eclipse!

Viel Spaß beim Kennenlernen von Eclipse und beim Erobern neuer Horizonte!
Lars Wunderlich

17
eclipse_v01.book Seite 18 Montag, 30. Januar 2006 12:02 12
eclipse_v01.book Seite 19 Montag, 30. Januar 2006 12:02 12

2 Wegweiser durch Eclipse

von Lars Wunderlich

Zur besseren Orientierung auf der Oberfläche von Eclipse wollen wir in diesem Kapitel mit
einem Quickstart beginnen. Dieser Überblick über Eclipse beinhaltet im Gegensatz zu einer
Referenz nicht jede Funktion, sondern nur jene, die bei der Arbeit vorrangig wichtig er-
scheinen.
Nach dem Start von Eclipse fragt dieses nach einem Workspace-Verzeichnis für seine eige-
nen Dateien. Wir klicken die Checkbox an, um das vorgeschlagene Verzeichnis zum De-
faultdirectory zu erklären, und wählen danach OK. Sollten Sie eine alte Eclipse-Version in-
stalliert haben oder die Daten mit einer anderen Eclipse-Version teilen wollen (z.B. wenn
Eclipse-Metadaten in einem Source-Control-System liegen), empfiehlt sich die Wahl eines
entsprechenden, anderen Verzeichnisses.
Eclipse begrüßt uns daraufhin mit dem Welcome-Bildschirm. Für Neueinsteiger empfiehlt
sich hier grundsätzlich die Führung durch die einzelnen Tutorials, um die Oberfläche ken-
nen zu lernen. Wir möchten allerdings gleich in medias res gehen und klicken daher im
Menü WINDOW den Menüpunkt OPEN PERSPECTIVE | JAVA an, um in die Java-Perspektive zu
wechseln. Gegebenenfalls müssen Sie das Welcome-Fenster auch schließen.
Aus jeder beliebigen Ansicht lässt sich über diese Menüpunkte zurück in die Java-Perspek-
tive wechseln.

2.1 Perspektiven und Views von Eclipse

2.1.1 Perspektiven
Das Aussehen von Eclipse bzw. dessen Erscheinungsbild bei der Arbeit hängt im Wesent-
lichen von der Perspektive ab, die jeweils aktiviert ist. Eine Perspektive ist eine Sammlung
von Fenstern (im Eclipse-Kontext als Views bezeichnet) und Menüpunkten, die gemeinsam
ein Themengebiet behandeln. Solche Themengebiete könnten das Editieren des Source-
codes, das Debuggen, aber auch das Installieren von weiteren Plug-Ins sein.
Grundsätzlich können eigene Perspektiven wie in einer Art Baukasten zusammengestellt, an-
geordnet und für weitere Verwendungszwecke gespeichert werden (siehe Abschnitt 2.11.1).

2.1.2 Perspektiven auswählen


Zur Auswahl einer persönlichen Perspektive muss das Menü WINDOW | OPEN PERSPECTIVE
geöffnet und dann die gewünschte Ansicht angeklickt werden. Für das im Folgenden be-
trachtete Beispiel wählten wir die Java-Perspektive. Die jeweils aktuelle Perspektive ist da-
bei mit einem schwarzen Kreis markiert. Weitere Perspektiven (auch jene, die gegebenen-
falls von Plug-Ins hinzugefügt werden) finden sich unter dem Menüpunkt WINDOW | OPEN
PERSPECTIVE | OTHER.

19
eclipse_v01.book Seite 20 Montag, 30. Januar 2006 12:02 12

Perspektiven und Views von Eclipse

Die so genannte Shortcut-Bar, die Leiste der Schnellzugriffe, ist am rechten oberen Rand
des Eclipse-Fensters angeordnet. In ihr befinden sich alle auswählbaren und bereits einmal
aktivierten Perspektiven. In der linken unteren Ecke sehen Sie Icons für alle Views, die vom
Benutzer in die Leiste gelegt wurden, um schnell zugreifbar zu sein.

2.1.3 Die Java-Perspektive


Die Standard-Java-Perspektive ist in Abb. 2.1 dargestellt. Die dort erscheinenden einzelnen
Fenster bezeichnen wir als Views. Sie tragen die Beschriftungen Package Explorer, Outline
oder Tasks und beschreiben einen konkreten Sachverhalt oder ein Problem der aktuellen
Arbeit.

Abb. 2.1: Java-Perspektive

In Abb. 2.1 sehen wir oben die aus vielen Programmen bekannte Menüzeile. Auf die ein-
zelnen Funktionen wollen wir hier noch nicht eingehen. Wenn bereits Erfahrungen mit
Eclipse vorliegen, sollte Ihnen bekannt sein, wie man die Such- und Startfunktionen für
Java-Programme aufruft und neue Klassen anlegt. Daneben finden sich hier alle wesentli-
chen Funktionen – die Arbeit mit Projekten, Sourcecode-Modifikationen, Suchalgorith-
men, die Konfiguration der kompletten Oberfläche, das Hilfesystem und der Welcome-
Bildschirm, den wir bereits sahen.

20
eclipse_v01.book Seite 21 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

Viele Funktionen aus den Menüs kommen im Beispielprojekt zum Einsatz. Eine ganze Rei-
he von ihnen sind auch über entsprechende Kontextmenüs oder eine spezielle Tastenkom-
bination, die neben dem Namen des Menüpunkts steht, verfügbar.

2.1.4 Neues Java-Projekt erzeugen


Zur Erstellung eines Java-Projekts wählen wir zunächst das Menü FILE | NEW | PROJECT und
klicken im nachfolgenden Dialog JAVA PROJECT und den Button NEXT an. Anschließend ge-
ben wir einen Projektnamen ein (z.B. „Testprojekt“) und klicken den FINISH-Button an, um
ein Java-Projekt mit Standardeinstellungen zu erzeugen.
Auf der linken Seite der Java-Perspektive erscheint im PACKAGE EXPLORER das Projekt. Über
das Kontextmenü des Projekts und den Befehl FILE | NEW können Packages und Klassen zu
dem Projekt hinzugefügt werden.

2.1.5 Package Explorer und View-Funktionen


Wie in Abb. 2.2 zu sehen, zeigt der Package Explorer die Projekte mit deren Packages
sowie deren Elementen (Klassen, Interfaces, Methoden, Variablen etc. – hier am Beispiel
eines Ausschnitts der String-Klasse). Die Icons, die zur visuellen Unterstützung der Ele-
mente angezeigt werden, sind abhängig von der View bzw. davon, welche Plug-Ins aktiviert
und welche Label Decorators (siehe Abschnitt 2.12) zugelassen sind.

Abb. 2.2: Package Explorer

Viele Views verfügen über eine eigene Toolbar (wie beispielsweise Package Explorer und
Outline View), die sich direkt unter der Fenster-Namenszeile befindet und zumeist das Aus-

21
eclipse_v01.book Seite 22 Montag, 30. Januar 2006 12:02 12

Perspektiven und Views von Eclipse

sehen und den Inhalt der jeweiligen View beeinflusst. Die Package Explorer View erlaubt
dabei beispielsweise – wie dargestellt – die nachfolgenden Aktionen:
BACK – zeigt die Hierarchie, so wie sie direkt vor der aktuellen Anzeige ausgesehen hat.
FORWARD – zeigt die Hierarchie, wie sie direkt nach der aktuellen Anzeige ausgesehen hat.
UP – zeigt die Hierarchie des Elternobjekts der aktuell höchsten Resourcestufe.
COLLAPSE ALL – schließt die aktuelle Baumansicht, indem der Baum in sich „zusammen-
fällt“.
LINK WITH EDITOR – aktiviert die Synchronisation zwischen dieser View und dem aktiven
Editorfenster.
Einzelne ausgesuchte Views besitzen zusätzlich ein eigenes Funktionsmenü, das über den
kleinen nach unten weisenden schwarzen Pfeil aktiviert wird. In diesen Menüs befinden
sich zumeist jene Aktionen, die es erlauben, die Anzeige dieser View weiter an die persön-
lichen Bedürfnisse anzupassen (Ein/Ausblenden bestimmter Inhalte, Wechsel der Struktur-
darstellungen, ...).

Abb. 2.3: Fensterfunktionen

Mit einem Rechtsklick auf das Fenster-Icon können die Fensterfunktionen angezeigt wer-
den (Abb. 2.3), wobei die Features (egal ob View oder nicht) stets gleichbleibend sind. Ne-
ben den klassischen Funktionen wie Minimierung (MINIMIZE), Maximierung (MAXIMIZE),
Größenänderung (SIZE) oder Schließen (CLOSE) verfügen die Fenster zusätzlich über die Fä-
higkeit, sich mit anderen in einem so genannten Stack zusammenfassen zu lassen. Ein
Stack ist dabei eine Art Blattsammlung mit Registern (siehe Abb. 2.4).

Abb. 2.4: View Stack (Package Explorer ausgewählt)

Per Drag&Drop kann entschieden werden, auf welchem Stack eine View abgelegt werden
soll (docking). Neu geöffnete Views werden automatisch einem Stack zugeordnet. Die Re-
gister der Views im Stack erscheinen am oberen Rand der jeweiligen View. Durch Klicken
auf den Namen des entsprechenden Registers öffnet sich die zugehörige View. Views, die

22
eclipse_v01.book Seite 23 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

nicht mehr in den Stack passen, können über die Doppelpfeile am rechten Rand ausgewählt
und über das Eingabefeld am oberen Rand sogar namentlich gesucht werden.
Als so genannte Fast Views werden View-Fenster bezeichnet, die über den Menüpunkt FAST
VIEW in die Shortcut Bar auf der unteren linken Seite des Eclipse Fensters verbannt wurden.
Beim Klicken auf deren Icon wird vorübergehend die entsprechende View eingeblendet.
Durch Klicken in einen anderen Fensterbereich verschwindet sie wieder. Über die rechte
Maustaste auf dem View-Icon kann die Fast-View-Anzeige wieder deaktiviert werden. Für
die Navigation zwischen Views und ihren Funktionen finden sich entsprechende Menü-
punkte und Tastatur-Shortcuts im Menü WINDOW | NAVIGATION.

2.1.6 Das Eclipse-Editorfenster


Im Zentrum von Eclipse befindet sich der Editor bzw. das Editorfenster. Pro geöffneter Da-
tei existiert ein Editierfenster (siehe Abb. 2.5), wobei jeweils eines aktiv ist. In allen nach-
folgenden Betrachtungen der Fähigkeiten des Eclipse Editors werden wir uns die Aktionen
innerhalb dieses Fensters ansehen.

Abb. 2.5: Eclipse-Editorfenster und Outline View

2.1.7 Outline View


Auf der rechten Seite der Java-Perspektive erscheint die Outline View (Abb. 2.5). Sie vi-
sualisiert abhängig von der jeweils geöffneten Datei deren Inhalte strukturell. Bei Anzeige
einer Java-Datei werden darin die Hauptelemente (Variablen, Methoden, Konstruktoren),
deren Attribute und Abhängigkeiten zu Superklassen angezeigt.
Im Gegensatz zur Package Explorer-Ansicht kann hier die Klassenstruktur ganz individuell
angezeigt werden. Dazu stehen folgende Funktionalitäten in der Outline-View als Shortcuts
zur Verfügung:

23
eclipse_v01.book Seite 24 Montag, 30. Januar 2006 12:02 12

Perspektiven und Views von Eclipse

SORT – Aktivierung/Deaktivierung der Sortierung innerhalb der Java-Elemente der Klasse


HIDE FIELDS – Ein-/Ausblenden von Instanz- und Klassenvariablen
HIDE STATIC FIELDS AND METHODS – Ein-/Ausblenden von Klassenelementen
HIDE NON-PUBLIC MEMBERS – Ein-/Ausblenden aller nicht öffentlichen Variablen/Methoden
HIDE LOCAL TYPES – Ein-/Ausblenden aller lokalen und anonymen Typen der Klasse
LINK TO EDITOR (IM MENÜ) – Synchronisation zwischen Editorfenster und dieser View
GO INTO TOPLEVEL TYPE (IM MENÜ) – öffnet die Ansicht des Baums auf Ebene der angezeig-
ten Klasse

Hilfreich ist es, wenn alle Views einen in sich geschlossenen, konsistenten
Zustand zeigen. Damit Outline View, Editor und Package Explorer dabei
Bezug nehmen auf die gleichen Methoden und Klassen, aktiviert man in
den beiden Views das doppelte File-Icon (link to editor). Danach führen
——

Klicks in einem der Fenster zur Synchronisation der Ansicht mit den ver-
bleibenden.

2.1.8 Tasks/Problems View


Rechts unten finden sich in Eclipse standardmäßig die Tasks, Warnings und Errors. Dabei
handelt es sich stets um zu erledigende Aufgaben, allerdings mit unterschiedlicher Priorität.
Im schlimmsten Fall zeigt die Problems View (siehe Abb. 2.6), dass ein komplettes Projekt
nicht kompiliert werden konnte, weil einzelne Bibliotheken oder sogar das komplette Java
Runtime Environment fehlt oder nicht an der gesuchten Stelle gefunden wurde. In einem
anderen Fall werden Methoden, die als deprecated gekennzeichnet sind, aufgerufen, so dass
bestimmte Klassen nicht kompiliert werden können. In der separaten Taskview (aktivierbar
über das Menü WINDOW | SHOW VIEW | OTHER | BASIC | TASKS) können Hinweise angezeigt
werden, was z.B. noch überarbeitet, getestet oder dokumentiert werden soll.

Abb. 2.6: Problems View zur Anzeige von Build-Problemen

Wie in jeder View kann auch in diesen beiden die Darstellung gefiltert werden. Durch Klick
auf das Filternsymbol (die drei nach rechts weisenden Pfeile auf der Tasks/Problems View)
erscheint ein Dialog, in dem individuell bestimmt werden kann, welche Informationsele-
mente wichtig sind.

24
eclipse_v01.book Seite 25 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

Die Anzeige sollte sinnvollerweise eingeschränkt werden. Hierzu sollte die


Menge der gleichzeitig sichtbaren Fehler (LIMIT VISIBLE ITEMS TO) auf 500
reduziert werden und der Radiobutton ON ANY RESOURCE IN SAME PROJECT
gewählt werden. In diesem Fall werden pro angelegtem Eclipse-Projekt
——

die Fehler angezeigt. Wenn externe Klassen, auf deren Sourcecode-


Debugging nicht verzichtet werden soll oder kann, in eigene Eclipse-Pro-
jekte ausgelagert werden, wird das Durcheinander in der Problem- und
Task-Anzeige deutlich reduziert, allerdings belastet häufiges Wechseln
zwischen den Projekten dann auch die Gesamtperformance der Anwen-
dung. Darüber hinaus besteht die Möglichkeit, durch die Definition von so
genannten Working-Sets (Arbeitsmengen) die Menge der Klassen noch-
mals wesentlich einzuschränken. Nur kann dies auch schnell zu Verwir-
rungen führen.

2.1.9 To be done – Arbeiten mit Tasks


Wird in einem Projekt konsequent modelliert und der Sourcecode von einem CASE-Tool
generiert (siehe auch Kapitel 3 über Modellierung), werden eine Fülle von Klassen und
Methoden erzeugt, deren Ausgestaltung (Implementierung und Erstellung entsprechender
Dokumentation) vom Wohlwollen der Programmierer abhängt.

Abb. 2.7: Tasks View und TODO-Definitionen im Editorfenster

Eclipse erlaubt die Erfassung von so genannten Tasks. Hierzu wird im linken schraffierten
Bereich des Editors die rechte Maustaste gedrückt, ADD TASK gewählt und dann die noch
auszuführende Aufgabe beschrieben. Die neue Task befindet sich daraufhin in der Tasks
View (siehe Abb. 2.7). Abgeschlossene Arbeiten können später gelöscht werden.
Sollte die Taskview noch nicht angezeigt werden, lässt sie sich über WINDOW | SHOW VIEW |
OTHER... | BASIC | TASKS schnell herbeizaubern.
Alternativ zu Standardkommentaren bietet Eclipse die Möglichkeit, durch Inline-Kommen-
tare im Sourcecode solche Tasks selbst zu definieren und zu erzeugen. Diese Variante hat
den Vorteil, dass die komplette Information im Sourcecode bereits enthalten ist und nicht
verloren geht, wenn die Eclipse-Informationen nicht mit weitergegeben werden (z.B. zwi-
schen Entwicklern). Dafür sind diese ToDo’s auch gleich projektweit für alle sichtbar.

25
eclipse_v01.book Seite 26 Montag, 30. Januar 2006 12:02 12

Perspektiven und Views von Eclipse

Hierzu wird einfach der Begriff TODO und dahinter die Beschreibung, was zu tun ist, in
den Sourcecode-Kommentar geschrieben. Beim nächsten Speichern der Datei (Ÿ+S)
wird der Kommentar automatisch zu einer Task in der Taskliste. Dort kann er gegebenen-
falls auch modifiziert werden, was sich gleich wieder auf den Sourcecode auswirkt.
Eclipse selbst bedient sich dieser Funktionalität auch gerne und erzeugt künstlich Kom-
mentare mit dem Hinweis „To do“, wenn es beispielsweise Methoden automatisch gene-
riert und daher Javadoc und Implementierung nicht vollständig sein können.

ToDo’s in den Sourcecode zu schreiben ist sehr angenehm und einfach


und überlebt z.B. auch einen Eclipse-Absturz, bei dem hin und wieder lei-
der auch manuell erfasste Tasks verloren gehen können. Wenn die Zahl
der ToDo's (eigene und die von Kollegen) zu unübersichtlich wird, lassen
——

sich auch eigene persönliche definieren. Vordefiniert sind „TODO“,


„FIXME“ und „XXX“.

2.1.10 Eigene Tasks definieren


Die Tasks bzw. die begrifflichen Definitionen, die zu einer Task führen, können in einem
Projekt vollkommen frei vergeben werden. Entweder wird dies in den Preferences einge-
stellt (siehe Abschnitt 2.12) oder man entscheidet sich, dass nur das einzelne Projekt auf
diese Tasks reagieren soll (Kontextmenü des Eclipse-Projekts im Package Explorer PROPER-
TIES | JAVA COMPILER | JAVA TASK TAGS – siehe Abb. 2.8).

Abb. 2.8: Erstellen eigener Tasks über Project-Properties

Neben dem TODO-Tag sind andere Tags wie „to be reviewed“ oder „to be
optimized“ in ihrem jeweiligen Kontext sinnvoll. Sehr interessant ist auch
immer mitanzusehen, wenn ein Projekt beispielsweise aus dem Jakarta-
Umfeld importiert wird, an welchen Hotspots die dortigen Autoren noch
——

nicht gedreht haben.

26
eclipse_v01.book Seite 27 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

2.2 Hilfe in Eclipse


Die Hilfe ist vor allem für diejenigen interessant, die noch nie mit Eclipse gearbeitet haben.
Wir haben sie bereits kurz nach der Installation von Eclipse in Form einer Reihe von Tuto-
rials in der Welcome-Perspektive gesehen. Diese lässt sich bei Bedarf über den Menüpunkt
HELP | WELCOME erneut anzeigen.
Die Standard-Eclipse-Hilfe wird über den Befehl HELP | HELP CONTENTS aktiviert. In zahl-
reichen Dialogen kann aber auch durch Drücken der É-Taste eine kontextsensitive Hilfe
aufgerufen werden, die einzelne Funktionen erläutert.
Eclipse unterstützt zusätzlich einen Mechanismus der sich Cheat Sheets nennt und den Be-
nutzer durch eine Sequenz von definierten Schritten führt. Nach dem Abschluss jedes ein-
zelnen Schritts schreitet das Cheat Sheet voran, wobei jeder Step automatisch ausgeführt
oder manuell durch den Benutzer durchzuführen ist. Die Tutorials in Eclipse unterstützen
diese Cheat-Sheet-Technik, um beispielsweise die Entwicklung eines Hello-World-Bei-
spiels in Eclipse zu präsentieren (HELP | CHEAT SHEETS...). Zu diesem Mechanismus gehört
auch eine Cheat Sheets View, die sich über WINDOW | SHOW VIEW | OTHER | CHEAT SHEETS |
CHEAT SHEETS aktivieren lässt.

Beispielprojekt – Arbeiten mit Regular Expressions


Jedes klassische Buch über Programmierung oder IDEs fängt mit dem berühmten Hello-
World-Beispiel an, das einen zwischen ein paar Minuten Aufwand und mehreren Anrufen
beim entsprechenden Support Zeit kosten kann (eben auch je nach Sprache und Entwick-
lungsumgebung).
Da es allerdings wenige Unternehmen gibt, die Hello-World-Programme auch kommerziell
vertreiben können (außer vielleicht Buchverlage und deren Autoren ;-), werden wir im Fol-
genden ein etwas komplizierteres Beispiel betrachten.
Ziel ist das Entwickeln einer Klasse, die einen StringBuilder (z.B. gelesen aus einer Datei
oder von einer Webseite) nach bestimmten Zeichenkombinationen durchsucht. Gemeinhin
wird so etwas oft mit Regular Expressions realisiert. Da man im Projekt oft schon fertige
Frameworks einsetzt und mit größeren Klassenmengen umgehen muss, werden wir mittels
Jakarta ORO an einer solchen Kleinstanwendung die Funktionen von Eclipse betrachten.
Ziel ist es nicht, ein komplexes Design abzubilden, sondern zunächst einmal Eclipse-
Grundkenntnisse zu erhalten.
Hierzu wird Apache Jakarta ORO (hier in Version 2.0.8) als Sourcecode1 benötigt.
Bevor wir uns dem Beispiel widmen, erfahren Sie, was unter einer Regular Expression zu
verstehen ist. Eine Regular Expression beschreibt, wie ein Computerprogramm ein Text-
muster verarbeiten soll und was zu tun ist, wenn dieses Muster gefunden wird. Das ORO-
Framework prüft unter Verwendung unterschiedlicher Methoden, inwieweit andere Zei-
chenketten eine beschriebene Bedingung erfüllen.
Eine solche Regular Expression könnte lauten: „[0-9]“. Alle Zeichenketten, die genau aus
einer Zahl bestehen, würden in diesem Fall der Regular Expression gehorchen.

1. http://jakarta.apache.org/oro

27
eclipse_v01.book Seite 28 Montag, 30. Januar 2006 12:02 12

Hilfe in Eclipse

Die Beschreibung einer Regular Expression setzt sich zumeist aus der Kombination eines
Wertebereichs (z.B. [0-9]) und einer Mengenangabe ({5}) zusammen. Tabelle 2.1 zeigt ei-
nige Beispiele.

Tabelle 2.1: Einfache Regular Expressions

Regular expression Erläuterung

[0-9] eine Ziffer – Beispiel: 0, 1, 2, 3, 4, 5, ...

[0-9]{2} zwei Ziffern – Beispiel: 05, 76, 65, 54, 29, 99, ...

[A-Za-z] irgendein Buchstabe – Beispiel: A, z, Z, j , I, x, C

Für unser Beispiel legen wir jetzt als Erstes über das Menü FILE | NEW | PROJECT | JAVA | JAVA
PROJECT | NEXT ein neues Java-Projekt mit dem Namen RegExTest an. Nach der Eingabe
des Namens im Feld PROJECT NAME drücken wir den Button FINISH, um das Projekt zu er-
zeugen. Dann drücken wir auf dem Projektnamen die rechte Maustaste, wählen aus dem
Kontextmenü den Menüpunkt NEW | PACKAGE, tippen als Package-Namen com.entwick-
ler.eclipsebuch.kapitel2.analyzer ein und klicken auf FINISH. Auf dem erzeug-
ten Package wählen wir wiederum aus dem Kontextmenü den Menüpunkt NEW | CLASS, als
Klassennamen geben wir ExpressionAnalyzer im Wizard ein und drücken auf FINISH.
Sollte automatisch eine Main-Methode erzeugt worden sein, können Sie diese getrost erst
einmal löschen.
Damit ist die erste Beispielklasse com.entwickler.eclipsebuch.kapitel2.analy-
zer.ExpressionAnalyzer erzeugt, wenn auch bisher mit wenig Sourcecode. Diesen
kann man sich in einem Editor ansehen. Ein Doppelklick auf den Dateinamen öffnet stets
ein entsprechendes Editorfenster. Eine gegebenenfalls generierte Main-Methode können
wir vorerst löschen.
Wir schreiben jetzt eine Methode, die alle Vorkommnisse eines Pattern innerhalb eines
Strings heraussucht und die Menge dieser gefundenen Ergebnisse in einem Vektor zurück-
liefert. Zunächst erzeugen wir die Methodensignatur. Hierzu tippen wir den folgenden
Sourcecode in die ExpressionAnalyzer-Klasse ein:

public Vector <String> getAllOccurrencesOfPattern(


final String originalContent, final String patternString) { }

Bereits jetzt meldet Eclipse, dass es die Begriffe Vector und den Listentyp String nicht
kennt, da wir in den Importstatements die Klasse Vector auch noch nicht bekannt gemacht
haben. Lassen wir die Maus über den Begriff Vector fahren, zeigt uns Eclipse die Begrün-
dung in einem so genannten Hover an. Eclipse benutzt Hovers an vielen Stellen zur Anzei-
ge von Problemen, für Vorschläge, wie man diese lösen kann, oder zur Kenntlichmachung
von Dokumentationen.

28
eclipse_v01.book Seite 29 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

Abb. 2.9: Hinweise auf Build-Fehler in Eclipse

Haben wir bereits gespeichert (Tastenkombination: Ÿ+S) und dadurch implizit den
Compile-Lauf ausgelöst, finden wir eine ganze Reihe von Hinweisen auf einen Fehler. Zu-
nächst einmal ist der Begriff Vector unterstrichen. Das kleine Lämpchen mit dem X links
vom Sourcecode zeigt den Fehler an, auf der rechten Seite vom Sourcecode-Fenster findet
sich ein kleiner Balken, der anzeigt, dass sich an dieser Stelle vom Gesamtdokument ein
Fehler befindet. Ein kleiner roter Kasten in der rechten oberen Ecke des Editorfensters
weist auf einen Fehler hin und der Editorreiter mit dem Namen ExpressionAnalyzer.java
besitzt ebenfalls ein rotes X (siehe Abb. 2.9).
Dies setzt sich in der Package Explorer View auf der linken Seite fort. Die Klasse, das
Package und auch das komplette Projekt bekommen ein X als Kennzeichnung eines Com-
pile-Fehlers und schließlich finden sich in der Problems View darunter noch einmal die
Fehler mit Beschreibung, Resource, Ordner und Angabe der Zeilennummer. Ein Dop-
pelklick auf den Fehler in der Problems View führt ebenfalls zum Compile-Fehler. Dass der
Fehler auch in der Outline View rechts angezeigt wird, dürfte schon fast selbstverständlich
erscheinen.
Eclipse bietet zur Problemlösung mehrere Möglichkeiten an. Bei keiner davon muss das
Importstatement allerdings per Hand ergänzt werden.

Eclipse kompiliert im Gegensatz zu vielen anderen Umgebungen inkre-


mentell, das heißt, nur jene Sourcecodeteile, auf die Änderungen Auswir-
kungen haben, werden kompiliert. Eclipse kompiliert die Teile, die
kompilierbar sind, und lässt die anderen beim Erzeugen des Classfiles
——

unberührt. Voraussetzung für den inkrementellen Compile ist, dass der


automatische Compile des Projekts nicht deaktiviert ist (Menü PROJECT |
BUILD AUTOMATICALLY) und durch die Tastenkombination Ÿ+S kompiliert
wird. Über den Menüpunkt PROJECT kann man nun das gesamte Projekt,
einen Source-Teil (Workingset) oder alle Projekte neu kompilieren und den
Neucompile mittels des Menüpunkts CLEAN forcieren.

29
eclipse_v01.book Seite 30 Montag, 30. Januar 2006 12:02 12

Hilfe in Eclipse

Abb. 2.10: Quick Fix und Lösungsmöglichkeiten im Einsatz

Das Klicken auf das kleine Lämpchen links vom Sourcecode (oder Ÿ-1 auf dem se-
lektierten Vector-Begriff) ermöglicht es, sich die Lösungsmöglichkeiten (Quick Fix) für
das Problem anzeigen zu lassen. Eclipse bietet u.a. an, die Klasse Vector aus java.util
zu importieren, eine Vector-Klasse zu erzeugen oder ein entsprechendes Interface zu ge-
nerieren, und zwar jeweils im Hover daneben unter Erläuterung der Aktivitäten, die es
durchführen würde. Wir entscheiden uns für den Import der Klasse Vector. Nach einem
weiteren Compile mit Ÿ+S bleiben Vector und String aber markiert. Grund ist dafür
der Compliance Level. In Java 1.4 waren getypte Listen noch nicht erlaubt. Wir klicken nun
mit dem Cursor auf den Begriff „String“ und drücken erneut Ÿ-1. Aus der Auswahl
wählen wir CHANGE WORKSPACE COMPLIANCE AND JRE TO 5.0. Hiermit wird der gesamte
Workspace Java-5.0-fähig gemacht. Wir speichern erneut mit Ÿ-S. Vector und
String sind jetzt nicht mehr unterstrichen. Dieser Fehler scheint behoben.

Eclipse 3.1 ist grundsätzlich in der Lage, neben 5.0 auch mit JRE 1.3 und
1.4 umzugehen. Die Eclipse-Version 3.2 bringt zusätzlich Unterstützung
für Java 6 mit. Da sich die Java-5-Sprachfeatures deutlich von denen von
Java 1.4 unterscheiden, müssen sie entweder pro Projekt oder für das
——

ganze Eclipse aktiviert werden. Die Installation eines Java-5-JDKs ist hier-
für Voraussetzung. Haben Sie dies noch nicht getan, können Sie weitere
JREs über das Menü WINDOW | PREFERENCES | JAVA | INSTALLED JREs nach-
holen. In den Beispielen kam ein Sun JDK 5.0_05 zum Einsatz. Wählen
Sie zum Ändern der Compile-Einstellungen entweder im Menü WINDOW |
PREFERENCES | JAVA | COMPILER | JAVA COMPLIANCE LEVEL aus oder wählen Sie
über das Kontextmenü auf einem Java-Projekt im Menüpunkt PROPERTIES
den Unterpunkt JAVA COMPILER | ENABLE PROJECT SPECIFIC SETTINGS und stel-
len Sie den JAVA COMPLIANCE LEVEL entsprechend ein.

Dafür weist Eclipse nun darauf hin, dass wir zwar ein getyptes Vector-Objekt zurückge-
ben wollten, aber kein return-Statement haben. Auch hier können wir den Fehler auf die
gleiche Art und Weise beheben, indem wir uns die Lösungsvarianten durch Klick auf das
Lämpchen anzeigen lassen (siehe Abb. 2.11).

30
eclipse_v01.book Seite 31 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

Die Entscheidung fällt erneut auf die erste Lösungsalternative: das Erzeugen des return-
Statement.

Abb. 2.11: Quick Fix mit Lösungsmöglichkeiten (2)

2.3 Eclipse-Projekte individuell konfigurieren


Vor dem Einsatz des ORO-Frameworks importieren wir es. Hierzu müssen zunächst die zu
Jakarta ORO gehörigen Dateien gespeichert werden.

Abb. 2.12: ORO-Verzeichnisstruktur

Daraufhin sollte am Speicherort die in Abb. 2.12 sichtbare Verzeichnisstruktur zu finden


sein. Neben einem „Document-Verzeichnis“ ist noch das Source-Verzeichnis, in dem die
Java-Klassen liegen, interessant.
Wir wechseln zurück zu Eclipse und erzeugen ein neues Projekt mit dem Namen ORO, in-
dem wir im Menü FILE | NEW | PROJECT | JAVA PROJECT | NEXT wählen und im daraufhin er-
scheinenden Fenster als Projektnamen ORO eingeben. Wir aktivieren den Radiobutton
CREATE PROJECT FROM EXISTING SOURCE und wählen das Verzeichnis, in dem wir ORO ent-
packt haben (z.B. D:\jakarta-oro-2.0.8). Bereits hier können Sie wählen, ob Eclipse später
Sourcecode und generierte Class-Files in unterschiedlichen Verzeichnissen ablegen soll.
Für den Moment lassen wir diese Funktion aber unangetastet.
Eclipse deaktiviert die Definition eines Projektlayouts und versucht nun, das Verzeichnis
nach bereits existierenden Class-Files und Libraries zu durchsuchen. Mit NEXT gelangen
wir auf die Project-Property-Seite.

31
eclipse_v01.book Seite 32 Montag, 30. Januar 2006 12:02 12

Eclipse-Projekte individuell konfigurieren

Abb. 2.13: ORO – New project – Java settings

Eclipse erkennt anhand seines Scans über das Verzeichnis, in welchen Unterverzeichnissen
sich Java-Klassen befinden (kann versionsbedingt leicht abweichen) und dass zum Beispiel
ein Compile des /doc-Verzeichnisses keinen Sinn macht (siehe Abb. 2.13). Eclipse erkennt
dabei auch die unterschiedlichen Packagestatements und dass bei den Hauptklassen die
Package-Struktur bereits bei /src/java beginnt. Es bietet ein entsprechendes Projektlayout
zur Erstellung in Form einer Baumdarstellung an.
Da wir die Tools und Examples nicht mitkompilieren möchten, löschen wir deren Einträge
heraus, so dass nur noch /src/java übrig bleibt. Hierfür klicken wir auf dem /src/java-Zweig
mit der rechten Maustaste den Menüpunkt CONFIGURE INCLUSION/ EXCLUSION FILTERS an. Er
definiert, welche Verzeichnisse unterhalb von /src/java nicht in den Compile mit einbezo-
gen werden sollen. Um die gegebenenfalls unerwünschten Packages examples und tools
auszuschließen, fügen wir nun im Fenster EXCLUSION PATTERN über den Button ADD MUL-
TIPLE die Verzeichnisse examples und tools (siehe Abb. 2.14) hinzu.

32
eclipse_v01.book Seite 33 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

Abb. 2.14: Source Folder Inclusion and Exclusion Patterns

Gleiches können Sie auf Wunsch mit dem org-Verzeichnis tun.


Nach der Bestätigung mit dem Button OK ist das Source-Verzeichnis, aus dem wir ORO
laufen lassen wollen, hinreichend beschrieben. Den DEFAULT OUTPUT FOLDER stellen wir von
ORO/bin auf 6ORO/classes ein (dies ist eine der Default-Eigenschaften, die in den Prefe-
rences definiert werden kann, siehe Kapitelabschnitt 2.12).
Eclipse zieht zusätzlich zum Sourcecode auch die jakarta-oro-2.0.8-JAR-Bibliothek mit he-
ran. Auch sie finden wir noch im Projektbaum vor. Über die rechte Maustaste auf der Datei
und den Menüpunkt REMOVE FROM BUILD PATH können wir diese nun wieder ausschließen.
Für jedes erkannte Source-Verzeichnis lässt sich auf Wunsch ein anderes Output-Verzeich-
nis für die Classfiles definieren. Zu diesem Zweck wird die Checkbox ALLOW OUTPUT FOL-
DERS FOR SOURCE FOLDERS aktiviert. Für das Beispiel macht diese Funktion allerdings keinen
Sinn, weshalb wir die Checkbox deaktiviert belassen.

33
eclipse_v01.book Seite 34 Montag, 30. Januar 2006 12:02 12

Eclipse-Projekte individuell konfigurieren

Wenn ein entsprechend umfangreiches Projekt vorliegt und die generier-


ten Classfiles außerhalb noch weiterbearbeitet werden sollen (z.B. in einer
Ant-Task) kann es sinnvoll sein, Teile des Sourcecodes von anderen ört-
lich zu trennen. Für das hier betrachtete einfache Beispiel bringt das aber
——

keine Vorteile.

Abschließend klicken wir den FINISH-Button an, um die Projekt-Properties zu verlassen,


und führen somit den Compile für das Projekt durch, das daraufhin wie in Abb. 2.15 aus-
sehen sollte.

Abb. 2.15: ORO-Projektstruktur

Möglicherweise (je nach Version, die von ORO vorliegt) erscheinen eine Reihe von Warn-
dreiecken, die in der Problems View von Eclipse als nicht verwendete Importstatements in
diversen Klassen ausgewiesen werden. Diese Warnings können wir wie im vorhergehenden
Kapitel beschrieben wegfiltern. Sie würden allerdings als Warndreiecke weiterhin zu sehen
sein. Alternativ lassen sich auch die Compile-Einstellungen so ändern, dass diese Fehler
keine Warnungen erzeugen.

34
eclipse_v01.book Seite 35 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

2.3.1 Vorhandenen Sourcecode ins Projekt Link to Source


Grundsätzlich lässt sich darüber diskutieren, wie mit Sourcecode umgegangen werden
sollte, der gleichzeitig in Form von Classfiles vorliegt. Im obigen Beispiel haben wir den
Sourcecode nicht importiert, sondern quasi das Projekt aus dem normalen Eclipse-Work-
space heraus auf der Festplatte verlinkt, um dort unseren Compile durchzuführen.

Import Source
Würde die normale Importfunktionalität von Eclipse benutzt werden (Menü FILE | IMPORT
|FILE SYSTEM), würde im Normalfall ein Projekt im Eclipse-Workspace-Verzeichnis ange-
legt werden und alle bereits vorhandenen Dateien müssten noch einmal dort hineinkopiert
werden. Das ist z.B. dann sinnvoll, wenn der Sourcecode nicht in einzelnen Dateien vor-
liegt, sondern in einem Jar-File, er aber trotzdem gesehen werden soll, oder wenn die ur-
sprünglichen Dateien durch Modifikationen in Eclipse nicht verändert werden sollen.

Attach Source
Eclipse bietet die Möglichkeit wie auch beim Sourcecode des Java Runtime Environments,
Sourcecode nachträglich zu „attachen“. Wenn also neben dem Sourcecode auch der zustän-
dige Bytecode bereits vorliegt, werden in das Projekt die entsprechenden Class- bzw. Jar-
Files importiert und Eclipse wird durch Doppelklick auf dem entsprechenden Classfile mit-
geteilt, den Sourcecode hierzu anzuzeigen.

Die Varianten im Vergleich


Die Auswirkungen aller drei Varianten sind allerdings sehr unterschiedlich. Letztgenannte
ist zwar sehr schnell und einfach, weil sie keinen Recompile und kein Vorhandensein ab-
hängiger Libraries benötigt, dafür kann man nicht vernünftig durch diese Klassen debug-
gen.
Wird der komplette Sourcecode über die Import-Funktion (rechte Maustaste auf dem Java-
Projekt oder über das Menü FILE | IMPORT) importiert, wird er effektiv eigentlich nur unnö-
tigerweise verdoppelt, ohne dadurch einen Mehrwert zu haben.
Wird zum Sourcecode verlinkt, existiert der Sourcecode nur einmal und er lässt sich debug-
gen. Der Nachteil bei der Variante ist, dass alle abhängigen Libraries vorhanden sein müs-
sen, um kompilieren zu können. Dafür können aber gegebenenfalls auch Modifikationen
am Sourcecode vorgenommen werden.

35
eclipse_v01.book Seite 36 Montag, 30. Januar 2006 12:02 12

Eclipse-Projekte individuell konfigurieren

Als ratsam hat sich erwiesen, unter bestimmten Umständen entgegen dem
obigen Beispiel Sourcecode und das fertig kompilierte Jar-File mit den
Classfiles gleichzeitig zu importieren. In diesem Fall wird der Sourcecode
quasi doppelt importiert. Wenn man überlegt, dass die JVM streng nach
——

der Reihenfolge der Klassen im Classpath die Classfiles den Classloadern


übergibt, macht es Sinn, im Register ORDER der Build Path-Properties des
Projekts die Sourcecode-Dateien vor die restlichen Libraries zu schieben.
Je nach Eclipse-Version und verwendetem JDK zeigt sich Eclipse dann
großzügig und debuggt durch die Sourcecode-Dateien, während im Hinter-
grund für fehlerhafte Klassen, die nicht kompiliert werden konnten, die
Classfiles aus dem Jar-File herangezogen werden. Dies macht auch dann
Sinn, wenn einzelne Funktionen eines Frameworks testhalber deaktiviert
werden sollen und dem Original eine „Fake“-Klasse untergejubelt werden
soll, die weiter vorn in den Build- und Runtime-Classpath gelegt wird.

2.3.2 Projekteigenschaften definieren


Um die Eigenschaften des ORO-Projekts neu zu konfigurieren (siehe Abb. 2.16), klicken
wir auf den Namen des ORO-Projekts mit der rechten Maustaste und wählen den Unter-
punkt PROPERTIES oder drücken Ç+Æ. Auf der linken Seite erscheinen die Kategorien,
für die Einstellungen vorgenommen werden können, auf der rechten Seite daneben die zu-
geordneten Parameter, die durch Anklicken des OK-Buttons übernommen werden können.

Abb. 2.16: Java-Projekt – Eigenschaften

36
eclipse_v01.book Seite 37 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

Über die integrierte Combobox können Sie in den Properties nach Schlagwörtern suchen,
was bei umfangreichen Einstellungen und vielen installierten Plug-Ins sehr hilfreich sein
kann, um den Überblick nicht zu verlieren. Mittels der Pfeile in der rechten oberen Bild-
hälfte ist es möglich zwischen einzelnen Eigenschaftsseiten und deren Konfigurationen
hin- und herzublättern.

Compiler-Einstellungen
Im Unterpunkt JAVA COMPILER nehmen wir die Einstellungen für die individuelle Konfigu-
ration des Compile-Vorgangs bezogen auf dieses einzelne ORO-Projekt vor. Wir aktivieren
die Checkbox für USE PROJECT SETTINGS, um das Projekt separat zu konfigurieren. Hier kön-
nen Sie nun auch nochmals – falls noch nicht geschehen – den Compiler-Level auf 5.0-Un-
terstützung umschalten. Neben der separaten Einzeleinstellung der generierten Class-File-
Versionen findet sich darunter auch die sehr wichtige Konfiguration der zu generierenden
Attribute der Class-Files. Wer sicher gehen möchte, dass keine Probleme während des De-
buggens auftreten, sollte an diesen Einstellungen möglichst wenige Modifikationen vor-
nehmen.
Auf der linken Seite finden Sie im Baum unterhalb der Basis-Compiler-Einstellung auch
weitere Eigenschaftsseiten für den Build-Prozess und die erzeugten Warnungen und Fehler,
die der Compiler dabei ausgibt. Wir klicken nun den Unterpunkt ERRORS/WARNINGS an, ak-
tivieren die Checkbox USE PROJECT SETTINGS und klicken das Element UNNECESSARY CODE
an. Wir finden hier eine ganze Reihe von Prüfroutinen, darunter auch auf nicht verwendete
Importstatements, die u.a. schuld an den Build-Warnungen bei ORO sind. Hier stellen wir
die Combobox UNUSED IMPORTS von WARNING auf IGNORE um. Dies führt beim Kompilieren
zum Ignorieren der unbenutzten Importstatements im Sourcecode.
Grundsätzlich sollte im Projekt vereinbart werden, wie streng der Compile bei der Arbeit
vorgeht, für welche Classfile-Version kompiliert wird und wie der Build-Path (welche Lib-
raries verwendet werden) aussehen soll. Für die projektübergreifende Dokumentation der
Compiler-Einstellungen siehe Abschnitt 2.12.
In diesem ERRORS/WARNINGS-Abschnitt finden sich sehr unterschiedliche Elemente, die den
Build-Prozess beeinflussen können. Die Einstellungen für POTENTIAL PROGRAMMING PRO-
BLEMS und NAME SHADOWING AND CONFLICTS helfen schnell und einfach, bereits bei der Ko-
dierung mögliche Unstimmigkeiten im Sourcecode aufzudecken. Eclipses Fähigkeit, dabei
jede Java-Klasse sofort beim Speichern kompilieren zu können (inkrementelle Updates),
hilft beim schnellen Aufdecken von Fehlern.
Unter diesen Einstellungen findet sich auch eine, die serialisierbare Klassen auf das Vor-
handensein eines serialVersionUID-Felds prüft. Da diese Prüfung nur bedingt sinnvoll ist,
sei an dieser Stelle für das ORO-Projekt empfohlen, den Punkt SERIALIZABLE CLASS WITHOUT
SERIALVERSIONUID auf die Prüfung IGNORE zu schalten.

Auch für die Java 5-Version wurden hier zahlreiche eigene Einstellungen integriert, die sich
um Prüfungen bezüglich der neuen Sprachfeatures ranken. Darunter finden sich sowohl
Checks für generische Typen als auch Autoboxing (J2SE 5.0 OPTIONS). Da dieses ORO-Pro-
jekt noch Java-1.4-kompatibel ist, zielen zahlreiche Warnungen des Compilers auf unge-

37
eclipse_v01.book Seite 38 Montag, 30. Januar 2006 12:02 12

Eclipse-Projekte individuell konfigurieren

typte List-Elemente ab, die sich darin finden. Um diese Probleme zu umgehen, wählen Sie
in der Combobox UNCHECKED GENERIC TYPE OPERATIONS den Checkbox-Punkt IGNORE aus.
Im Unterelement BUILDING der Java-Compiler-Einstellung lassen sich auf die Projekte im
Workspace bezogene weitere Build-Einstellungen vornehmen, die nur indirekt zum Java-
Compiler in Beziehung stehen.

Die Einstellungen im Bereich BUILDING sind besonders dann interessant,


wenn man beispielsweise mit vielen separaten Source- und unterschiedli-
chen Ausgabe-Compile-Verzeichnissen arbeiten möchte. Hier lässt sich
nun konfigurieren, ob Eclipse auf unvollständige oder „kaputte“ Build-
——

Pfade (z.B. Jar-Files, die tatsächlich derzeit nicht verfügbar sind) reagieren
soll und ob zirkuläre Abhängigkeiten zwischen Verzeichnissen erlaubt
sind. In letzterem Fall wird Eclipse nur schwer handhabbar, wenn man
nicht gleichzeitig Einfluss nimmt, wann die IDE auch die Ausgabeverzeich-
nisse für einen Recompile löscht (Scrub output folders when cleaning pro-
jects). Diese Einstellungen sind sehr mächtig, Änderungen an diesen
können aber auch zu sehr unangenehmen Nebeneffekten führen (z.B.
existierende Class-Files ohne zugehörige Java-Dateien).

Der Unterpunkt JAVA TASK TAGS ermöglicht es, das bereits definierte Tag TODO (siehe Ab-
schnitt 2.1.10) von der Liste der geprüften Tags zu entfernen. Wenn nicht großes Interesse
an der Dokumentation von Alpha- oder Betaversionskommentaren besteht, ist es hin und
wieder vorteilhaft, sich von diesem Ballast zu befreien. Wir löschen den TODO-Tag heraus.

Info und Java Build Path


Die Dialogpunkte INFO und JAVA BUILD PATH des Projekteigenschaftsdialogs schauen wir an
dieser Stelle nicht weiter an. Die Info gibt grundsätzliche Informationen über das Projekt.
Beim Punkt JAVA BUILD PATH handelt es sich um die Zusammenstellung der Source- und
Class-Verzeichnisse und der Libraries, die zum Compile herangezogen wurden (siehe Ab-
schnitt 2.3).

Java Code Style


Unter dem Unterpunkt JAVA CODE STYLES lassen sich individuelle Einstellungen für die Ko-
dierungsrichtlinien dieses Projekts vornehmen. Dies ist besonders in größeren Projekten
mit klar formulierten Design- und Coderichtlinien sehr hilfreich, wenn zahlreiche Einstel-
lungen bereits während der Programmierphase geprüft oder bei der Erstellung entspre-
chend unterstützt werden.

Javadoc Location
Der Dialogpunkt JAVADOC LOCATION bietet die Möglichkeit, die aus dem Javadoc durch die
Entwickler erzeugte API-Dokumentation in das Projekt einzubringen. Da wir bereits in
dem Beispiel den fertigen Sourcecode importiert haben, ist die Javadoc-Dokumentation
quasi schon inklusive. Aber besonders bei Klassen von Drittanbietern, deren Sourcecode
nicht vorliegt, ist das Hinzufügen der API-Dokumentation sinnvoll.

38
eclipse_v01.book Seite 39 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

Die in dieser Kurzvorstellung präsentierten Einstellungen für das ORO-Projekt lassen sich
selbstverständlich auch für die gesamte Workbench, sprich alle Projekte, gleichzeitig
durchführen. Sie finden diese stets über das WINDOW | PREFERENCES-Menü oder den Link
CONFIGURE WORKSPACE SETTINGS in diesem Eigenschaftendialog.
Schließen Sie nun den Eigenschaftsdialog des ORO-Projekts mit dem Klick auf den Button
OK. Haben Sie einige oder sogar alle der oben empfohlenen Einstellungen vorgenommen,
werden Sie nun gefragt, ob das Projekt sinnvoller neu kompiliert werden sollte, um diese
auch zu aktivieren. Wir bestätigen in diesem Fall mit OK. Haben Sie alle Einstellungen vor-
genommen, sollte das Projekt selbst jetzt fehlerfrei kompilieren.

Fremde Projekte referenzieren


Wir öffnen nun noch einmal die PROPERTIES des RegExtTest-Projekts über die Tastenkombi-
nation Ç+Æ nach Anklicken des Projektnamens im PACKAGE EXPLORER und klicken
auf die Einstellungen des BUILD PATH.
Da die beiden angelegten Projekte RegExTest und ORO sich gegenseitig nicht kennen, muss
das Projekt RegExTest die ORO-Klassen beim Build berücksichtigen. Dies wird dadurch
abgewickelt, dass das ORO-Projekt beim Compile referenziert wird.
Klicken Sie im Register PROJECTS den Button ADD an und wählen Sie das nun neu zu asso-
ziierende ORO-Projekt. Klicken Sie dann auf OK, damit das Projekt in den BUILD PATH-
Eigenschaften von RegExTest erscheint. Auf diese Art und Weise werden die ORO-Klassen
aus den Source-Pfaden des ORO-Projekts für das RegExTest-Projekt sichtbar.

Vom ORO-Projekt selbst wiederum referenzierte weitere Projekte, JAR-


Files oder Verzeichnisse werden erst auf Wunsch über eine separate
Export-Funktion im Register Classpath sichtbar.

Für das RegExTest-Projekt erscheinen die Klassen aus ORO wie in einem einzigen großen
Projekt, während ORO umgekehrt von RegExTest gar nichts weiß.

Das gegenseitige Referenzieren sollte nicht übertrieben werden, um


bösen Überraschungen vorzubeugen. Besonders zirkuläre Beziehungen
kann Eclipse nicht vernünftig bearbeiten, da es nicht weiß, wer nun bei der
Definition der Klassen die Vorreiterrolle spielt und wie der Classpath für

den Compile und zur Laufzeit zu bilden ist. Dennoch sind sie grundsätzlich
möglich, wie wir bereits gesehen haben. Besonders wenn Fremd-Plug-Ins
oder unterschiedliche Library-Versionen oder Implementierungen benutzt
werden (was z.B. bei XML-Parser-Verwendung leicht passieren kann), ist
das Chaos schnell sehr groß. ClassNotFound-Exceptions, MethodNot-
Found-Probleme usw. können leicht durch unerwartete Bibliotheken und
Projektreferenzen entstehen.

39
eclipse_v01.book Seite 40 Montag, 30. Januar 2006 12:02 12

Sourcecode-Editor-Features

2.4 Sourcecode-Editor-Features
Der Sourcecode-Editor hat eine große Fülle an Features zu bieten, die nun anhand des Bei-
spiels betrachtet werden sollen. Grundsätzlich kann der Sourcecode vollkommen ohne wei-
tere Unterstützung über die üblichen Tastenkombinationen, wie z.B. Cut and Paste, bear-
beitet werden. Eclipse 3.2 bietet zusätzlich über die Cut and Paste-Funktionalität auch an,
Klassen, die sich in der Zwischenablage als Sourcecode befinden, automatisch ohne Anle-
gen expliziter Java-Dateien in Eclipse zu integrieren und zu kompilieren.
Wir ergänzen den Sourcecode der ExpressionAnalyzer-Klasse, so dass er wie folgt aus-
sieht (die Kommentare können dabei wahlweise ignoriert werden, die Fehler im Source-
code werden wir im Folgenden beseitigen):

package com.entwickler.eclipsebuch.kapitel2.analyzer;
import java.util.Vector;

/**
* ExpressionAnalyzer Test-Klasse
*/

public class ExpressionAnalyzer {


public Vector <String> getAllOccurrencesOfPattern(final String
originalContent,final String patternString) {
// Erzeugen eines Ergebnisvectors:
Vector <String> results = new Vector<String>();
// Jedes neue Muster muss zunächst von einem Compiler
// kompiliert werden:
Perl5Pattern pattern =
(Perl5Pattern) regexpCompiler.compile(patternString);
// Für das Auffinden von Mustern benötigen wir einen
// so genannten Matcher und sinnvollerweise
// einen zugehörigen Input, der sich als Objekt aus dem
// Original-Inhalt, den wir der Methode übergeben, ergibt:
Perl5Matcher occurencesMatcher = new Perl5Matcher();
PatternMatcherInput input = new PatternMatcherInput
(originalContent);
return null; // dieser Fehler ist beabsichtigt!
}
}

Sourcecode Folding
Seit Version 3.0 beherrscht Eclipse nach Vorlage aus dem Microsoft .NET Visual Studio
und NetBeans nun auch das Sourcecode Folding. Über die kreisförmig umrahmten +- und
–-Zeichen links des Sourcecodes können einzelne Methoden oder ganze Klassen „zusam-
mengefaltet“ werden, um Überblick im Sourcecode zu erlangen.

40
eclipse_v01.book Seite 41 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

Formatierung des Sourcecodes


Wir wollen nun dem Sourcecode etwas Struktur verleihen. Hierzu kehren wir zurück in die
Methode in der ExpressionAnalyzer-Klasse, klicken die rechte Maustaste und wählen
im Untermenü SOURCE den Punkt FORMAT (Ÿ+Á+F) (siehe Abb. 2.17). Daraufhin
wird der Sourcecode so formatiert, wie wir es in den globalen Preferences von Eclipse ein-
gestellt haben.
Sie können so den kompletten Sourcecode einer Klasse, bei Klick auf ein Package sogar
innerhalb eines Packages oder Projekts, oder gegebenenfalls nur markierte Textbereiche
formatieren.

Abb. 2.17: Sourcecode-Aktionen

Neben dieser kleinen „Schönheits-OP“ verbirgt das Source-Menü aber eine ganze Reihe
weiterer Features. Bevor wir diese betrachten, fügen wir für die unbekannten Klassen die
entsprechenden Importstatements hinzu, die derzeit einen fehlerlosen Compile verhindern.
Dazu wählen wir ebenfalls im Untermenü SOURCE den Punkt ORGANIZE IMPORTS und drü-
cken nach dem automatischen Einfügen der Imports durch Eclipse Ÿ+S zum Spei-
chern.
Jetzt führt nur noch die Variable regexpCompiler zu einem Fehler. Falls weitere Fehler
auftreten, könnte es daran liegen, dass das ORO-Projekt vom RegExTest-Projekt aus nicht
korrekt – wie oben beschrieben – referenziert wurde (siehe Abschnitt 2.3.1).
Hinweis: Teilweise beherrscht Eclipse ein automatisches Organize Import. Wenn zwischen
zwei Klassen Sourcecode kopiert wird und sich die Referenzen auf Klassen des eigenen
Projekts beziehen, wird das Organize Imports unter Umständen von der IDE bereits ohne
weiteres Zutun durchgeführt.
Die Indentation, also die Einrückung des Sourcecodes, kann per Hand mit der Å-Taste
vorgenommen werden, wobei Á+Å die Einrückungen rückgängig macht. Der Wert
für die Anzahl der Leerzeichen pro Å-Tastendruck lässt sich in den PREFERENCES (siehe
Abschnitt 2.12) einstellen. Über den Menüpunkt CORRECT INDENTATION im Source-Kontext-
menü lässt sich zudem die Einrückung automatisch korrigieren.
Die nachfolgenden Funktionen erreicht man über das Untermenü SOURCE mit der rechten
Maustaste im geöffneten Editorfenster:

Kommentare
Mit dem Menüpunkt TOGGLE COMMENT (Ÿ+/) lässt sich für einen mit der Maus mar-
kierten Bereich ein Kommentar (//) erzeugen bzw. löschen. Mit den darunter folgenden
Aktionen ADD BLOCK COMMENT (Ÿ+Á+/) und REMOVE BLOCK COMMENT
(Ÿ+Á+\) ist selbiges auch im C++-Kommentarstil (/*) möglich.

41
eclipse_v01.book Seite 42 Montag, 30. Januar 2006 12:02 12

Sourcecode-Editor-Features

Wenn Sie jetzt korrekterweise noch den Javadoc-Kommentar vermissen: Dieser lässt sich
über den Unterpunkt ADD JAVADOC COMMENT (Ç+Á+J) anlegen. Der Javadoc-Kom-
mentar bezieht sich dabei immer auf das jeweilige Java-Element, das zurzeit im Source
markiert ist.

Die unterschiedlichen hier vorgestellten Funktionen sind nicht immer alle


aktiv. Dies ist abhängig davon, ob man beispielsweise einen Textbereich
markiert hat oder nur eine einzelne Sourcecode-Stelle. Ebenso wichtig ist
es, ob an dieser Stelle die jeweilige Aktion auch syntaktisch überhaupt

durchführ- bzw. anwendbar ist bzw. ob die Datei nicht schreibgeschützt ist
oder der Sourcecode nur attacht wurde und nicht geändert werden kann.

Importstatements
Das Ändern aller Importstatements gleichzeitig haben wir oben bereits gesehen. Ist ein
Klassenname im Kontext aller Libraries, die sich in einem Build-Path befinden, nicht ein-
deutig, bietet Eclipse eine entsprechende Dialogauswahl an. Über den Untermenüpunkt
ADD IMPORT (Ÿ+Á+M) kann im Gegensatz zu ORGANIZE IMPORTS (Ÿ+Á+O)
nur ein einzelnes Importstatement angelegt werden, und zwar für die Klasse, auf der sich
der Cursor zurzeit befindet.
Der Vollständigkeit halber sollten wir die Features in Bezug auf Imports auch abrunden.
Wenn der Cursor auf einer unbekannten Klasse steht, zeigt Eclipse an, dass es die Klasse
nicht kennt. Lassen wir den Cursor stehen und bewegen die Maus hinter den Namen der
Klasse, zeigt Eclipse an, welche Klasse es hinter dem Klassennamen vermutet. Gehen wir
mit dem Cursor direkt hinter den Klassennamen und drücken Ÿ+Leertaste, generiert
Eclipse (soweit es die Klasse im Build-Path findet) das Importstatement automatisch.
In Abschnitt 2.12 ist beschrieben, wie die Reihenfolge der Importstatements beeinflusst
werden kann.

Override/Implement Methods/Generate Getters and Setters


Diese Menüpunkte dienen dem Überschreiben von Methoden (Abschnitt 2.5.1) in Unter-
klassen und dem Erzeugen von Getter- und Setter-Methoden (Abschnitt 2.4.2). Für eine de-
tailliertere Beschreibung sei auf die jeweiligen Kapitelteile verwiesen.

Generate Delegate Methods


Öffnet den Delegate-Methoden-Dialog, der es erlaubt, Delegate-Methoden für Felder des
aktuellen Typs (Klasse, Interface) zu erzeugen.

Add Constructor from Superclass


Fügt über einen Wizard der aktuellen Klasse einen weiteren Superklassen-Konstruktor an
der definierten Sourcecode-Stelle (INSERT POINT) hinzu. Der implizite Aufruf des Default-
Konstruktors kann dabei wahlweise ausgespart werden (OMIT CALL TO DEFAULT CONSTRUC-
TOR).

42
eclipse_v01.book Seite 43 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

Generate Constructor using Fields


Erzeugt über einen Wizard einen Konstruktor für diese Klasse, der die ausgewählten In-
stanzvariablen der Klasse enthält.

2.4.1 Quick Fix auf Variablen


Dass Quick Fixes auf Fehlern schnell zur Lösung derselbigen beitragen, haben wir schon in
Abschnitt 2.2 gesehen. Jetzt wollen wir die QuickFix-Möglichkeiten auf der Variable reg-
expCompiler nutzen, um die fehlende Variable zu generieren und Getter- und Setter-Me-
thoden für sie zu erzeugen. Wir klicken dafür erneut die kleine Hinweislampe neben dem
Sourcecode oder die regexCompiler-Variable an und drücken Ÿ-1.

Abb. 2.18: Quick Fix – Variablenerzeugung

Eclipse bietet eine ganze Reihe an Lösungsmöglichkeiten an (siehe Abb. 2.18), angefangen
vom einfachen Anlegen einer lokalen Methodenvariablen, über eine Instanzvariable, einen
neuen Methodenparameter bis zur Generierung von nicht ganz Java-Style-like Klassen und
Interfaces mit kleinem führenden Anfangsbuchstaben.
Wir entscheiden uns für die Erstellung des privaten Felds (zweiter QuickFix-Punkt).
Da Eclipse den Typ der Variablen, die danach gecastet wird, innerhalb des Methodenauf-
rufs nicht erraten kann, erzeugt es ein relativ unspektakuläres Feld:

private Object regexpCompiler;

Wir werden den Typen modifizieren und für dieses Feld, das private deklariert worden
ist, noch eine Setter-Methode definieren.

2.4.2 Automatische Getter/Setter-Erzeugung


Zur automatischen Erzeugung der Getter- und Setter-Methoden markieren wir mit der Maus
den Namen der Variablendeklaration (regexpCompiler) oder selektieren den Instanz-
variablennamen im PACKAGE EXPLORER und wählen über die rechte Maustaste im Kontext-
menü SOURCE den Unterpunkt GENERATE GETTERS UND SETTERS. Die automatische Generie-
rung von Getter- und Setter-Methoden ist besonders dann hilfreich, wenn eine ganze Reihe
von Accessor-Methoden für eine Menge von Instanzvariablen erzeugt werden soll, ohne
jede einzelne manuell anzulegen und Javadoc-Kommentare zu erzeugen. Interessant sind
auch die Steuerung des INSERTION POINT, bei dem angegeben werden kann, wo im Source-

43
eclipse_v01.book Seite 44 Montag, 30. Januar 2006 12:02 12

Sourcecode-Editor-Features

code die neue Methode platziert werden soll, und die SORT BY-Optionen, die die Reihenfolge
der angelegten Methoden kontrollieren.
Über die Checkbox GENERATE METHOD COMMENT lassen sich die typischen Methodenkom-
mentare erzeugen. In den Generierungsschritt von Kommentar und Methodenname können
Sie allerdings über entsprechende CODE TEMPLATES separat eingreifen. Klicken Sie zur Kon-
figuration hier auf den Link für die Code Templates. Sie werden aus den Workbench Pre-
ferences bedient, die wir später noch näher kennen lernen.
In Abb. 2.19 ist der Dialog für die Erzeugung von Getter- und Setter-Methoden dargestellt.
Wir klicken dabei die Get-Methode aus und wählen anschließend OK zur Generierung des
Setters.

Abb. 2.19: Getter/Setter-Erzeugung im Dialog

2.4.3 Sourcecode-Vervollständigung (Code completion)


Da Object als Klasse innerhalb der Deklaration für die regExpCompiler-Variable nicht
besonders sinnvoll ist, ersetzen wir sie durch PatternCompiler. Leider bietet Eclipse
hierfür noch keine vernünftige Refactoring-Maßnahme an. Allerdings können wir uns die
Code completion zu Nutze machen. Wir löschen dazu den Begriff Object und geben statt-
dessen den Begriff PatternCompiler nur unvollständig ein (z.B. „PatternC“) und drü-
cken dann mit dem Cursor hinter dem letzten Buchstaben die Tastenkombination
Ÿ+Leertaste. Eclipse bietet daraufhin eine Liste von möglichen Vervollständigungen

44
eclipse_v01.book Seite 45 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

an. Über die Cursortasten lässt sich eine auswählen oder der Begriff manuell vervollständi-
gen. Zu bekannten Klassen zeigt Eclipse gleichzeitig daneben den Javadoc-Kommentar zur
Hilfestellung an. Die Auswahl einer Klasse führt implizit auch zur Generierung eines zu-
gehörigen Importstatements.
Eclipse unterscheidet sich bei der Code completion von den meisten anderen IDEs durch
die umfangreiche Liste der Vervollständigungsangebote. Während andere IDEs teilweise
noch die Klassen- und Methodensignaturen umständlich neu ermitteln müssen, macht
Eclipse dies intern automatisch – genauso wie die Auswahl des zugehörigen Javadoc-Kom-
mentars.

Wird Ÿ+Leertaste hinter einer schon voll ausgeschriebenen, allerdings


noch ohne Importstatement versehenen Klasse gedrückt, ergänzt Eclipse
automatisch das fehlende Importstatement (soweit die Klasse nicht unter
einem anderen Package bereits in den Imports vorhanden ist).

Was für die Vervollständigung von Klassennamen gilt, gilt genauso für die Vervollständi-
gung von Methoden- oder Variablennamen – bei den Variablen sogar bezogen auf Instanz-
oder lokale Variablen, die in der jeweiligen Methode verfügbar sind. Wurde noch keine Ein-
gabe getätigt, bietet Eclipse bei Ÿ+Leertaste eine Auswahl der vorhandenen Felder,
Methoden und Variablen der Klasse an.
Mittlerweile sieht der Sourcecode unseres Beispiels in etwa so aus (die Kommentare sind
der Einfachheit halber weggelassen, die noch vorhandenen Fehler sind markiert):

package com.entwickler.eclipsebuch.kapitel2.analyzer;

import java.util.Vector;
import org.apache.oro.text.regex.PatternCompiler;
import org.apache.oro.text.regex.PatternMatcherInput;
import org.apache.oro.text.regex.Perl5Matcher;
import org.apache.oro.text.regex.Perl5Pattern;

public class ExpressionAnalyzer {


private PatternCompiler regexpCompiler;
public Vector<String> getAllOccurrencesOfPattern(
final String originalContent,
final String patternString) {
Vector<String> results = new Vector<String>();
// hier fehlt noch ein Try-Catch-Block:
Perl5Pattern pattern = (Perl5Pattern) regexpCompiler
.compile(patternString);
Perl5Matcher occurencesMatcher = new Perl5Matcher();
PatternMatcherInput input =
new PatternMatcherInput(originalContent);
return null; // hier wird noch null zurückgegeben
}

45
eclipse_v01.book Seite 46 Montag, 30. Januar 2006 12:02 12

Sourcecode-Editor-Features

public Object getRegexpCompiler() {


return regexpCompiler;
}
public void setRegexpCompiler(Object regexpCompiler) {
// hier gibt's noch einen Konvertierungsfehler:
this.regexpCompiler = regexpCompiler;
}
}

Jetzt gibt es noch einen Compile-Fehler in der setRegexpCompiler-Methode und ein


Try-Catch-Problem. Beide Fehler werden wir als Nächstes beseitigen.

2.4.4 Try-Catch-Block-Generierung
Im Beispiel-Sourcecode muss die MalformedException abgefangen werden. Dazu mar-
kieren wir alle bisher kodierten Zeilen der gesamten Methode mit der Maus und wählen
dann über das Menü hinter der rechten Maustaste SOURCE | SURROUND WITH TRY/CATCH
BLOCK. Eclipse scannt die einzelnen Zeilen ab, um herauszufinden, welche unterschiedli-
chen Exception-Typen generiert werden können. Kann Eclipse keine finden, bietet es die
Möglichkeit, RuntimeExceptions im Catch-Block abzufangen. Die Generierung schlägt bei
unvollständig markierten Blöcken und anderen vorhandenen Compile-Fehlern im markier-
ten Bereich fehl (siehe Abb. 2.20).

Abb. 2.20: Try-Catch-Block-Generierung

46
eclipse_v01.book Seite 47 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

Grundsätzlich bezieht sich die Generierung von Try-Catch-Blöcken nur auf von ja-
va.lang.Exception abgeleitete Exception-Klassen. RuntimeException, Error und
Throwable bleiben außen vor.
Den von Eclipse erzeugten Catch-Block füllen wir mit der Zeile:

throw new BusinessException("Error in pattern compile and"


+ " execution:"+patternString);

Hierbei erzeugt die Erstellung des Try-Catch-Blocks auch eine TODO-Task. Damit infor-
miert Eclipse über den noch zu füllenden Bereich und schreibt den Inhalt der Exception
über e.printStackTrace() in den Outputstream. Die BusinessException ist Eclipse
unbekannt, so dass es nach dem Compile zu entsprechenden Fehlermeldungen kommt. Mit
einem Klick auf das Fehlerlämpchen kann die Generierung einer neuen BusinessExcep-
tion-Klasse forciert werden. Eclipse geht dabei davon aus, dass die neue BusinessEx-
ception-Klasse von der java.lang.Exception-Klasse erbt und in das gleiche Package
gehört.
Wir lassen uns also über den Quick Fix eine entsprechende Exception-Klasse erzeugen.
Hierbei müssen wir darauf achten, die Checkbox CONSTRUCTORS FROM SUPERCLASS anzukli-
cken, um einen entsprechenden Konstruktor für die Klasse zu generieren, der auch den
String als Parameter entgegennehmen kann.
Es bleibt schließlich noch die fehlende throws-Information in der Methodensignatur der
getAllOccurrencesOfPattern-Methode. Wir können über einen Quick Fix das ge-
wünschte Ergebnis erzielen, indem wir das kleine Lämpchen auf der linken Seite anklicken
und CREATE CLASS 'BUSINESSEXCEPTION' wählen.
Eclipse öffnet jetzt einen Wizard zur weiteren Definition der Klasse. Wir belassen einfach
die Standardeinstellungen wie sie sind und klicken den OK-Button an.
Die neu generierte Exception wird erzeugt. Die Signatur der Methode hat allerdings noch
keine throws-Erweiterung. Daher klicken wir nach dem Speichern der Klasse erneut auf das
Lämpchen links vom Compile-Fehler und wählen ADD THROWS DECLARATION. Eclipse möch-
te schließlich noch wissen, welchen Exception-Typ die Methode werfen soll. Hier wählen
wir die BusinessException aus (siehe Abb. 2.21).

Abb. 2.21: Erweiterung der Throws-Klausel

Zur endgültigen Vervollständigung der Methode getAllOccurrencesOfPattern ma-


chen wir uns Code-Templates zunutze.

47
eclipse_v01.book Seite 48 Montag, 30. Januar 2006 12:02 12

Sourcecode-Editor-Features

2.4.5 Sourcecode-Templates verwenden


Ein Eclipse-Feature, das auch in die Code completion hineinspielt, ist die Verwendung von
Sourcecode-Templates. Templates sind Vorlagen, die verwendet werden können, um die
Erzeugung immer gleicher Sourcecode-Bestandteile zu beschleunigen. Funktional läuft es
genauso ab wie bei der Code completion. Es wird der Name des Template eingegeben und
die Tastenkombination Ÿ+Leertaste gedrückt. Für den Fall, dass es mehrere Vervoll-
ständigungsmöglichkeiten gibt, erscheint eine Auswahlliste.
Für wichtige Sprachkonstrukte wie Schleifen besitzt Eclipse entsprechende Vorlagen. Für
Iterationsstrukturen, wie z.B. For- oder While-Schleife, aber auch für viele andere Kon-
strukte wie Try-Catch-Blöcke oder Kommentare besitzt die IDE Vorlagen. Bei der De-
finition von Vorlagen werden bestimmte Freiheitsgrade offen gelassen. Dazu zählen bei-
spielsweise die Festlegung von Kontextbausteinen, Methodennamen oder Klassennamen.
Detailliert gehen wir darauf in Abschnitt 2.12 ein.
Eine komplette Übersicht der vorhandenen Templates erhalten wir durch Auswahl von
Menü WINDOW | PREFERENCES im Baum JAVA | CODE STYLE | CODE TEMPLATES.

Erinnerung: Sie können auch in der Such-Combobox im Preferences-Dia-


log den Begriff „code templates“ eingeben, um schnell zur gewünschten
Eigenschaftenseite zu gelangen.

Wir beginnen die Zeile hinter dem PatternMatcherInput und vor dem return-
Statement mit der Eingabe des Befehls while. Danach drücken wir Ÿ+Leertaste, um
die Kontexthilfe aufzurufen. Eclipse bietet drei verschiedene While-Schleifen an. Wir be-
gnügen uns mit der einfachsten (WHILE LOOP WITH CONDITION) und wählen diese aus (siehe
Abb. 2.22).

Abb. 2.22: Code template für eine While-Schleife

Der Editor vervollständigt die Schleife und markiert den noch undefinierten Condition-
Ausdruck. Wir geben als logischen Ausdruck die Buchstaben occ ein und drücken
Ÿ+Leertaste. Der Begriff wird auf occurrencesMatcher automatisch vervollstän-
digt. Anschließend tippen wir einen Punkt ein und drücken erneut Ÿ+Leertaste, um die
Menge aller Methoden zu sehen, die diese Objektinstanz anbietet.
Wenn wir c als ersten Buchstaben der zu suchenden Methode eingeben, filtert Eclipse auf
alle Methoden im occurrencesMatcher-Objekt, die mit c beginnen. Wir wählen die
zweite Methodensignatur (PatternMatcherInput, Pattern) aus, um diese zu erstellen
(siehe Abb. 2.23).

48
eclipse_v01.book Seite 49 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

Abb. 2.23: Code completion – Beispiel

Der Editor hilft bei langen Methodensignaturen, dass Sie nicht den Überblick über die Pa-
rameterreihenfolge verlieren, indem jeweils der gerade eingegebene Parameter im Tooltip
über der Methode fett markiert wird. Der Editor kann auch so konfiguriert werden, dass er
die Variablennamen, die in der Methodensignatur existieren, automatisch in die Code com-
pletion übernimmt. Allerdings ist dies nur selten nützlich. Wir ergänzen die While-Condi-
tion um die Parameter input und pattern. Selbstverständlich können wir auch hierbei
die Code-Vervollständigung ganz nach den eigenen Wünschen anwenden oder weitertip-
pen (siehe Abb. 2.24).

Abb. 2.24: Parameterübergabe

Semantisch haben wir festgelegt, dass die Schleife so lange durchgeführt wird, bis der
Matcher (quasi das Suchprogramm) im Input (in den Daten, die ihm übergeben worden
sind) das Pattern (das Suchmuster) findet.
Wir vervollständigen nun die Methode getAllOccurrencesOfPattern so, dass der
Sourcecode in der Methode wie folgt erscheint (Kommentare sind entfernt):

Vector <String> results = new Vector<String>();


try {
Perl5Pattern pattern =
(Perl5Pattern) regexpCompiler.compile(patternString);
Perl5Matcher occurencesMatcher = new Perl5Matcher();
PatternMatcherInput input = new PatternMatcherInput
(originalContent);
while (occurencesMatcher.contains(input,pattern)) {
MatchResult foundObject = occurencesMatcher.getMatch();
String matchingText = foundObject.group(0);
results.add(matchingText);
}
} catch (MalformedPatternException e) {
e.printStackTrace();
throw new BusinessException("Error in pattern compile and"
+ " execution:"+patternString);
}
return null; // Rückgabewert ist absichtlich falsch!

49
eclipse_v01.book Seite 50 Montag, 30. Januar 2006 12:02 12

Sourcecode-Editor-Features

Prüfen Sie bitte auch, ob die Erzeugung des results-Elements und das return-State-
ment wie im Beispielcode außerhalb des Try-Catch-Blocks sind, um die Übung problemlos
fortsetzen zu können. Sollten Sie erstmals die Code Completion-Funktionen von Eclipse
ausprobiert haben, wird dies vermutlich ohne Erfolg verlaufen sein. Grundsätzlich ist
Eclipse bei der Editierung nur dann behilflich, wenn es über genügend Wissen bezüglich
Klassen und Referenzen verfügt. Da Eclipse die Klasse MatchResult noch nicht kennt,
ergänzen wir das Importstatement entsprechend, indem wir hinter dem Begriff Match-
Result den Cursor setzen und Ÿ+Leertaste betätigen.
Wie im Beispiel-Source zu sehen, holen wir uns aus dem Matcher-Objekt die gefundenen
Results und daraus die erste Gruppe (group(0)). Bei einfachen Regular Expressions ent-
spricht dies dem gesuchten Ergebnisstring. Diesen fügen wir in unseren Result-Vektor re-
sult ein und erhalten so die fertige Ergebnismenge.

2.4.6 Gimmicks des Editors


Über Sinn und Unsinn komplexer Tastenkombinationen zum Fingerbrechen lässt sich zu-
weilen streiten. Eine Reihe dieser scheinbaren Gimmicks, die Eclipse bereithält, seien hier
der Vollständigkeit halber aber genannt.
Einfügen, Verschieben und Duplizieren
Für diese scheinbar einfachen Aufgaben hält Eclipse die Tastenkombinationen Ç+| /
Ç+~ (Aufwärtsverschieben/Abwärtsschieben der aktuellen (markierten) Zeile(n)) be-
reit. Bei zusätzlich gedrückter Ÿ-Taste dupliziert Eclipse die markierten Zeilen in der
per Pfeil gewählten Richtung.
Zum Einfügen neuer Leerzeilen kann Ÿ+Á+Æ für Zeilen oberhalb oder
Á+Æ für Zeilen unterhalb benutzt werden.

Smart Insert
Der Editor unterscheidet selbstverständlich auch zwischen Overwrite (Überschreiben) und
Insert (Einfügen), erreichbar über die ¡-Taste. Dabei kennt er aber auch eine Java-spe-
zifische Variante Smart Insert, deren Verhalten über die Preferences steuerbar ist.

Anzeigen der Typhierarchie


Im Abschnitt 2.9.1 findet sich eine Erläuterung darüber, wie Eclipse detaillierte Informa-
tionen über Vererbungshierarchien zur Verfügung stellt. Eine einfache Version (Quick type
hierarchy view) lässt sich über die Tastenkombination Ÿ+T anzeigen, wenn der Cur-
sor zuvor auf einer Variablen positioniert wird. Mittels der gleichen Tasten lässt sich zwi-
schen normaler und Supertype Hierarchie-Anzeige wechseln.

50
eclipse_v01.book Seite 51 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

2.4.7 Immer ein Undo für die Vergangenheit


Variante 1: Grundsätzlich können alte Zustände einer Arbeit über die Tastenkombination
Ÿ+Z wiederhergestellt (Undo) oder erneut ausgeführt werden (Ÿ+Y, Redo).
Variante 2: Über die rechte Maustaste am linken Editorrand kann eine so genannte Quick
Diff-Referenz (SHOW QUICKDIFF) erzeugt werden. In einem farbig markierten Indikatorstrei-
fen ist erkennbar, ob sich auf Basis der letzten gespeicherten Version an dieser Stelle eine
Änderung ergeben hat. Über die rechte Maustaste besteht auch die Möglichkeit, einzelne
Blöcke (REVERT BLOCK) oder Zeilen (REVERT ADDEDED LINE/REVERT REMOVED LINE) zeitlich zu-
rückzudrehen.
Variante 3: Über die rechte Maustaste und das Menü LOCAL HISTORY erreicht man im Editor
die Standardfunktionen zum Wiederherstellen alter Sourcecode-Zustände. Wie gut dies
funktioniert, hängt davon ab, ob neben dem Eclipse zur Verfügung gestellten RAM-Spei-
cher noch ein Sourcecode-Management-System existiert, das die langfristige Versionie-
rung von Dateien erlaubt.
Eclipse zieht grundsätzlich beim Speichern der Datei eine Version, die es intern mit einem
Zeitstempel belegt. Diese Versionen speichert es für folgende Menüfunktionen zwischen:
COMPARE WITH – Vergleich dieser Version mit einer alten über einen entsprechenden Wizard
REPLACE WITH PREVIOUS – Ersetzen der aktuellen Source-Version mit der letzten gespeicher-
ten
REPLACE WITH – Ersetzen der aktuellen Source-Version mit einer gespeicherten
RESTORE FROM – Wiederherstellung bereits gelöschter Elemente (Methoden, Felder, ...) aus
der lokalen Historie

Kleiner Tipp für Löschexperten: Wenn Sie versehentlich einmal eine Java-
Klasse gelöscht haben sollten, was ohne Source Control Management-
System einer Vernichtung der Datei entspricht, erzeugen Sie eine neue
Java-Klasse mit dem exakt gleichen vollqualifizierten Namen und suchen

Sie mit REPLACE WITH FROM LOCATION HISTORY den letzten noch im Speicher
vorhandenen Stand. So lassen sich oft auch gelöschte Klassen recht ein-
fach wieder rekonstruieren.

2.5 Refactoring mit Eclipse


Refactoring wird hin und wieder mit Umorganisieren übersetzt. Dies ist zwar sehr schön
eingedeutscht, verliert damit aber ähnlich an Charakter, wie wenn Application Server mit
Anwendungsdienstleister übersetzt wird, ohne Berücksichtigung der Bedeutung.
Der Autor Martin Fowler verfasste vor Jahren ein Buch mit dem Titel „Refactoring. Impro-
ving The Design Of Existing Code“1 und beschrieb darin eine Reihe von Mechanismen für
objektorientierte Programmiersprachen, um das Design der Anwendung zu verbessern.

1. Fowler, Martin – Refactoring – Wie Sie das Design vorhandener Software verbessern, 1. Auflage 15.03.2000,
Verlag Addison-Wesley, ISBN 3827316308

51
eclipse_v01.book Seite 52 Montag, 30. Januar 2006 12:02 12

Refactoring mit Eclipse

Spricht man von Design, so ist nicht das Einrücken des Sourcecodes gemeint, sondern die
Art und Weise, wie Klassen und Objekte miteinander in Beziehung stehen und interagieren
(siehe auch Themenstellung Modellierung im Kapitel 3).
„Jeder Dummkopf kann Code schreiben, den ein Computer versteht. Gute Programmierer
schreiben Code, den Menschen verstehen.“1
Normalerweise bemüht sich der Entwickler um ein sauberes Design bei seiner Modellie-
rung, macht sich Gedanken über abstrakte Inhaltsbeschreibungen, konkrete Implementie-
rungen, Komponenten oder Interaktionen. Irgendwann, wenn das fertige Produkt dann an
den Kunden ausgeliefert ist, gibt es dennoch etliche Stellen, wo man von der Schönheit der
ersten Modellierungsstunde abgekommen ist, wo es Redundanzen im Sourcecode gibt, wo
Dokumentation vernachlässigt worden ist oder wo unnötige Abhängigkeiten von Kompo-
nenten untereinander entstanden sind. Meist sind Gründe dafür mangelndes Wissen über
die Ziele des Architekten/Designers, der das Design entwarf, oder Kompromisse, die man
aus Zeitgründen eingegangen ist, um schnell am Markt zu sein. Solche Strategien rächen
sich oft später beim Bugfixing oder in der Wartung.
Refactoring versucht, eine Antwort auf diese Probleme zu liefern, indem es beschreibt, wie
Sourcecode angepasst werden kann, damit er leichter verständlich und gleichzeitig leichter
wartbar wird.
Eclipse bietet eine Reihe von Refactoring-Mechanismen. Selbst wenn nur eine Variable
umbenannt oder eine Klasse von einem Package in ein anderes verschoben werden soll, ist
dies streng genommen eine Art von Refactoring.
Bei der Durchführung einer Refactoring-Maßnahme ergeben sich in der Regel eine Viel-
zahl unerwarteter Abhängigkeiten. Wird z.B. einer Methode ein anderer Name gegeben, ist
es sinnvoll, zu wissen, wer diese Methode aufruft, um an all diesen Stellen entsprechende
Änderungen vorzunehmen.
Wir wählen in unserem Beispiel im Package Explorer im Kontextmenü zur Methode set-
RegExpCompiler den Menüpunkt REFACTOR | CHANGE METHOD SIGNATURE. Als neuen
Parametertyp tragen wir im Register PARAMETERS (siehe Abb. 2.25) PatternCompiler ein
und bestätigen die Eingabe mit Æ. Im Register EXCEPTIONS können wir auch die Ausnah-
men beeinflussen.
Tipp: Das kleine Lämpchen neben der Tabellenzeile zeigt an, dass Ihnen die Vervollständi-
gungsfunktion von Eclipse zur Verfügung steht, so dass man mittels Ÿ-Leertaste un-
vollständige Klassennamen vervollständigen lassen kann.
Interessant ist die PREVIEW-Möglichkeit, bei der Eclipse anzeigt, an welchen Klassen Ände-
rungen vorgenommen werden. Müssen größere Refactoring-Aktionen durchgeführt wer-
den, sind solche Mechanismen unerlässlich.

1. Fowler, Martin - Refactoring, S. 13, s.o.

52
eclipse_v01.book Seite 53 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

Abb. 2.25: Refactoring – Change Method Signature

Beim Refactoring darf der gesunde Menschenverstand nie ausgeschaltet


werden. Ob sich das Design verbessert oder nicht, kann der Editor nicht
entscheiden. Er kann nur helfend zur Seite stehen. Der Entwickler hat
immer die Möglichkeit, einzelne der identifizierten Aktionen in der Preview

auch auszuklicken oder das Redesign von Hand zu beginnen.

Als weiteres Beispiel markieren wir die ersten zwei Zeilen des While-Schleifenblocks in
der Methode getAllOccurrencesOfPattern und wählen im Kontextmenü REFACTOR |
EXTRACT METHOD.
Zur Erinnerung hier die beiden im Editor zu markierenden Zeilen:

MatchResult foundObject = occurencesMatcher.getMatch();


String matchingText = foundObject.group(0);

Eclipse bietet daraufhin an, die Zeilen aus der Methode zu extrahieren und in eine neue
private-Methode zu schreiben (siehe Abb. 2.26).
Als neuen Methodennamen geben wir getMatchingTextFromMatcher ein und belassen
den private-Gültigkeitsbereich. Anschließend aktivieren wir die Checkbox ADD THROWN
RUNTIME EXCEPTIONS TO METHOD SIGNATURE und schauen uns im Preview-Modus das fertige
Ergebnis an. Um die Extrahierung durchzuführen, klicken wir auf OK.
Die Extrahierung findet über diesen Wizard bei Bedarf auch Duplikate im Sourcecode (RE-
PLACE X DUPLICATE CODE FRAGMENTS) und hilft so, mehrere Codestellen gleichzeitig zu refac-
toren.

53
eclipse_v01.book Seite 54 Montag, 30. Januar 2006 12:02 12

Refactoring mit Eclipse

Abb. 2.26: Refactoring – Extract Method

Folgendes erscheint als fertiges Ergebnis in der While-Schleife:

String matchingText = getMatchingTextFromMatcher


(occurencesMatcher);

Bei allen nachfolgenden Erläuterungen gehen entsprechende Refactoring-Dialoge und Pre-


views Hand in Hand. Tabelle 2.2 enthält einen Überblick über die Refactorings.

Tabelle 2.2: Refactoring-Maßnahmen in Eclipse

Refactoring-Maßnahme Durchgeführte Aktion

Rename (Ç-Á-R) Umbenennen von Java-Elementen (siehe Beispiel oben)

Move (Ç-Á-V) Verschieben von Java-Elementen (z.B. Packagechanges)

Change Method Signature Ändern der Methodensignatur (siehe Beispiel oben)

Convert Anonymous Class Wandelt eine anonyme innere Klasse in eine eingebettete,
to Nested benannte Subklasse um

Convert Nested Type to Wandelt eine eingebettete Unterklasse in eine eigen-


Top Level ständige Klasse um und erzeugt für diese eine eigene
Java-Datei

Move Member Type to Extraktion einer Unterklasse in eine eigene Datei


New File

Push Down Verschiebt eine Menge von Methoden und/oder Feldern zu


einer Unterklasse dieser Klasse

54
eclipse_v01.book Seite 55 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

Tabelle 2.2: Refactoring-Maßnahmen in Eclipse (Forts.)

Refactoring-Maßnahme Durchgeführte Aktion

Pull Up Verschiebt eine Methode oder ein Feld von einer Unter-
klasse in die Oberklasse

Extract Interface Erzeugt ein neues Interface und entzieht der aktuellen
Klasse dafür alle Instanzmethodensignaturen. Die alte
Klasse implementiert ab jetzt das Interface als eine (von
theoretisch mehreren) möglichen Implementierungen.

Generalize Type Erlaubt es, den aktuellen Typ durch einen der geerbten
Supertypen zu ersetzen, soweit dies möglich ist

Use Supertype Where Ersetzt das Auftreten einer Klasse im Sourcecode durch
Possible dessen Superklasse, wo immer dieser Austausch syntak-
tisch möglich ist

Infer Generic Type Ersetzt Raw types durch generische Typen ab Java 5
Arguments

Inline Gegenteil des Extrahierens einer Methode. Fügt den Source-


code dieser Methode als Inlinecode an jeder aufrufenden
Stelle ein.

Extract Method Extrahiert eine Menge von Sourcecode-Zeilen in eine


eigene Methode (siehe Beispiel oben)

Extract Local Variable Ersetzt die selektierte Variable durch eine andere mit
anderem Namen unter Zuweisung der ersten an die zweite

Extract Constant Überführt eine Instanzvariable in eine Konstante. Wenn die


Transformation durchführbar ist, werden entsprechende
Referenzen auf die Variable korrigiert.

Encapsulate Field Kapselt eine Instanz- oder Klassenvariable über Accessor-


Methoden (getter und setter)

Introduce Parameter Auf einer selektierten Expression aufgerufen, wird der


umgebenden Methode ein neuer Parameter hinzugefügt
und alle Methodenaufrufe werden entsprechend korrigiert.

Generalize type Nach Selektion einer Variablen, eines Parameters, Felds


oder eines Methodenrückgabetyps zeigt ein Wizard die
Supertyphierarchie des Elements. Nach Auswahl eines
Typs wird die Deklaration des Elements angepasst.

Introduce factory Nach Selektion eines Konstruktors und Wahl dieser Aktion
wird eine statische Fabrikmethode für den nun privaten
Konstruktor erzeugt. Alle Aufrufe des Konstruktors werden
ersetzt.

55
eclipse_v01.book Seite 56 Montag, 30. Januar 2006 12:02 12

Refactoring mit Eclipse

Die durch Eclipse abgedeckten Refactoring-Mechanismen sind sicher noch nicht vollstän-
dig. Andere IDEs bieten unter Umständen noch mehr Möglichkeiten. Betrachtet man je-
doch die Geschwindigkeit, in der die Fortentwicklung bei Eclipse in Bezug auf den Editor
betrieben wird, scheint dieses allerdings nur eine Frage der Zeit zu sein.
Nicht alle der oben genannten Refactoring-Maßnahmen stehen dem Benutzer zu jedem
Zeitpunkt zur Verfügung. Dies hängt vom Kontext ab (welcher Sourcecode ist markiert, wo
steht der Cursor, welches Element wurde angeklickt etc.).

2.5.1 Überschreiben/Implementieren von Methoden


Beim Überschreiben von Methoden der Basisklasse und beim Implementieren der Metho-
den eines Interface werden wir ebenfalls unterstützt. Im SOURCE-Menü, erreichbar über die
rechte Maustaste im Editor, gibt es hierfür den Menüpunkt OVERRIDE/IMPLEMENT METHODS,
der auch im Editor über die rechte Maustaste erreicht werden kann. Da jede Klasse mindes-
tens von java.lang.Object als Root-Klasse erbt, können zumindest Methoden wie to-
String oder equals in der ExpressionAnalyzer-Klasse überschrieben werden. Sollte
ein weiteres Interface der Klasse hinzugefügt werden, das die Implementierung einzelner
Methoden verlangt, kann ähnlich verfahren werden.
Wird ein Interface manuell der Klasse über implements im Klassenkopf hinzugefügt, bie-
tet Eclipse an, jene zu implementierenden Methoden, die jetzt zum Syntaxfehler führen, au-
tomatisch hinzuzufügen oder die Klasse selbst als abstrakt zu deklarieren.
Während beim Interface oder einer abstrakten Superklasse öfter Methoden in der eigenen
Klasse selbst implementiert werden müssen, um dem Interface zu genügen, muss z.B. die
toString-Methode nicht zwingend implementiert werden. Wir machen es der Übung hal-
ber trotzdem.

Abb. 2.27: Override/Implement Methods

56
eclipse_v01.book Seite 57 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

Über die rechte Maustaste SOURCE | OVERRIDE/IMPLEMENT METHODS bekommt man in einem
Dialog alle Methoden angezeigt (siehe Abb. 2.27), die überschrieben werden können. Da-
bei werden alle Superklassen der Hierarchie samt implementierten Interfaces aufgelistet.
Auch hier (wie in bereits vorangegangenen Beispielen) lassen wir uns den Methodenkom-
mentar mitgenerieren und schauen uns das Ergebnis an.
Auch in diesem Dialog sei auf die Wechselmöglichkeit in die CODE TEMPLATES verwiesen,
die es erlauben, die so generierten Methoden individuell anzupassen. Dies gilt speziell für
die generierten Javadoc-Kommentare.
Eclipse erzeugt hier nach Klick auf OK im Standardfall folgende Statements:

/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
// TODO Auto-generated method stub
return super.toString();
}

Im automatisch erzeugten Source finden wir sowohl ein @see-Tag für die toString()-
Methode der Superklasse als auch die Java-5-Annotation für eine überschriebene Methode
(@Override) vor.
Das erzeugte return-Statement in der toString-Methode ersetzen wir jetzt beispielhaft
durch die folgende Zeile:

return "{ExpAnalyzer:"+String.valueOf(this.regexpCompiler)+"}";

Welche Methoden überschrieben wurden, zeigt Eclipse an einem kleinen


nach oben zeigenden Dreieck neben der Methodensignatur im Source-
code. Entsprechende Hinweissymbole finden sich zudem in der Outline-
view und der Vererbungshierarchieanzeige.

2.5.2 Auslagern von Zeichenketten/Mehrsprachigkeit


Für unser Beispiel ist es zwar nicht so relevant, aber wir könnten sehr einfach die Fehler-
meldung, die in unserer BusinessException weitergereicht wird, im Sourcecode auslagern.
Dies ist ein erster Schritt zur Implementierung einer Mehrsprachigkeit, die wir damit um-
setzen können, wenn wir entsprechende Resource-Bundle-Klassen geschrieben haben.
Wir klicken mit der rechten Maustaste auf den Klassennamen im Package Explorer und
wählen im Menü SOURCE | EXTERNALIZE STRINGS.

57
eclipse_v01.book Seite 58 Montag, 30. Januar 2006 12:02 12

Refactoring mit Eclipse

Eclipse durchsucht daraufhin die Klasse nach Zeichenketten und zeigt jede einzelne von ih-
nen als zu externalisieren an. Dabei würde sie durch eine entsprechende Variable ersetzt,
um diese in eine eigene Datei zu verlagern.
Da die Auslagerung innerhalb der toString-Methode nur begrenzten Nutzen hat, wählen
wir die beiden unteren Einträge und lassen den Ausdruck „Error in pattern...“ stehen (siehe
Abb. 2.28).

Abb. 2.28: Externalize Strings – Dialog

Eclipse zeigt zu jedem Key auch die Stelle im Sourcecode, wo dieser vorkommt. Wir erset-
zen die Key-Namen der ersten beiden Values durch ERROR_PATTERN_COMPILE1 bzw.
ERROR_PATTERN_COMPILE2. Die Namen dürfen hierbei keine Leerzeichen aufwei-
sen. Anschließend deaktivieren wir die Übersetzung für die beiden unteren Zeichenketten
durch Klick auf das Häkchensymbol und klicken auf CONFIGURE.
Dann vergeben wir als Property-Dateinamen language.properties und lassen die
weiteren Einstellungen unangetastet. Anschließend klicken wir, zurück im EXTERNALIZE
STRINGS Dialog angekommen, zweimal auf NEXT.
Ist bereits ein Accessor, also ein Default-Objekt, vorhanden, das z.B. Übersetzungen vor-
nimmt, werden die Einstellungen entsprechend angepasst. Eclipse informiert nach Klick
auf NEXT, dass das Property-File neu angelegt wird, und zeigt in einer Vorschau nochmals
die durchzuführenden Ergänzungen.

58
eclipse_v01.book Seite 59 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

Nach Klick auf FINISH hat Eclipse die Fehlermeldung in eine eigene Property-Datei ausge-
lagert und eine neue Message-Klasse erzeugt, über die die Fehlermeldungen eingelesen
werden können.
Die Einbindung im Sourcecode sieht nunmehr wie folgt aus:

throw new BusinessException(Messages.getString(


"ExpressionAnalyzer.ERROR_PATTERN_COMPILE1") //$NON-NLS-1$
+ Messages.getString(
"ExpressionAnalyzer.ERROR_PATTERN_COMPILE2") + patternString);
//$NON-NLS-1$

2.6 Erstellung eines JUnit-Tests


Unit-Tests sind ein probates Mittel für kleine Tests (vor allem Klassen). Für einen tieferen
Einblick in JUnit sei auf die JUnit-Webseite1 verwiesen.
JUnit-Tests vereinfachen vor allem den Test von Schnittstellen zu Klassen, wobei unter-
schiedliche Konstellationen von Inputdaten in Form von Parametern gegen die korrekten
Rückgabewerte bzw. das erwartete Auftreten von Exceptions getestet werden. Mit JUnit
können auch sehr komplexe Testszenarien erzeugt werden.
Ein Vorteil von JUnit ist die ständige Reproduzierbarkeit von Tests per Mausklick. Aber
auch nach der Implementierung macht das Aufsetzen eines Tests Sinn.
In unserem Fall ist ein Fehler offensichtlich. Die Methode getAllOccurrencesOfPat-
tern gibt immer null zurück (wenn dies von Ihnen nicht schon korrigiert worden ist) und
diesen Fehler wollen wir jetzt „entdecken“ und debuggen.
Man unterscheidet zwei Varianten von JUnit-Klassen: Test Cases und Test Suites. Während
der Test Case einen bzw. mehrere Testfälle durchspielt, ist die Test Suite eine darüber lie-
gende Klammer, die die Test Cases strukturiert.

Ab einer gewissen Phase und unter entsprechendem Zeitdruck des Pro-


jekts ist man immer gerne versucht, Test Cases nicht zu schreiben oder
geschriebene nicht auszuführen, weil sie eh nicht mehr funktionieren. Um
den Testvorgang zu automatisieren, werden die Test Cases als Target in

das Ant-Build-Skript, das für das Deployment verwendet wird, gehängt.

Wir erstellen einen JUnit-Test-Case. Hierfür klicken wir mit der rechten Maustaste auf die
Klasse ExpressionAnalyzer und wählen den Menüpunkt NEW | JUNIT TESTCASE.
Da JUnit selbst ein in Java implementiertes Framework ist, fragt Eclipse den Entwickler, ob
es sinnvoll wäre, das notwendige junit.jar mit in den Build-Pfad aufzunehmen. Das können
wir mit YES bestätigen.

1. http://www.junit.org

59
eclipse_v01.book Seite 60 Montag, 30. Januar 2006 12:02 12

Erstellung eines JUnit-Tests

Eclipse sollte jetzt den Namen der zu testenden Klasse kennen. Der Name der Testklasse ist
der der Originalklasse, ergänzt um das Postfix „Test“. Wir tragen als Package-Namen
test ein und klicken die Checkbox SETUP an. Wenn der Test Case ohne Test Suite laufen
soll, müssen wir die Checkbox zur Erzeugung von main-Methode und TESTRUNNER aktivie-
ren. Für unseren Fall brauchen wir das allerdings nicht, da Eclipse mit JUnit-Test-Cases
auch so zurechtkommt (siehe Abb. 2.29).

Abb. 2.29: JUnit-Test-Case – Generierung

Wir klicken auf NEXT und wählen die Checkbox neben der ExpressionAnalyzer-Klasse
und die zur Generierung von Tasks an.
In der Baumansicht entscheiden Sie sich, welche Methoden Sie testen möchten. Darunter
können auch Methoden der Superklassen fallen, wenn diese von Relevanz sind. Die Check-
box mit den Tasks erzeugt für jede Methode eine solche Task, was hilfreich sein kann, wenn
wir sehr viele Methoden testen und den Überblick nicht verlieren möchten. Mit FINISH wird
die neue Testklasse erzeugt.
Wir ergänzen den nachfolgenden Sourcecode. Der Einfachheit halber wird das Testen der
toString-Methode unterlassen und die Importstatements sind entsprechend selbstständig
zu ergänzen (z.B. über Ÿ-Leertaste hinter dem jeweiligen Klassennamen).

60
eclipse_v01.book Seite 61 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

[Importstatements]
public class ExpressionAnalyzerTest extends TestCase {
/** zu testender Content innerhalb des Tests */
public static final String CONTENT = "Willkommen im "
+ "Eclipse-3.1-Buch!";
/**
* regular expression-Pattern, das auf Kleinbuchstaben
* zwischen a und e reagiert
*/
public static final String PATTERN ="[a-e]";
/** Expression-Analyzerreferenz */
private ExpressionAnalyzer analyzer = null;
protected void setUp() throws Exception {
analyzer = new ExpressionAnalyzer();
analyzer.setRegexpCompiler(new Perl5Compiler());
}
public void testGetAllOccurrencesOfPattern() throws
BusinessException {
List <String> result =
analyzer.getAllOccurrencesOfPattern
(CONTENT, PATTERN);
Assert.assertNotNull("Ergebnisobjekt ist leer",
result);
Assert.assertEquals("Anzahl der Ergebnisse ist"
+ " falsch",result.size(),4);
Assert.assertTrue("Inhalt des Ergebnisses ist"
+ " falsch",result.get(0).equals("e"));
}
public void testSetRegexpCompiler() {
analyzer.setRegexpCompiler(null);
try {
analyzer.getAllOccurrencesOfPattern
(CONTENT,PATTERN);
throw new AssertionFailedError("Business"
+ "Exception did not occur.");
} catch (BusinessException e) {
// erwarteter Fehler
}
}
}

Über so genannte Assert-Aufrufe definiert man Annahmen über den Zustand und den In-
halt von Objekten zur Laufzeit. Treffen diese Annahmen nicht zu, ist entweder der Testfall
falsch definiert oder das zu testende Objekt arbeitet falsch. Hier haben wir sehr einfache
Assertions verwendet.

61
eclipse_v01.book Seite 62 Montag, 30. Januar 2006 12:02 12

Debuggen in Eclipse

Die setUp-Methode initialisiert den Test. Für jede aufgerufene Testmethode (alle beginnen
mit dem Präfix test) wird die Methode setUp einmal aufgerufen. So bekommen wir je-
weils eine neue ExpressionAnalyzer-Instanz.
In der testGetAllOccurrences-Methode rufen wir den Analyzer mit dem Inhalt
„Willkommen im Eclipse-3.1-Buch!“ auf. Das Suchmuster lautet a-e. Wir su-
chen also alle Kleinbuchstaben, die a, b, c, d oder e lauten. Davon gibt es in diesem Text
vier.
Wir speichern jetzt die Klasse mit Ÿ+S, starten diese über die Toolbar oder das Menü
RUN | RUN AS | JUNIT TEST und erwarten das Ergebnis des Tests.
JUnit liefert Fehler, einen Error und einen Failure. Der Error ist derjenige, den wir bereits
erwartet haben. Der zweite ist eine NullpointerException, die vorauszuahnen war,
nachdem der Analyzer ohne Zuordnung eines Compilers gestartet worden ist (siehe Abb.
2.30).

Abb. 2.30: Junit-Test-Case – Ergebnisse

Das Ergebnis entspricht voll und ganz den Erwartungen, aber es soll mal so getan werden,
als wäre dies nicht der Fall. Wir werden uns jetzt mit dem Debugging beschäftigen, um den
ersten der beiden Bugs zu „finden“ und zu „fixen“. ;-)

2.7 Debuggen in Eclipse

2.7.1 Breakpoints
Wir wechseln in die fehlgeschlagene testGetAllOccurrencesOfPattern-Methode
und doppelklicken auf den schraffierten Bereich links vom Sourcecode in der ersten
Sourcecode-Zeile der Methode, um einen Breakpoint zu setzen.
Grundsätzlich können Breakpoints nur sinnvoll gesetzt werden, wenn Sourcecode und
Classfile übereinstimmen. Dazu kommt, dass manche Stellen einer Klasse (wie Kommen-

62
eclipse_v01.book Seite 63 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

tare oder Importstatements) während eines Debug-Laufs nicht angesprungen werden kön-
nen, da dies technisch nicht abgebildet werden kann.
Breakpoints, die mit einem kleinen Häkchen versehen sind, sind solche, die erfolgreich im
Classfile hinterlegt und von der virtuellen Maschine angesprungen werden können. Zum
Debug-Zeitpunkt wird daher der gesetzte Breakpoint vermutlich mit einem Häkchen ver-
sehen sein, wenn der Classloader der VM die Klasse geladen hat.
Über die Breakpoint-Properties, die über die rechte Maustaste auf einem Breakpoint er-
reicht werden können, können wir die Aktivierung des Breakpoints mit zusätzlichen Eigen-
schaften einschränken (siehe Abb. 2.31) und beispielsweise vorgeben, wie viele Male der
Breakpoint übersprungen wird, bevor er greifen soll. Bei Breakpoints innerhalb von Schlei-
fenkonstrukten ist das von Vorteil, aber auch die Vorgabe einer expliziten zusätzlichen Be-
dingung kann in den Properties erfasst werden. Für unsere Zwecke können wir z.B. erfas-
sen, dass das Result leer sein soll.

Abb. 2.31: Breakpoint-Properties-Dialog

Zwei interessante Properties sind in Bezug auf größere Projekte oder den Einsatz von
Multithreading-Anwendungen wie Application Server oder Webserver erwähnenswert.
Über den Baumast FILTERING kann eingeschränkt werden, in welchen Threads der Break-
point aktiv werden soll. Da erst zur Laufzeit eine Menge von Threads entsteht, kann erst
dann entschieden werden, welche Threads gefiltert werden sollen.
Eine andere wichtige Eigenschaft ist die SUSPEND POLICY. Unangenehmerweise kann es vor-
kommen, dass genau ein Thread angehalten wird und die anderen in Dead Locks verfallen

63
eclipse_v01.book Seite 64 Montag, 30. Januar 2006 12:02 12

Debuggen in Eclipse

oder sich ungewohnt verhalten. Dadurch kann der Test beeinträchtigt werden. In diesem
Fall wird mit SUSPEND VM die virtuelle Maschine angehalten, um einen konsistenten Zu-
stand zu gewährleisten.
Wir verlassen den Dialog mit CANCEL, um bei einem „Standard“-Breakpoint zu bleiben.

2.7.2 Aufsetzen einer Launch configuration


Für jedes Starten einer virtuellen Maschine – egal ob im Debug- oder Run-Modus – können
wir eine eigene Ausgangsumgebung schaffen, einen eigenen Classpath definieren, eine vir-
tuelle Maschine auswählen usw. So können wir den Build bzw. den Compile-Lauf vollkom-
men anders und mit einer anderen VM durchführen als mit der, mit der wir testen wollen.
Dieses Maximum an Flexibilität hat allerdings den Nachteil, dass wir aufpassen müssen,
was wir wo einstellen.
Über den Debug-Button in der Toolbar und den Menüpunkt DEBUG (oder über das Menü
WINDOW | DEBUG...) wollen wir uns die Session jetzt individuell definieren. Bereits erzeugte
Sessions (wie die vom Schnelltest vorhin bei JUnit) stehen bereits im Menü.
Im Schnelldurchlauf wollen wir uns diese Konfigurationen ansehen. Im linken Fenster kön-
nen dem Menübaum neue Launch-Konfigurationen hinzugefügt und alte entfernt werden.

Abb. 2.32: Launch configuration für einen JUnit-Test-Debugdurchlauf

64
eclipse_v01.book Seite 65 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

Denjenigen, die zu den Programmierern gehören, die gern mehrere Stages


(Entwicklungsumgebung, Integrationstest, Kundentest, Produktion) in einer
Workbench haben, kann es schnell passieren, dass sich die gleichen Klas-
sen in unterschiedlichen Projekten wiederfinden. Besonders dann muss bei

der Konfiguration sehr aufgepasst werden, weil eine Klasse n-fach vorkom-
men kann und durch Löschung von Projekten Konfigurationsleichen
zurückgelassen werden.

Die verfügbaren Aktionen in den jeweiligen Registern der Launch configuration, die wir
hier erstellen, richten sich nach dem jeweiligen Debug-Typus. Wir wollen uns auf JUnit und
Java Application beschränken.
Bei JUNIT-Tests kann der Test als Einzeltest definiert werden oder es können alle Tests in ei-
nem Projekt bzw. Package oder Source-Verzeichnis gestartet werden (siehe Radiobutton).
Mittels des Punkts REMOTE JAVA APPLICATION können wir uns an entfernte, virtuelle Maschi-
nen „attachen“. Das heißt, wir sind potenziell in der Lage, auch produktive Java-Applika-
tionen, so diese im Debug-Modus gestartet wurden, über eine TCP/IP-Verbindung und ei-
nen auf dem Server geöffneten Socket zu debuggen. Die dafür notwendigen Startparameter
lernen wir im Debug-Abschnitt kennen. Grundsätzlich heißt es vor Remote Debugging kei-
ne Angst zu haben, denn Eclipse nutzt diese Funktion faktisch bei jedem Debug-Durchlauf
selbst, nur dass hierbei beide Applikationen auf ein und demselben Rechner laufen.
Wichtig ist: Wer Remote Debugging einsetzen möchte, muss darauf achten, entsprechende
VMs auf beiden Seiten (am besten gleiche Version des gleichen Herstellers) einzusetzen
und die zu debuggende Applikation auch im Debug-Modus zu starten bzw. beim Compile
mit Debug-Informationen zu durchsetzen. Weitere essenzielle Voraussetzung: Die zu de-
buggenden Sourcecode-Stellen müssen auf beiden Seiten den gleichen Versionsstand auf-
weisen, um vernünftig debuggen zu können.
Bei einer JAVA APPLICATION findet sich im vordersten Register die Benennung von Projekt
und zu startender Klasse.
Es kann automatisch in der Mainklasse gestoppt werden, selbst wenn kein Sourcecode exis-
tiert. Wenn beispielsweise ein Webserver debuggt werden soll, kann es Sinn machen, dass
die main-Methode aus einem „external jar“ kommt, also aus einer Library, für die kein
Sourcecode vorhanden ist.

Arguments
Im Register ARGUMENTS werden die Parameter eingetragen, die an das Testprogramm über-
geben werden sollen. Dabei wird zwischen Parametern an die main-Methode und weiteren
Parametern für die startende VM unterschieden. Dieses erlaubt es, z.B. weitere Java-
Umgebungsvariablen zu übergeben (-Dproperty=<wert>) oder den zur Verfügung
stehenden Prozessspeicher der Anwendung zu vergrößern (-Xmx512m). Eclipse erlaubt
dabei jetzt auch die Verwendung und Übergabe von definierten Variablenwerten wie Pro-
jektname, Projektpfad, Workspace-Lokation etc.

65
eclipse_v01.book Seite 66 Montag, 30. Januar 2006 12:02 12

Debuggen in Eclipse

Classpath
Hier wird der Klassenpfad zusammengestellt und die Libraries werden gewählt, die dem
Klassenpfad hinzugefügt oder von ihm entfernt werden sollen. Hierbei ist zu beachten, dass
sich der Klassenpfad um jene Libraries vergrößert, die aus fremden Projekten, mit denen
das Projekt verbunden ist, kommen. Außerdem spielt die Reihenfolge der Libraries für den
Classloader eine Rolle.
Eclipse lässt es dem Benutzer dabei auch offen, über den Button ADVANCED weitere Lib-
raries oder Library-Sets (eine Menge zusammengehöriger Libraries, z.B. wie Struts und
Jakarta Commons Libraries, die nur zusammen lauffähig sind) einzubinden. Über den
Button RESTORE DEFAULT ENTRIES lassen sich die ursprünglichen Originaleinstellungen je-
derzeit wieder aufrufen.
In der Classpath-Ansicht wird zwischen den USER ENTRIES und den BOOTSTRAP ENTRIES un-
terschieden. Grundsätzlich entspricht der Classpath der Kombination von Libraries und
Projekten, die auch zum Build-Zeitpunkt verwendet wurden, um die Classfiles zu erzeugen.
Im Register USER ENTRIES wird der Classpath angegeben, den wir der VM zum Start einer
Klasse mitgeben (was wir per Hand mit dem Argument -cp der Java-VM übergeben).
Hierbei erlaubt Eclipse das Hinzufügen weiterer Projekte oder externer Jars. Im Baum
BOOTSTRAP ENTRIES wird die Menge aller Klassen festgelegt , die zum Startzeitpunkt der vir-
tuellen Maschine (nicht des eigentlichen Programms) herangezogen werden. Diese ergeben
sich im Standardfall aus den Libraries, die der JRE-Hersteller mitliefert.

Wenn Probleme mit dem Klassenpfad auftauchen und bestimmte Klassen


oder Ressource-Dateien nicht gefunden werden, ist zunächst zu klären,
ob das File bzw. Verzeichnis im Classpath ist, und danach zu überlegen,
durch welchen Classloader (besonders wichtig bei Application Servern

oder Servlet Containern wie Tomcat) es geladen werden sollte. Erscheint


der Fehler „java.lang.Object kann nicht gefunden werden“, ist die Konfigu-
ration des JRE für das Projekt defekt oder fehlerhaft.Versuchen Sie durch
Korrekturen des Classpath und ein Clean auf dem Projekt den Compile zu
korrigieren.

JRE
In diesem Register wird das Java Runtime Environment gewählt, das beim Starten der An-
wendung genutzt werden soll. Weitere JREs werden über das Menü WINDOW | PREFERENCES
oder den Button INSTALLED JRES eingerichtet und können hier wahlweise das Projekt-JRE
ersetzen. Zusätzlich lässt sich nach der Auswahl definieren, ob mit dem Standard javaw
Executable gestartet werden soll oder nicht.

Source
Im Register SOURCE, das bei reinen Java-Applikationen zur Konfiguration zur Verfügung
steht, können wir selbst vorgeben, ob wir noch irgendwo zu den eingesetzten Libraries für
den Classpath weiteren Sourcecode besitzen. So lässt sich eine Gesamtapplikation aus

66
eclipse_v01.book Seite 67 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

mehreren Java-Projekten zusammenbauen, die unabhängig kompiliert werden und dennoch


zum Start der Applikation gemeinsam vorhanden sein sollen. Dies kann sich zum Debug-
Zeitpunkt als sinnvoll erweisen.

Arbeiten Sie in einem großen Multiprojektumfeld, ist es unerlässlich, die


dort verwendeten Teilprojekte zu strukturieren. Hierbei hilft Eclipse, indem
man mehrere Projekte im Package Explorer zu so genannten Working
Sets gruppieren, einblenden und ausblenden lassen kann. Diese Funktio-

nen können auch helfen, sinnvolle Launch-Konfigurationen zu erstellen


und Programme aus Einzelkomponenten zusammenzusetzen.

Die Checkbox SEARCH FOR DUPLICATE SOURCE FILES ON THE PATH kann einem manchen Ärger
mit Sourcen ersparen, besonders, wenn unterschiedliche Source-Versionen existieren und
Eclipse nicht mehr weiß, welchen der generierten Classfiles es eigentlich entsprechen soll.

Environment
Über das Register ENVIRONMENT können in Eclipse für die zu startende Klasse weitere Um-
gebungsvariablen definiert werden. Diese ergänzen oder ersetzen dann je nach Wunsch die
„nativen“ Umgebungsvariablen des JRE.

Common
Wenn mühselig eine Launch Configuration aufgesetzt worden ist, ist es ärgerlich, dass
Eclipse bei jeder neuen Startklasse mit einer neuen Launch Configuration aufwartet. In die-
sem Fall kann die erarbeitete Variante in einer Datei gespeichert und wiederverwendet
(shared) werden und es lässt sich gleichzeitig vorgeben, welche Perspektive zum Startzeit-
punkt dieser Launch Configuration geöffnet werden soll. Es empfiehlt sich hier, die ent-
sprechende Checkbox aktiviert zu belassen.

Launch Perspectives
Klicken wir auf den Konfigurationstypen selbst (also z.B. JAVA APPLICATION oder JUNIT im
Konfigurationsbaum), können wir die Default-Einstellungen für den jeweiligen Typ vorge-
ben.
Grundsätzlich erscheint es sinnvoll, die Debug-Perspektive zu öffnen, wenn wir debuggen
wollen. Wenn wir aber eine eigene Debug-Perspektive zusammengestellt und gespeichert
haben, sollten wir diese beim Start aktivieren. Gleiches gilt für die Perspektive beim nor-
malen Start der Anwendung. NONE lässt die bereits laufende Perspektive dabei unberührt.

2.7.3 Die Debug-Perspektive im Überblick


Verschaffen wir uns einen Schnellüberblick über die Debug-Perspektive, indem wir den
JUnit-Test nochmals mit aktiviertem Breakpoint im Debug-Modus starten. Klicken Sie
dafür auf Debug im Dialog der Launch Configurations oder im Menü auf RUN DEBUG
HISTORY | EXPRESSIONANALYZERTEST. Unter Umständen fragt Eclipse dabei noch einmal, ob

67
eclipse_v01.book Seite 68 Montag, 30. Januar 2006 12:02 12

Debuggen in Eclipse

es die Perspektive wechseln und in die Debug-Ansicht wechseln darf. Wir klicken die
Checkbox REMEMBER MY DECISION an und wählen den YES-Button.
Die unterschiedlichen Views stellen die wesentlichen Debug-Informationen zusammen.
Drei alte Bekannte begegnen uns dabei: der Editor (in verkleinerter Form), die Outline
View und die Tasks.
Die Console View ist für Outputs eines Programms gedacht, die an System.out oder
System.err geschickt werden (farblich unterscheidbar) und die nicht anderweitig um-
gelenkt wurden (z.B. in eine Datei).

Abb. 2.33: Debug-Perspektive im Überblick

2.7.4 Grundfunktionen des Debuggens


Es ist bereits durch die Markierung der Sourcecode-Zeile zu sehen, dass der Debugger zum
Stehen gekommen ist. Der kleine blaue Pfeil zeigt dabei auch auf die Zeile, in der der De-
bugger angehalten hat. Im Sourcecode verankerte Breakpoints sind durch den kleinen run-
den Kreis und ein Häkchen daneben gekennzeichnet.
Da davon ausgegangen wird, dass bereits Erfahrungen mit dem Debuggen in den unter-
schiedlichsten IDEs vorliegen, wird im Folgenden lediglich eine kurze Liste der wichtigs-
ten Tastaturaktionen vorgestellt. Die beschriebenen Funktionen lassen sich auch über die

68
eclipse_v01.book Seite 69 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

rechte Maustaste auf der aktuellen Methode im Stack, die Toolbar des Debugviews oder
über das RUN-Menü ausführen (siehe Tabelle 2.3).

Tabelle 2.3: Primäre Debug-Funktionen

Aktionsname Durchgeführte Funktion

Open Declaration Type Öffnen des Sourcecode-Abschnitts, in dem das aktuell mar-
kierte Objekt deklariert ist

Open Declaration Type Öffnen der Vererbungshierarchieanzeige des aktuellen


Hierarchy Objekts

Resume – Ð Fortsetzen des Programms bis zum nächsten Breakpoint

Suspend Anhalten des Programms (Threads), während es läuft

Terminate Beenden des Programms (normalerweise gleichbedeutend


mit dem Beenden aller Threads)

Step Into – Í Debuggen in den Methodenaufruf hinein

Step Over – Î Zur nächsten Sourcecode-Zeile in dieser Methode weiter-


gehen

Step Return – Ï Den Thread bis zum Erreichen des Verlassens der aktuellen
Methode (Return) laufen lassen

Terminate and Remove Wie Terminate mit Löschen des toten Eintrags der VM aus der
Debug View

Relaunch Neustart dieser Launch Configuration/dieses Programms

Terminate All Beenden aller in Eclipse laufenden virtuellen Maschinen

Disconnect Löst eine Verbindung zu einem fernen Prozess. Diese Funk-


tion wird allgemein bei Remotedebugging von Java-Anwen-
dungen außerhalb der Eclipse-Umgebung verwendet.

Use Step Filter / Mit Step Filtering lassen sich einzelne Klassen oder Packages
Edit Step Filter / beim Debug-Durchlauf ausblenden. Diese Funktion kann ein-
Filter Type / und ausgeschaltet werden. Filter Type/Package beziehen den
Filter Package aktuellen Typ und das aktuelle Package in den Filter mit ein.

2.7.5 Allgemeine Hinweise zum Debuggen in Eclipse


Im Bereich des Java Debuggens hat sich seit den ersten JDKs viel getan. IBM schaffte vor
Jahren mit dem damaligen Visual Age für Java-Produkte etwas ganz Erstaunliches. Da IBM
eine eigene virtuelle Maschine mit der IDE auslieferte, war es dem Entwickler möglich,
nicht nur einzelne Werte von Variablen zur Laufzeit zu ändern, sondern sogar große Teile
des Sourcecodes und der Klassenstrukturen zum Debug-Zeitpunkt dauerhaft zu ändern. Wo
andere Produkte einen Rebuild des Sourcecodes brauchten, konnte der Entwickler hier den

69
eclipse_v01.book Seite 70 Montag, 30. Januar 2006 12:02 12

Debuggen in Eclipse

Fehler, den er erkannte, auch gleichzeitig fixen und musste sich nicht in mühevoller Arbeit
nach dem Build wieder bis zum Breakpoint vortasten, um seine Änderungen auf Korrekt-
heit zu prüfen.
Leider hatten diese Funktionen den Nachteil, dass IBM stets eine eigene VM entwickeln
musste – Inkompatibilitäten zu anderen VMs waren daher nicht ausgeschlossen.
Mit der früheren Java Plattform Debugging Architecture1 (JPDA) brach ein ganz neues
Zeitalter für virtuelle Maschinen an. Man definierte einen festen Einsprungpunkt in eine
laufende VM, um Entwicklungsumgebungen und virtuellen Maschinen eine definierte
Schnittstelle zu schaffen. Heute trägt diese Plattformfunktionalität auch den Namen JVMTI
(Java Virtual Machine Tool Interface), eine Zusammenfassung der Funktionalitäten JVMPI
und JVMDI (Profiling und Debug Interface).
So kann heute über Parameter ein Java-Programm bereits auf einen Debug-Lauf vorbereitet
und über einen geöffneten Port mit dem laufenden Programm Kontakt aufgenommen wer-
den (remote debugging). Dies wird in Eclipse dadurch realisiert, dass Eclipse per Zufall ei-
nen TCP/IP-Port ermittelt und diesen einer virtuellen Maschineninstanz mitgibt, um sich
selbst (als weiterer Java-Prozess) mit diesem Port zu verbinden. Deshalb läuft Eclipse kom-
plett autark von den Java-Programmen, die über Eclipse gestartet und debuggt werden. Es
kann sogar passieren, dass Eclipse abstürzt und das Programm unbeeindruckt weiterläuft.
Aus dieser klaren Trennung zwischen den zwei Prozessen (Eclipse und dem Debug-Pro-
zess) ergeben sich aber auch Konsequenzen, und zwar durch den Einsatz unterschiedlicher
virtueller Maschinen. So kann es passieren, dass viele Debug-Abläufe in Abstürzen enden,
während der gleiche Sourcecode in einer anderen VM vollkommen unproblematisch läuft.
Eclipse muss über die JVMTI-Schnittstelle den Quellcode und die Daten aus den Classfiles
in der Fremd-VM synchronisieren. Wenn die Classfiles ohne Debug-Informationen erstellt
worden sind, erschwert sich dieser Prozess für Eclipse. Noch schlimmer ist, wenn Source-
code- und Classfiles komplett unterschiedlich sind. Dann beginnt Eclipse im Sourcecode zu
springen, hält sich nicht mehr an gesetzte Breakpoints und der Überblick geht verloren.
Sollten also einige der nachfolgenden Erläuterungen nicht funktionieren, zum Absturz füh-
ren oder gar nicht erst verfügbar sein, liegt dies zumeist an der Auswahl der VMs, in der
Eclipse oder ein Programm läuft.
Da den alten VMs, wie Version 1.1.8 oder 1.2.2, aber auch 1.3.1, eine Reihe von Funktio-
nen fehlen, startet man die Debugsession häufig mit einer moderneren. Zu beachten ist hier-
bei die Zielplattform [VM-Version, JRE-Version, Ziel-VM-Hersteller, VM-Typ (Client-/
Server-VM), Optimierungsverhalten (Hotspot, Debug ...) und Classfile-Version]. Ein Trick
kann sein, eine 1.4-VM mit einer JRE-Library von 1.3.x zu kombinieren, was sich aller-
dings auch als kompliziert herausstellen kann. Es könnte auch beispielsweise ein 1.3.1er in
den Projekt-Properties als Build-VM bestimmt werden und in der Debug-Launch-Configu-
ration dann eine 1.4.2er zum Debuggen benutzt werden. Es besteht also eine große Freiheit
der Zusammenstellung von Eclipse-VM, Build-VM und Debug-VM und es existieren die

1. http://java.sun.com/products/jpda/

70
eclipse_v01.book Seite 71 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

verschiedensten Variationsmöglichkeiten, die dies extrem flexibel, aber auch anfällig für
Fehlkonfigurationen macht.
Grundsätzlich nicht empfehlenswert ist das Mischen von Java 1.x VMs mit Java 5.0/6.0
Compile-Ständen oder VMs. Auch die für Java 7 angekündigten Bytecode-Erweiterungen
zeigen auf, dass man in den unterschiedlichen Majorrelease-Versionen von Java besser
nicht mischen sollte.

2.7.6 Debug View


In der Degub View (links oben in der Debug-Perspektive) erscheinen alle laufenden Run/
Debug-Sessions sowie deren Namen und Typ. Tote Sessions werden als terminiert ange-
zeigt. Des Weiteren wird die Startklasse und die Port-Adresse für die Kommunikation zwi-
schen Eclipse-VM und Debug-VM angezeigt (siehe Abb. 2.34).

Abb. 2.34: Threadstack in der Debug View

Darunter befindet sich die Liste der parallel laufenden Threads in der VM und deren Typ
(SYSTEM THREAD oder nicht) sowie deren Name und Status (RUNNING, SUSPENDED ...). Wei-
tere System-Threads können über den Zusatzmenüpunkt in der Toolbar SHOW SYSTEM
THREADS eingeblendet werden.
Der Main-Thread wird am Breakpoint angehalten, der Stack ist zu sehen, wobei die Ver-
schachtelung wie immer im Stack von unten nach oben gelesen tiefer geht. Dort, wo der
Sourcecode zu dem entsprechenden Frame im Stack zugeordnet werden kann, gibt es eine
Zeilenangabe (z.B. Line: 31). Dort, wo er nicht vorhanden ist oder es sich um eine native
Methode handelt, die nicht in Java implementiert ist, finden wir ein NOT AVAILABLE. Am
Ende steht ein Eintrag für die virtuelle Maschine, in der dieser Test läuft und die Startzeit
der VM.

71
eclipse_v01.book Seite 72 Montag, 30. Januar 2006 12:02 12

Debuggen in Eclipse

Über einen Doppelklick auf dem entsprechenden Frame wird dieser (soweit der Source-
code vorhanden ist) im Editor angezeigt. Über die rechte Maustaste können die oben be-
schriebenen Grundfunktionen zum Navigieren durch den Sourcecode benutzt werden.

Drop to frame
Die Menüfunktion DROP TO FRAME über die rechte Maustaste auf dem Stack ermöglicht es,
in der aktuellen Methode, die im Stack zurzeit selektiert ist, an den Anfang zurückzusprin-
gen. Allerdings ist dies mit der Einschränkung verbunden, dass Variableninhalte nicht die
Werte erhalten, die sie beim ersten Einsprung besaßen. Ob DROP TO FRAME angeboten wird
und funktioniert, hängt vom Kontext und der verwendeten VM ab. Es funktioniert z.B.
nicht in nativen oder main-Methoden.
Grundsätzlich ist es kein Problem, jenen Sourcecode bzw. jene Klassen zu modifizieren, die
von der VM noch nicht über die jeweiligen Classloader-Instanzen in die virtuelle Maschine
geladen wurden. Hier ist die Modifikation recht einfach. Beachten Sie auch, dass nur jene
Sourcecode-Stellen debuggt werden können, die keine Compile-Fehler aufweisen. Da
Eclipse einen inkrementellen Compile vorsieht, ist es ohne weiteres möglich, eine Klasse
zu laden, die fehlerhafte Methoden enthält. Die Ausführung dieser Methoden wird aller-
dings nicht erfolgreich sein, wohl aber von Teilen, die sich konsistent kompilieren ließen.

Startup-Parameter einsehen
Ist durch eine sehr komplexe Launch Configuration der Überblick darüber verloren gegan-
gen, welche Parameter in welcher Reihenfolge übergeben wurden und wie Eclipse den
Klassenpfad zusammengebaut hat, dann empfiehlt sich ein Blick zu den Startup-Parame-
tern. Hierzu wird mit der rechten Maustaste auf den Namen der Startklasse in der Debug
View geklickt und PROPERTIES gewählt. Im sich öffnenden Fenster kann daraufhin die Start-
konfiguration eingesehen werden.

Über Cut and Paste kann der Inhalt aus der Process Information in ein
Command- bzw. Shellfile kopiert werden, um die Java-Klasse standalone
ohne Eclipse zu starten und von der fertigen Konfiguration zu profitieren.
Damit dies zum Erfolg führt, müssen die JVMTI-bezogenen Parameter

entfernt werden (z.B. -Xdebug -Xnoagent -Djava.compiler=NONE


Xrunjdwp:transport=dt_socket,suspend=y,address=localhost:4253). Diese
Parameter können allerdings benutzt werden, falls Remote-Debugging
selbst durchgeführt werden soll.

Variables View
In der Variables View (rechts oben in der Debug-Perspektive) können die Inhalte von loka-
len Instanz- und Klassenvariablen eingesehen werden. Anhand der aus der Outline View
bekannten Symbole lässt sich entnehmen, welche Modifier die Variable besitzt. Dahinter
erscheint der Typ (wenn das kleine Toolbar-Symbol mit dem X aktiviert ist) sowie die Na-
men und der Inhalt der Objekte. Bei komplexen Objekten können diese in der Baumansicht
aufgefächert werden. Die darunter liegende Detailanzeige (Details) zeigt standardmäßig

72
eclipse_v01.book Seite 73 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

bei primitiven Datenelementen deren Inhalte. Bei komplexen wird im Default-Modus die
toString()-Methode aufgerufen.
Wenn man sich streng an das Beispielprojekt gehalten hat, findet man in der Detailansicht
tatsächlich den toString()-Inhalt wieder. Ansonsten sieht man hier den Namen der Ob-
jektklasse und die Hashcode-Adresse.

Abb. 2.35: Variables View

Besonders das Debuggen komplexer Objekte stellte in den vorangegan-


genen Eclipse-Versionen große Probleme dar. Die kleine Variables View
hat nicht nur Probleme, umfangreiche Objektstrukturen wie beispielsweise
alle Member-Variablen eines JFrame anzuzeigen, es finden sich darin

auch oft verhältnismäßig viele, für den Debug-Prozess „uninteressante“


Informationen und Werte. Um dieses Problem zu lösen, gibt es zwei Mög-
lichkeiten: 1. uninteressante Membertypen wegblenden (über das Menü in
der Variables View lassen sich Konstanten und statische Elemente ein-
und ausblenden) und 2. die logische Baumstruktur modifizieren.
Mit Eclipse 3.1 ist ein neuer Formatter eingeführt worden, der nicht die
Detailansicht (also die toString()-Ausgabe) modifiziert, sondern die logi-
sche Baumstruktur des jeweiligen Objekts anpasst. Er ist über das Kon-
textmenü der Variables View erreichbar und trägt den Namen JAVA LOGICAL
STRUCTURES. Seine Bedienung ist ein wenig kompliziert. Über den Button
ADD können für einzelne vollqualifizierte Klassennamen einzelne oder
Baumstrukturen definiert und in einer Vorschauansicht begutachtet wer-
den. Haben Sie also eine der berüchtigten Swing-Klassen zur Hand, die
gedebuggt werden sollen, können Sie hier die wenigen interessanten
Membervariablen herausfiltern und alle anderen ausschließen.

In den PREFERENCES können die Inhalte typartig einzeln konfiguriert werden. Bei allen kom-
plexen und zusammengesetzten Objekten (Arrays) findet sich ein ID-Wert hinter dem Na-

73
eclipse_v01.book Seite 74 Montag, 30. Januar 2006 12:02 12

Debuggen in Eclipse

men. Der ID-Wert stellt eine logische Speicheradresse innerhalb der virtuellen Maschine
dar (ID A == ID B => Objekt A == Objekt B).
Über das Menü des Debug-Fensters lässt sich die Ansicht der View steuern. Abgesehen von
der Steuerung der Anordnung der Fensterelemente und der Ausblendung einzelner Typen
ist der Menüpunkt JAVA PRIMITIVES interessant. Hier wird vorgegeben, in welcher Repräsen-
tation primitive Java-Typen ausgegeben werden sollen. Neben der Anzeige eines Character
in Form seines Unicode-Zeichens könnte die Anzeige als hexadezimaler Wert von Interesse
sein.
Eine Reihe weiterer Funktionen in der Variables View sind über die rechte Maustaste auf
den entsprechenden Baumeinträgen verfügbar (siehe Abb. 2.35). Diese sind in Tabelle 2.4
beschrieben.

Tabelle 2.4: Variables View-Funktionen

Menüpunkt Aktion

Select All Selektiert alle geöffneten Baumelemente.

Copy Variables Kopiert die Informationen der geöffneten und selektierten Baum-
elemente in die Zwischenablage z.B. zum Anzeigen in einem Editor.

Change Variable Öffnet ein Fenster zum Ändern des Variableninhalts. Dies geht nur
Value bei primitiven Datentypen nicht jedoch bei komplexen. Um kom-
plexe Objekte zu beeinflussen, z.B. auf null zu setzen oder neu zu
initialisieren, siehe Abschnitt „Display View – Execute“.

Find Variable Listet alle Variablen innerhalb des aktuellen Scope mit ihrem
Namen auf. Nach ihnen kann jeweils durch Teileingabe des
Namens gesucht werden.

New Detail Öffnet einen Dialog, in dem durch einen Code Snippet vorgegeben
Formatter werden kann, wie der Inhalt des Objekts in der Detailanzeige ange-
zeigt werden soll. Dies ist dann hilfreich, wenn die toString-
Methode nicht implementiert wurde bzw. werden konnte (fremder
Bytecode) und kurzfristig die Ansicht geändert werden soll.

Open Declared Öffnet ein Fenster mit dem Sourcecode des Objekttyps.
Type

Open Declared Öffnet ein Fenster mit der Vererbungshierarchieansicht des Objekt-
Type Hierarchy typs.

Instance Zeigt alle Breakpoints innerhalb des selektierten Objekts an, die
Breakpoints definiert sind, und erlaubt es, diese zu begrenzen.

Toggle Fügt einem Objekt einen Watchpoint hinzu oder entfernt diesen.
Watchpoint Zum Thema Watchpoints empfiehlt sich ein Blick in den anschlie-
ßenden Abschnitt über Breakpoints (2.7.7).

74
eclipse_v01.book Seite 75 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

Tabelle 2.4: Variables View-Funktionen (Forts.)

Menüpunkt Aktion

Watch Mit Watch wird ein Beobachter auf das selektierte Objekt gesetzt,
das ab jetzt in der Expression View wiederzufinden ist und das die
ganze Zeit beobachtet wird. Siehe Expression View-Abschnitt.

Inspect Über Inspect kann das aktuell selektierte Objekt als eigenständige
Baumanzeige in der Expression View geöffnet und dessen Inhalte
separat betrachtet werden.

Geänderte Variablenwerte werden jeweils farbig markiert. Dies hilft auch beim Debuggen
durch den Sourcecode. So ist zur Laufzeit erkennbar, welche Werte durch den Sourcecode
manipuliert worden sind.

2.7.7 Breakpoints, Watchpoints und Exceptioncatching


Grundsätzlich gibt es mehrere Möglichkeiten, die virtuelle Maschine zur Laufzeit anzuhal-
ten. Bei den Breakpoints wird eine Position im Sourcecode angegeben. Unter Umständen
wird ein Breakpoint mit einer Bedingung verbunden. Wie das funktioniert, wurde bereits
betrachtet.
Watchpoints:
Ein Watchpoint ist eine Art Beobachter, allerdings wird nicht der Sourcecode, sondern es
werden die benutzten Variablen in Form von Objekten oder primitiven Typen beobachtet.
Dies kann zu einem häufigen Stoppen der virtuellen Maschine führen, wenn der als Watch-
point gewählte Typ in unterschiedlichen Kontexten sehr oft vorkommt.
Ein Watchpoint lässt sich über die Funktion TOGGLE WATCHPOINT in der Variables View, die
wir gerade angesehen haben, setzen. Wird ein Watchpoint aktiviert, ändert sich in der Va-
riables View zunächst gar nichts. In der Breakpoint View kommt allerdings ein zusätzlicher
Watchpoint-Eintrag (Brillensymbol mit Stift, siehe Abb. 2.36) hinzu.

Abb. 2.36: Breakpoint View, Statements, Watchpoints, Exceptions

In der Abb. sind vier Breakpoint-Typen zu sehen, die von Eclipse unterstützt werden. Der
erste Breakpoint ist ein Classloader-Breakpoint. Beim Lesen und Laden der Klasse wird in
diesem Fall der Haltepunkt aktiv.
Der zweite Breakpoint ist ein Exceptionbreakpoint. Er wird aktiv, wenn eine NullPoin-
terException geworfen bzw. gefangen wird (CAUGHT AND UNCAUGHT). In diesem Fall ist
er zusätzlich auf eine konkrete Instanz (instance=(id=36)) fixiert, d.h., er greift nur,
wenn die NullPointerException exakt in dieser Objektinstanz geworfen wird.

75
eclipse_v01.book Seite 76 Montag, 30. Januar 2006 12:02 12

Debuggen in Eclipse

Der dritte Breakpoint ist der typische Standardhaltepunkt. Er unterbricht den Fortlauf des
Programms in Zeile 31, in der testGetAllOccurrencesOfPattern()-Methode.
Der vierte und letzte Breakpoint in der Auflistung ist ein Watchpoint auf der Variablen
regexpCompiler.
Wenn in der VARIABLES VIEW auf dem ExpressionAnalyzer ein TOGGLE WATCHPOINT
aufgerufen worden ist, erscheint dieser hier unter den Breakpoints (vierter Breakpoint in
der Abbildung). Dahinter ist dann zu erkennen, dass es ein Watchpoint aus dem Objekt
regexpCompiler ist.
Wichtig ist der Eintrag in Klammern: ACCESS AND MODIFICATION. Wie beim Breakpoint kann
man Bedingungen angeben, unter denen die virtuelle Maschine im Debug-Modus anhalten
soll. In diesem Fall geschieht das beim Zugriff auf das Objekt (z.B. Lesen des Werts) und
bei der Modifikation des Objekts.

Oft reicht ein Watchpoint auf die Modifikation der Instanz vollkommen aus.
Watchpoints auf häufig vorhandene Typen wie z.B. java.lang.String sollten
möglichst vermieden werden, um nicht im Breakpoint-Dickicht die Über-
sicht zu verlieren, oder erst über die Checkbox vor dem Breakpoint-Ein-

trag aktiviert werden, wenn sie benötigt werden.

Die möglichen Eigenschaften der jeweiligen Breakpoints lassen sich erwartungsgemäß


durch die rechte Maustaste auf dem jeweiligen Breakpoint erreichen. Typabhängig kann
hier z.B. für den Watchpoint eingetragen werden, ob er bei MODIFICATION oder bei ACCESS zu-
schlagen soll. Typunabhängig hingegen sind allgemeine Informationen wie ENABLE, also
Aktivieren des Breakpoints, DISABLE oder REMOVE und REMOVE ALL zum Löschen eines
oder aller Breakpoints. Der Unterschied zwischen SUSPEND VM und SUSPEND THREAD wurde
bei den normalen Breakpoints bereits dargestellt. Über diesen Menüpunkt ist wählbar, ob
nur der Thread, in dem der Breakpoint gefunden wurde, oder alle Threads der VM (also die
VM selbst) angehalten werden sollen.
In der Variables View können die Breakpoints durch Anklicken der jeweiligen Variable und
Auswahl des Menüpunkts INSTANCE BREAKPOINTS sogar auf einzelne Variablen beschränkt
werden.
Kommen wir zum dritten Typus von Breakpoints, zum Javastacktrace, den die meisten
Java-Anwendungen in einen Outputstream schreiben. Wenn es zu einem Fehler bzw. einer
Exception kommt, hilft es dem Programmierer zumeist schon, den Fehler auf eine Methode
einzukreisen.

Wird der Stacktrace im Fehlerfall auf die Konsole geschrieben (System.out/


.err) und diese nicht umgeleitet, so erscheinen die Ausgaben in Eclipse in
der Console View. Wenn man mit der Maustaste durch einen solchen
Stacktrace wandert, erkennt Eclipse die Methodennamen und führt den

Programmierer nach Klick mit der Maus direkt an die Stelle, wo der Fehler
aufgetreten ist. Beim Klicken auf den Namen einer Exception öffnet sich
automatisch der Dialog zum Exception Catchen als Breakpoints.

76
eclipse_v01.book Seite 77 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

Exception-Catching
Bei einer Nullpointerexception kann es passieren, dass im Log nur null oder nichts er-
scheint, je nachdem, wie viel Bemühungen in ein aussagekräftiges Log gesteckt worden
sind. In diesem Fall (und in einer ganzen Reihe von weiteren Fällen) ist es vorteilhaft, die
Nullpointerexception schon in dem Moment abzufangen, in dem sie auftritt. Um dies zu
tun, wählen wir das kleine „J“ mit dem Ausrufezeichen in der Toolbar der Breakpoint View,
das mit der Unterschrift JAVA EXCEPTION BREAKPOINT gekennzeichnet ist. Der Name be-
deutet nicht, dass nur java.lang.Exception oder alle davon abgeleiteten Klassen zum
Breakpoint definiert werden können, auch Throwable bzw. alle Error und Runtimeexcep-
tions können auf diese Art und Weise abgefangen werden.
Im sich öffnenden Dialog ist der gewünschte Exception-Typ auszuwählen und zu konfigu-
rieren, ob nur bei nicht abgefangenen oder bei abgefangenen Exceptions angehalten werden
soll. Die VM nur im ersten Fall anzuhalten, macht in der Regel keinen Sinn, da sie sowieso
nicht weiterlaufen kann.

Es ist selbstverständlich ungünstig, in der Vererbungshierarchie sehr weit


oben stehende Exception-Typen wie beispielsweise „caught“ Nullpoin-
terexceptions als Breakpoints zu aktivieren, denn dadurch taucht sehr
schnell die erste Exception in manchen Frameworks auf und wird abgefan-

gen, ohne sichtbar zu werden. Daher sollten sehr abstrakte Exceptions


(z.B. aus dem java.lang-Package) soweit möglich erst aktiviert werden,
wenn man schon weiß, dass diese gleich auftreten werden.

Class Load Breakpoint

Im Menü RUN finden wir den Menüpunkt ADD CLASS LOAD BREAKPOINT. Wir
können diese Breakpoints aber auch in jeder View (z.B. Package Explorer)
über die rechte Maustaste erreichen. In Eclipse 3 können so zu den „nor-
malen“ Breakpoints die VM bzw. Threads angehalten werden, wenn eine

Klasse zum ersten Mal durch einen Classloader geladen wird.

Method Breakpoint

Beim Aufruf von Methoden lässt sich ebenfalls ein separater Breakpoint
definieren. Dieser wird in Eclipse als Method Breakpoint bezeichnet und
kann in einer View über die rechte Maustaste und TOGGLE METHOD BREAK-
POINT aktiviert werden.

77
eclipse_v01.book Seite 78 Montag, 30. Januar 2006 12:02 12

Debuggen in Eclipse

Breakpoints organisieren

Da es in umfangreichen Applikationen zu komplexen und großen Break-


point-Listen kommen kann, die abhängig vom jeweiligen Testfall oder der
Komponente nur aktiv werden sollen, können in Eclipse WORKING SETS
angelegt oder Breakpoints anderweitig sortiert/organisiert werden. Hilf-

reich ist hierbei die Menüfunktion SHOW, erreichbar über das Kontextmenü
der Variables View. Wählen Sie dort die Organisation in Working Sets oder
über Sortierkriterien (z.B. in welchem Projekt sich die Breakpoints
befinden).

Expression View
Als Nächstes sehen wir uns die Expression View an. Dazu selektieren wir den Menüpunkt
WINDOW | SHOW VIEW | EXPRESSIONS.
Variablen bzw. Objekte, die in der Variables View über WATCH in die Expression View ko-
piert worden sind, können über Expressions ausgewertet werden. Um eine neue Expression
für das zu überwachende Element zu erfassen, wählen wir im Kontextmenü der Expression
View mit ADD bzw. EDIT WATCH EXPRESSION ein neues Element aus und geben im Dialog die
Watch-Expression ein, die evaluiert werden soll. Eine Reevaluierung dieser Bedingung
kann ebenfalls über das Kontextmenü vorgenommen werden.

Display View
Um die Display View zu aktivieren, wählen wir im Menü WINDOW den Menüpunkt SHOW
VIEW | DISPLAY.
Eine der meist unterschätzten Views ist die Display View. Hinter dem unscheinbaren Na-
men und dem leeren Eingabefeld verbirgt sich eine sehr mächtige Debug-Funktionalität.
Die Display View erlaubt die Anzeige, aber auch die Auswertung und Ausführung von
Sourcecodeteilen auf Objekten, die sich aktiv in der Variables View, also im Stack befinden.
Die Display View verfügt sogar über die Fähigkeit, diese Objekte nicht nur zu benennen,
sondern Code Completion bei der Eingabe zu bieten (siehe Abb. 2.37).

Abb. 2.37: Display View mit Code Completion

Im Folgenden sind kurz die Aktionen aufgelistet, die auf einem Sourcecode-Schnipsel
(snippet) über die rechte Maustaste ausgeführt werden können.

78
eclipse_v01.book Seite 79 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

Display
Lautet der Sourcecode z.B. analyzer.toString(), so kann dies unterschiedlich ausge-
wertet werden. Zum einen ist es sinnvoll, sich den Inhalt beim Aufruf anzusehen (originäres
Display). Hierzu wird der Sourcecode-Teil markiert, der in der Display View angesehen
werden soll, und dann über die rechte Maustaste DISPLAY angeklickt.
Das Ergebnis sieht folgendermaßen aus:

analyzer.toString()
(java.lang.String)
{ExpAnalyzer:org.apache.oro.text.regex.Perl5Compiler
@1edc073}

Eclipse liefert den Typ und den Inhalt zurück. Zu beachten ist, dass ein Display sinnvoller-
weise nur für Methoden, die nicht vom Typ void sind, erfolgen sollte. Bei allen anderen
Rückgabetypen wird das zurückgegebene Element in seine String-Repräsentation transfor-
miert.

Watch und Inspect


Watchpoints und Watchexpressions wurden bereits im vorangehenden Kapitelabschnitt
kurz erläutert. Die Expression, die beobachtet werden soll, kann auch in der Display View
eingegeben (z.B. analyzer==null) und dann über die rechte Maustaste zur Watch Ex-
pression gemacht werden.
Gleichzeitig kann durch Eingabe des Variablennamens und Markierung des Namens mit
anschließender Auswahl von INSPECT über die rechte Maustaste der Inhalt der Variablen an-
gezeigt werden. Wenn wir also analyzer.toString() markieren und mit INSPECT anse-
hen wollen, wird die Methode auf der Analyzer-Instanz aufgerufen und das Rückgabeob-
jekt (in diesem Fall String) mit seinen Unterelementen (Inhalte, Größe, Characterarray, ...)
in der Expression View angezeigt.

Execute
Die zuletzt genannten Optionen auf der Display View sind nützlich, aber keine echten Neu-
erungen gegenüber den Funktionalitäten, die die Expression View bereits zur Verfügung
stellt.
Die mächtigste Aktion ist das Aufrufen des Execute-Befehls auf einem Code Snippet der
Display View. Was einem die Variables View verwehrt, nämlich das einfache Bearbeiten
komplexer Objekte, lässt sich mit der Display View bewerkstelligen. Es ist dort möglich,
jede beliebige Methode auf jedem durch die VM erreichbaren Objekt aufzurufen.
Als Beispiel verwenden wir das Objekt analyzer vom Typ Analyzer und ordnen diesem
eine null-Referenz zu (analyzer=null). Nach Aufruf von EXECUTE verweist die Objekt-
variable auf null. Stattdessen kann auch jede andere beliebige public-Methode darauf
ausgeführt werden – so, als sei sie an dieser Stelle im Sourcecode vorgekommen.

79
eclipse_v01.book Seite 80 Montag, 30. Januar 2006 12:02 12

Debuggen in Eclipse

Content Assist
Content Assist ist behilflich bei der Suche von Objekten. Die weitere Dokumentation kann
aber, wie auch bei den Befehlen CUT, PASTE und CLEAR, entfallen, da es aus dem Zusam-
menhang deutlich wird.

Alle hier vorgestellten Aktionen (Inspect, Display, Execute und Watch)


können auch über das Kontextmenü der jeweiligen Objekte im Editor auf-
gerufen werden. So kann jedes Objekt untersucht und wiedergefunden
werden, ohne die Views manuell zu durchsuchen.

2.7.8 Hot Code Replacement


Wir kommen jetzt zu Funktionen in der Debug View, deren korrekte und erfolgreiche Ar-
beit sehr stark von der Unterstützung und damit zumeist der Version der darunter liegenden
und im Debug-Modus verwendeten VM abhängt. Welche VM diesbezüglich sich als taug-
lich erweist, muss man austesten oder beim entsprechenden Hersteller erfragen.
Eclipse erlaubt in begrenztem Maße, zur Laufzeit den Sourcecode einer Methode zu än-
dern. Dies hat den Vorteil, dass, wenn der Fehler gefunden worden ist, kein komplettes
Build durchgeführt und bis zum Fehler vordebuggt werden muss.
Diese Fähigkeit wird als Hot Code Replacement oder oft auch im Zusammenhang als Hot-
swap Feature bezeichnet. Ob einem die VM einen solchen Eingriff erlaubt, hängt von der
Implementierung der JVMTI für die jeweilige virtuelle Maschine ab (siehe Abschnitt
2.7.5).
Diese Fähigkeit soll kurz am Beispiel der NullPointerException aus dem Beispiel-
projekt betrachtet werden. Sollte dieser Fehler nicht behoben sein, muss an die Sourcecode-
Stelle debuggt werden, an der ein return null die Methode verlässt. Dieses wird ersetzt
durch return results und danach mittels Ÿ+S gespeichert. Anschließend sollte
eine VM, die Hotswap unterstützt, den geänderten Sourcecode akzeptieren und der Debug-
ger wieder an den Beginn der Methode zurückspringen.
Sollte das Hotswap fehlschlagen, gibt es eine ganze Reihe von möglichen Gründen. Einige
davon sind:
1. Hinzufügen, Entfernen oder Umbenennen von Methoden, Klassen oder Instanzvariab-
len, die vom Classloader während des Debug-Vorgangs schon in die VM geladen wor-
den sind.
2. Sourcecode-Änderungen, die zu Compile- und somit Syntaxfehlern führen.
3. Modifikationen in anderen Methoden des Stacks oder Sourcen, die nicht der aktuellen
Methode entsprechen.
4. Einsatz einer virtuellen Maschine, die das entsprechende Hotswap-Feature nicht unter-
stützt.

80
eclipse_v01.book Seite 81 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

Es kommt – wie bereits kurz in diesem Kapitel anklang – vor, dass Entwickler sogar im pro-
duktiven Application Server Fehler während der Laufzeit über Hotswap behoben und so
den Sourcecode korrigiert haben. Hiervon ist abzuraten, denn der Application Server muss
auf einer entsprechenden VM laufen, die Classfiles sollten am besten mit Debug-Attributen
versehen sein und schließlich muss die Anwendung Hotswap-Fehler abfedern können.
Je nachdem, wie atomar die Methoden sind und wie das Design einer Anwendung aussieht,
kann Hotswap mehr oder minder gut genutzt werden. Es sollte versucht werden, die Me-
thoden überschaubar zu halten und überflüssige Instanzvariablen als globalen Datenpool
der Klasse zu vermeiden. Dies vereinfacht die Arbeit mit dem Debugger.

2.8 Navigation im Sourcecode


Java ist eine Sprache, die vor allem durch die einfache und sichere Abbildung objektorien-
tierter Lösungen in Klassen und Interfaces populär wurde. Ein komplexes Beziehungs-
geflecht zwischen diesen beiden kann bei einer großen Anzahl eben solcher Elemente al-
lerdings auch schnell zur Verwirrung des Entwicklers führen.
Die Geschwindigkeit und Qualität, in der der Entwickler arbeitet, hängt dabei vorzugweise
von der Dokumentation des bestehenden Sourcecodes (vor allem Javadoc), aber auch von
der Fähigkeit ab, sich schnell in Vererbungshierarchien einarbeiten zu können, zu wissen,
welche Methoden z.B. überschrieben wurden und welche Interfaces zu implementieren
sind.
Selbstverständlich stellt Eclipse hierfür diverse Views und Funktionen bereit, teilweise stel-
len sie nur unterschiedliche Blickrichtungen auf ein und dieselbe semantische Tatsache dar,
weshalb wir ihre Aufzählung auf die wesentlichen von ihnen beschränken.

2.8.1 Javadoc View


Zu einer Klasse, dem Objekt einer Klasse oder einer Methode erscheint der zugehörige
Javadoc-Kommentar, wenn im Editor mit der Maus darübergefahren wird. Drücken der
Ê-Taste ermöglicht es, in diesem Fenster durch den vollständigen Kommentar zu scrol-
len.
Eclipse verfügt zusätzlich über eine eigene View für den Javadoc-Kommentar, die wie alle
anderen Views auch über das Menü WINDOW | SHOW VIEW | OTHER | JAVA | JAVA DOC aktiviert
werden kann. Jedes Mal, wenn im Sourcecode der Cursor neu gesetzt wird, wird der jeweils
im Kontext zugehörige Javadoc-Kommentar angezeigt. Über die rechte Maustaste in der
View kann man sich zur entsprechenden Implementierung führen lassen.

Über WINDOW | PREFERENCES kann man sich im Unterpunkt JAVA | COMPILER |


JAVADOC das falsche Ausfüllen des Javadoc zur Anzeige von Fehlern in der
Problems View anzeigen lassen. Entsprechende QuickFixes zur Korrektur
existieren ebenso.

81
eclipse_v01.book Seite 82 Montag, 30. Januar 2006 12:02 12

Navigation im Sourcecode

2.8.2 Navigation über die Toolbar


Man kann sehr schnell bei der Navigation durch den Sourcecode die Stelle aus den Augen
verlieren, an der man vor wenigen Momenten noch gewesen ist. Hilfreich ist hier die Tool-
bar, über die die entsprechende Positionierung wiedergefunden werden kann.

Abb. 2.38: Toolbar-Navigationsfunktionen

Die hier vorgestellten Funktionen sind zumeist sowohl über die Toolbar (siehe Abb. 2.38;
bei entsprechender Aktivierung über das Menü WINDOW | CUSTOMIZE PERSPECTIVE), über ei-
nen entsprechenden Menüeintrag (im Menü NAVIGATE) oder eine Tastenkombination er-
reichbar. Da die meisten Tastenkombinationen absolut frei vergebbar sind, wird auf deren
Aufzählung weitgehend verzichtet.
Die Links- und Rechtspfeile helfen dabei, in der Historie vor- bzw. zurückzugehen. Zu-
meist ist man als Entwickler aber weniger daran interessiert, alle Schritte, die man bei sei-
ner „Forschungsreise“ durch den Sourcecode gemacht hat, noch einmal zu durchlaufen,
sondern man möchte gleich an der letzten Stelle, an der man editiert hat, weitermachen.
Dafür dient der Linkspfeil mit dem kleinen Sternchen, der einen genau an diese Position
zurückbringt.
Die zwei nach oben und unten verweisenden Pfeile (Ÿ+., Ÿ+,) führen durch
die Annotations der Klasse. Die Annotations sind jene Stellen, die durch den Compile mar-
kiert wurden, an denen es Probleme gibt – sei es, dass ein Compile- bzw. Syntaxfehler vor-
liegt oder durch den Build-Vorgang eine Warnung erzeugt wurde. Welche Arten von Feh-
lern (Errors, Warnings, Infos, Tasks, ...) sie anzeigen, kann über den kleinen schwarzen
Pfeil rechts von ihnen konfiguriert werden.

2.8.3 Suchfunktionen in Eclipse


Die umfassendste Suche kann sicherlich mit dem SEARCH-Dialog durchgeführt werden, der
über die Taschenlampe in der Toolbar oder über den Menüpunkt SEARCH | SEARCH erreichbar
ist (siehe Abb. 2.39).
Die unterschiedlichen Register (FILE SEARCH, JAVA SEARCH, PLUG-IN SEARCH, NLS KEYS) ge-
ben die verschiedenen Kategorien bekannt, anhand derer gesucht werden kann. Über den
CUSTOMIZE-Button ist auswählbar, wie viele und welche Register angeboten werden.
Da die Plug-Ins in diesem Kapitel nicht betrachtet werden, werfen wir zunächst einen Blick
auf die FILE SEARCH.

82
eclipse_v01.book Seite 83 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

Abb. 2.39: Standard-Suchdialog

File Search
In CONTAINING TEXT wählen wir den zu suchenden Text (gerne auch mit Wildcards oder als
Regular Expression) und in FILE NAME PATTERNS den Dateinamen oder Typ an (sofern dieser
bekannt ist). Diese Suche entspricht der typischen Volltextsuche über eine Menge von Da-
teien. Erwartungsgemäß steigt die Ergebnisanzahl mit der Größe des Suchbereichs (hier als
Scope bezeichnet). Der Scope kann neben der selektierten Ressource oder dem Working-
Set auch die durch dieses Projekt eingeschlossenen Projekte (siehe Abschnitt 2.3.1) umfas-
sen. Maximal durchsucht Eclipse alle Dateien, die sich im Workspace befinden, wobei der
Workspace alle Projekte umfasst, die zurzeit in Eclipse aktiv sind.
Wenn man genauer abschätzen kann, in welchem Projekt oder sogar in welchem Verzeich-
nis gesucht werden soll, kann im Scope auch ein so genanntes Working Set definiert wer-
den. Über den CHOOSE-Button wird mittels eines logischen Namens konfiguriert, welche
Projekttypen, welche Verzeichnisse und welche Projekte durchsucht werden sollen. Gleich-
zeitig lassen sich Inhalte auch sofort ersetzen, dazu dient der REPLACE-Button bei der Datei-
suche.

Java Search
Die Java Search versucht dem logischen Suchstring einen Suchtyp zu assoziieren (TYPE,
CONSTRUCTOR, METHOD, ...), um das Element genauer zu beschreiben. Die Suche kann wei-
ter auf die Deklaration des entsprechenden Typs, auf Referenzen der Objekte dieses Typs,
auf Implementierungen oder auf alle Vorkommnisse begrenzt werden. Im Gegensatz zur
reinen Volltextsuche können die Ergebnisse also auch logische Ergebnisse sein, die durch
die Semantik der Java-Sprache der Suche entsprechen. Auch bei dieser Suche lässt sich der
Suchbereich einschränken.

83
eclipse_v01.book Seite 84 Montag, 30. Januar 2006 12:02 12

Navigation im Sourcecode

Die Ergebnisse der Suche werden in der Search View angezeigt, durch die mit den entspre-
chenden Pfeilen im View-Fenster navigiert werden kann. Eclipse passt die Menge der
Suchergebnisse zur Laufzeit an. Wird also ein Suchergebnis durch Modifikation einer Datei
aus der Ansicht eliminiert, aktualisiert sich die Search View entsprechend.
Sehr hilfreich in diesem Zusammenhang ist die Search History, die über das Toolbar-Icon
mit Lampe und kleinem Textfile erreichbar ist. So müssen bei erneuter Durchführung einer
alten Suche die damaligen Suchparameter nicht neu spezifiziert werden.
Alle gefundenen Sourcecode-Stellen werden nach einer Suche in Eclipse übrigens norma-
lerweise mit einem kleinen Pfeil am linken Editorrand markiert.

Find/Replace
Neben den Suchmethoden, die sich im Wesentlichen auf eine größere Menge von Dateien
beziehen und diese auf Suchergebnisse einzuschränken versuchen, gibt es die klassische
Aktion des FIND AND REPLACE-Mechanismus. Dieser greift allerdings nur im Editor auf einer
aktuellen Datei und lässt sich mit der Tastenkombination Ÿ-F aufrufen.
Es existieren hier die normalen Dialogelemente wie FIND und REPLACE-Buttons, die Mög-
lichkeit von Vorwärts- und Rückwärtssuche (von der Cursorposition aus gesehen) oder die
CASE SENSITIVE-Suche, also die Unterscheidung von Groß- und Kleinschreibung.
Seltenere Funktionen sind die Suche nur im markierten Textbereich (SCOPE – SELECTED
LINES), die Suche nur nach vollständigen Wörtern (WHOLE WORD), der WRAP SEARCH (Neu-
beginn der Suche am Anfang der Datei nach dem Erreichen von dessen Ende) und INCRE-
MENTAL SEARCH.

Bei der Suche nach einem Begriff stehen auch Regular Expressions zur Verfügung. Über
die Tastenkombination Ÿ+Leertaste kann man sich dabei Hilfe suchen.
Die Incremental Search ist ein sehr interessantes Feature. Während der Eingabe des zu su-
chenden Begriffs im FIND-Textfeld wird im Hintergrund ab der Cursorposition in der Datei
nach dem ersten Element gesucht, das der Suche entspricht, so dass zu dem Zeitpunkt, wo
der Suchbegriff eingetippt worden ist, dessen erstes Vorkommen schon angezeigt wird, be-
vor der FIND-Button angeklickt wird.

2.8.4 Open-Funktionen
Die folgenden Aktionen sind zumeist nach Selektion des Elements über einen entsprechen-
den Menüeintrag im Menü NAVIGATE, eine Tastenkombination oder durch Markierung eines
entsprechenden Begriffs im Sourcecode und Auswahl eines Kontextmenüpunkts über die
rechte Maustaste erreichbar.
Zu beachten ist, dass Eclipse bei allen nun folgenden Ausführungen nur jene Objekte bzw.
Elemente finden kann, die sich im Sourcecode befinden. Das heißt, all jene Bereiche von
Projekten, die zwecks Vereinfachung nicht in den Build-Pfad der Projekte einbezogen
wurden (dies betrifft auch nicht zum Klassenpfad hinzugezogene Libraries), werden von
Eclipse nur als reine Dateien erkannt. Auf der Suche nach Abhängigkeiten oder Verbin-
dungen kann nur die Volltextsuche helfen.

84
eclipse_v01.book Seite 85 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

Open Type (Menü, Ÿ+Á+T)


Dieser Befehl ermöglicht es, nach einem Typ (Klasse oder Interface) in der Workbench zu
suchen. Dabei bezieht sich die Suche auf die Menge aller identifizierten Typen in allen Pro-
jekten.
In einem Dialog wird der Name des Typs bzw. ein Suchmuster genannt, nach dem der
Name gefunden werden soll. In der darunter erscheinenden Liste von MATCHING TYPES kann
dann der zutreffende zur Anzeige ausgewählt werden.
Um die Suche nach immer wiederkehrenden Suchergebnissen zu erleichtern, wird in
Eclipse auch eine Liste von Vorschlägen automatisch angezeigt, die bereits einmal gefun-
dene Klassen anzeigt.

Open Declaration (Menü, rechte Maustaste, Ë)


Über diese Funktion kann man einen Typ, eine Methode oder ein Feld finden und dessen
Definition im Editor öffnen. Wenn es mehrere Ergebnisse zur Suchanfrage geben sollte,
werden diese in einem Auswahldialog zur Verfügung gestellt.

Open Type Hierarchy (Menü, rechte Maustaste, Ì)


Öffnet die Vererbungshierarchieansicht in einer entsprechenden View, abhängig davon,
welches Element selektiert worden ist. Handelt es sich beim selektierten Element um kei-
nen Typ, so wird die Typhierarchie des umgebenden Sourcecode-Elements (Klasse bzw. In-
terface) dargestellt. Für weitere Informationen zur Hierarchieanzeige siehe Hierarchy
View-Abschnitt 2.9.1.

Open Call Hierarchy (Menü, rechte Maustaste, Ç+Ÿ+H)


Zeigt die Calls to method View an, in der die Aufrufhierarchie nachvollzogen werden kann.
Entweder navigiert man zu den aufrufenden Methoden (Caller) oder zu den durch diese
Methode aufgerufenen (Callee). Einen Kurzüberblick zur Calls to method View folgt in
Abschnitt 2.9.2.

Open Super Implementation (Menü, rechte Maustaste)


Zeigt die „Super“-Implementierung einer Methode. Wenn der Cursor in eine Methode posi-
tioniert wird, die überschrieben worden ist, und dann die Aktion OPEN SUPER IMPLEMENTA-
TION ausgeführt wird, führt diese in die noch nicht überschriebene Originalimplementie-
rung.

Open External Javadoc (Menü, Á+Ê)


Vorausgesetzt, Javadoc befindet sich an einer anderen Stelle als nur im Sourcecode selbst,
kann man sich mit OPEN EXTERNAL JAVADOC die Javadoc-Kommentare im Browser anzeigen
lassen. Sinnvoll ist diese Funktion eigentlich nur für Classfiles, bei denen man keine
Sourcen besitzt und daher aus diesen keinerlei Informationen über Typen und Methoden
erlangen kann.

85
eclipse_v01.book Seite 86 Montag, 30. Januar 2006 12:02 12

Navigation im Sourcecode

Abhängigkeiten in Java finden


Bisher haben wir uns angesehen, wie man konkrete Klassen, deren Superklassen oder auch
Kommentare findet. Zwei Dinge klangen parallel schon an: das Suchen nach Referenzen
und das nach Implementierungen. In der Hierarchy View (siehe Abschnitt 2.9.1) kann an-
hand einer Baumdarstellung schnell nachvollzogen werden, wer dort welche Methode auf-
ruft.

Bei einem sauberen Design sollte von einer möglichst kleinen Schnittstelle
zwischen Klassen und vor allem Komponenten ausgegangen werden. Da
das automatische Generieren von Accessor-Methoden (Javabeans-Kon-
vention) oft dazu verleitet, alles public zu deklarieren und sich auch nicht

um Informationhiding zu kümmern, sondern Originalreferenzen um bisher


sorgsam als private deklarierte Variablen nach außen zu geben, ist es in
einem Review (wenn man meint, eine Komponente funktioniert) sinnvoll,
zu schauen, wer die Methoden aufruft. Es muss nicht alles private
deklariert werden, aber vielleicht ist der Gültigkeitsbereich protected
oder über ein Package auch schon ausreichend. Dabei kann die Suche
nach Referenzen an dieser Stelle helfen. Das rettet allerdings nicht vor
den „Feinden des sauberen Programmierens“, z.B. der Java Reflection.

Alle Suchfunktionen bezüglich Abhängigkeiten finden sich im SEARCH-Menü oder können


über die rechte Maustaste (im Editor oder auch in einer entsprechenden View wie dem
Package Explorer) auf dem entsprechenden Objekt aufgerufen werden.
Bei jeder Suchanfrage (Referenzen, Deklarationen, Implementierungen, Schreib/Lese-Zu-
griff) ist jeweils wählbar, ob im kompletten Workspace (alle Projekte, die zurzeit in Eclipse
sind, mit deren Sourcen), im aktuellen Projekt, in der aktuellen Hierarchie (Vererbungs-/
Implementierungsbaum) oder in einem Working-Set gearbeitet werden soll. Das Prinzip
des Working-Set haben wir bereits in den allgemeinen Suchfunktionen kennen gelernt.
Hierbei legt man unter einem logischen Namen einen Bereich fest, in dem gesucht werden
soll.
Nicht jede Aktion ist auf jedem Objekt (Klasse, Interface, Member-Variable, ...) ausführbar.
Wir dürfen nicht vergessen, dass wir z.B. beim Suchen von Referenzen im Java-Sourcecode
die Dateien ausschließen (die vielleicht Java-Code enthalten), die wir für den Build-Prozess
nicht als Sourcen mit ins Projekt einbezogen haben. Im Gegensatz zur Volltextsuche kann
Eclipse Referenzen in diesen Dateien nicht erkennen (z.B. wenn aus Performance-Gründen
Teile des Sourcecodes nicht kompiliert werden sollen).
Die Menge der Suchergebnisse (hier z.B. die Suche nach Referenzen auf die getAll-
OccurrencesOfPattern-Methode) wird wie immer in der Search View angezeigt (siehe
Abb. 2.40). Die Resultate lassen sich nach unterschiedlichen Kriterien, z.B. Projekte oder
Packages, gruppieren, um bei großen Ergebnismengen den Überblick nicht zu verlieren.
Über die rechte Maustaste besteht in der Search View auch die Möglichkeit, die Suche zu
modifizieren, erneut durchzuführen oder Referenzen und Deklarationen der Suchergebnis-
se sofort wieder aufzuspüren (um nur einige Funktionen zu benennen).

86
eclipse_v01.book Seite 87 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

Abb. 2.40: Suchergebnisse in der Search View

2.9 Weitere Views

2.9.1 Hierarchy View


Die Hierarchy View (Ì-Taste auf dem entsprechenden Java-Element oder WINDOW |
SHOW VIEW | HIERARCHY) zeigt, wie bereits im Open Type Hierarchy-Abschnitt 2.8.4 be-
schrieben, die Vererbungs- und Implementierungshierarchie vom jeweils ausgewählten
Java-Typ aus betrachtet (siehe Abb. 2.41). Im oberen Teil wird ein Hierarchiebaum an-
gezeigt. Abhängig davon, welche Toolbar-Einstellung aktiv ist, zeigt dieser entweder die
Hierarchie vom aktuellen Element aufwärts (von welcher Klasse erbt meine Klasse, wel-
che Interfaces werden von meiner Klasse implementiert?) oder abwärts (wer erweitert oder
implementiert den aktuellen Typ?) oder eingeschränkt sogar beides (ganz linker Toolbar-
Menüeintrag).

Abb. 2.41: Hierarchy View

Die Darstellung des aktuell ausgewählten Typs als Baum-Wurzelobjekt kann dabei etwas
verwirrend sein. Sie stellt nicht immer die Hierarchie nach oben oder nach unten dar.
Im darunter liegenden Teil der Hierarchy View zeigt Eclipse die Variablen und Methoden
an, die durch die einzelne Klasse bzw. das jeweilige Interface zusätzlich definiert werden.
Über die Toolbar dieses Anzeigebaums ist konfigurierbar, ob zusätzlich alle geerbten
Member ebenfalls angezeigt werden sollen, ob statische und final Member ausgeblendet
und ob die Reihenfolge der Anzeige sortiert sein soll.

87
eclipse_v01.book Seite 88 Montag, 30. Januar 2006 12:02 12

Weitere Views

2.9.2 Call Hierarchy View


Die Call Hierarchy View (WINDOW | SHOW VIEW | OTHER | CALL HIERARCHY) zeigt (aufgerufen
durch die entsprechende Menüfunktion) die Aufrufhierarchien der aktuellen Methoden
(wer ruft sie auf? welche anderen Methoden werden von ihr aufgerufen?). Über die Toolbar
der Views kann eingestellt werden, in welche Richtung navigiert werden soll, wie das Aus-
sehen der View modifiziert und die Ergebnismenge gefiltert werden.

2.9.3 Declaration View


Die Declaration View zeigt (ähnlich der Javadoc View) beim Klicken auf ein entsprechen-
des Element dessen Deklaration im Sourcecode an. Die View kann, wie alle anderen Views
auch, über das Menü WINDOW | SHOW VIEW geöffnet werden. Wird die View nicht sofort in
der Liste angezeigt, kann sie im Menüpunkt OTHER ausgewählt werden.

2.9.4 Navigator View


Der Vollständigkeit halber sei hier auch kurz auf die Navigator View eingegangen. Letzt-
endlich werden alle Aktionen, egal ob Java-Elemente editieren oder XML-Strukturen er-
zeugen, immer auf Dateien ausgeführt. Will man eine Windows-Explorer-ähnliche Sicht
haben (die Unix- und Linux-Gemeinde mit ihrer Menge an Shells, Konsolen und Window
Managern mögen einen dortigen passenden Vergleich entschuldigen), tut dies die Navi-
gator View. Der große Vorteil liegt hier darin, dass es Eclipse nicht kümmert, was es für
Dateien sind und ob durch Aktionen inkonsistente Zustände erzeugt werden.
Es gibt sicherlich Ausnahmefälle, in denen die Navigator View sinnvolle Dienste leisten
kann (z.B. beim Kopieren von Packages), auf der anderen Seite handelt man sich damit
leicht Probleme an anderer Stelle ein (z.B. fehlerhafte Package-Bezeichnungen).
Die Navigator View kann über WINDOW | SHOW VIEW geöffnet werden.

2.9.5 Progress View


Eine Reihe von Threads laufen in Eclipse hin und wieder im Hintergrund (job infrastruc-
ture). Darunter fallen im Wesentlichen Läufe, die den Workspace bereinigen oder Compiles
nebenbei durchführen. Wenn man wissen möchten, welche Aktivitäten dort laufen, hilft die
Progress View weiter (siehe Abb. 2.42), die sich durch Klick auf das kleine Laufbandsym-
bol in der rechte unteren Ecke des Eclipse-Fensters öffnen lässt. Laufende Aktionen werden
auch mit dem kleinen eingeblendeten Balken in der rechten unteren Ecke angezeigt.
Einzelne Aktionen lassen sich mit dem roten Stop-Button abbrechen und über Links unter
den einzelnen Tasks können weitere Detailinformationen über Ergebnisse der Aktionen ab-
gefragt werden.

88
eclipse_v01.book Seite 89 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

Abb. 2.42: Progress View und Progressbar

2.10 Java 5 – Features


Eine der wesentlichen Erweiterungen von Eclipse 3.1 gegenüber der Vorgängerversion 3.0
war zweifelsohne die Unterstützung der Java-5-Sprachspezifikation (Codename „Tiger“).
Dies wird in der kommenden Version Eclipse 3.2 mit Java 6 vermutlich fortgesetzt werden.
Nichtsdestotrotz ist die Kodierung auch weiterhin in Java 1.4 und darunter möglich, sogar
der Compile von 5.0 Classfiles ist möglich, allerdings sind diese mit 1.4 nicht startbar. Die
zahlreichen Wizards und Eclipse-Erweiterungen, die mit Version 3.1 Einzug hielten, wei-
sen allerdings Java 5 schon als Basisplattform für die Entwicklung aus. Einige dieser oft-
mals sanft eingeführten Neuerungen haben wir beim Streifzug durch unser Anwendungs-
beispiel mit Regular Expressions bereits erstmalig kennen gelernt.
Da an dieser Stelle genügend Grundlagen geschaffen sein sollten, um mit Eclipse zu arbei-
ten, werfen wir nun für jene, die Eclipse vorwiegend oder ausschließlich für Java-5-Zwecke
benutzen wollen, einen verschärften Blick auf genau diese Funktionalitäten.

2.10.1 Java 5 on board


Wie bereits besprochen, lassen sich die Java-5-Features vor allem dann gut benutzen, wenn
man eine Java-5-VM installiert hat. In unseren Beispielen in diesem Kapitel ist dies stets
die Java 5.0_05-Version von Sun gewesen.
Danach gilt es, projekt- oder workspace-weit auch die Unterstützung für Java 5 zu aktivie-
ren. Dies geschieht wie bereits angesprochen entweder über die Projekteigenschaften oder
die Workbench-Preferences (WINDOW | PREFERENCES | JAVA | COMPILER | COMPLIANCE LEVEL
5.0). Die dazugehörigen speziellen Java 5 Compile-Optionen sehen wir in Abb. 2.43.
Natürlich ist die Verwendung der Sprachelemente wie Generics oder Enumerations ein op-
tionales Angebot an die Entwickler, um ihren Sourcecode lesbarer und gegebenenfalls auch
typsicherer zu gestalten. Insofern muss man selbst entscheiden, ob ungetypte Listen nun
einen Verstoß darstellen und ob das automatische Boxing und Unboxing primitiver Typen
ein Sicherheitsrisiko darstellt. So sind denn auch die Compile-Optionen, die in Abb. 2.43 zu
sehen sind, ganz individuell nach dem eigenen Geschmack zu konfigurieren.

89
eclipse_v01.book Seite 90 Montag, 30. Januar 2006 12:02 12

Java 5 – Features

Abb. 2.43: J2SE 5.0 Compile-Optionen

Tatsache ist aber: Wenn man mit Java 5 umgehen möchte, wird bereits eine große Runtime-
Bibliothek – eben die von JRE-Hersteller – mitgeliefert, in der diese Konzepte umfangreich
angewandt werden. Insofern ist es ratsam, die hier von Eclipse vorgeschlagenen Warning-
Meldungen zumindest nicht zu unterschreiten.
Wer übrigens mehr über Java 5 als Sprache insgesamt erfahren möchte, dem sei ein Blick
in die inzwischen umfassende Reihe der Java-5-Bücher empfohlen1.
Zur Konfiguration der Compile-Einstellungen gehört auch die inzwischen angepasste For-
matierung des Sourcecodes, der sich zumindest inhaltlich leicht von seinem Vorgänger ab-
hebt.
In den Preferences der Workbench oder pro Java-Projekt finden sich die individuellen Ein-
stellungen des Code Formatters. Wir kommen später noch auf ihn zu sprechen. Sie können
unter JAVA | CODE STYLE | FORMATTER gefunden und eingestellt werden. Dort sehen wir bei-
spielsweise auch die Anpassbarkeit parametrisierter Typen (siehe Abb. 2.44).

1. z.B. Java 5 Programmierhandbuch, Ulrike Böttcher, Dirk Frischalowski, Software & Support,
ISBN 3935042639

90
eclipse_v01.book Seite 91 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

Abb. 2.44: Sourcecode Formatter-Einstellungen

Wie man ein neues Java-Projekt anlegt, Klassen darin erzeugt und kompiliert, haben wir in
diesem Kapitel bereits ausführlich behandelt. Deshalb erstellen wir über das Menü NEW |
PROJECT | JAVA PROJECT ein Java-Projekt mit dem Namen „Java5“, um von der Konfiguration
weg hin zur Arbeit mit dem Sourcecode an dieser Stelle zu kommen.
Nach der Erstellung des Projekts bietet Eclipse für die Java-Elemente Enumeration und An-
notation spezielle Wizards an, die die Erstellung beschleunigen sollen (siehe Abb. 2.45).
Wir wollen hier testhalber eine Enumeration, eine Annotation und eine normale Java-Klas-
se zum Testen verwenden.

Abb. 2.45: Erweiterte Java 5 Wizards

91
eclipse_v01.book Seite 92 Montag, 30. Januar 2006 12:02 12

Java 5 – Features

Zunächst legen wir dazu ein Package mit dem Namen com.entwickler.eclipse-
buch.kapitel2.examples an. Darin erzeugen wir eine Standard-Java-Klasse über
NEW | CLASS mit dem Namen JavaExamples.
Die Klasse ist zunächst rein Java-1.4-lastig gehalten. Sie nimmt alle Eingabeparameter und
erzeugt pro Wert ein Kundenobjekt mit dem Namen des Eingabewerts.
In der Main-Methode der Examples-Klasse wollen wir anschließend einen beliebigen Kun-
den herausgreifen und prüfen, ob er der erste Kunde ist1.
Hierfür benötigen wir zwei Klassen. Zum einen die JavaExamples-Klasse und eine „dum-
me“ Value-Klasse, die der Einfachheit halber sogar ohne Accessor-Methoden daherkommt.
Hier die JavaExamples-Klasse:

package com.entwickler.eclipsebuch.kapitel2.examples;

import java.util.ArrayList;
import java.util.List;

public class JavaExamples {

public static void main(String[] args) {


List customers = new ArrayList();
// Aufbau einer Kundenliste
Customer newCustomer = null;
for (int i=0;i<args.length;i++) {
String s = args[i];
newCustomer = new Customer();
newCustomer.age = new Integer(32);
newCustomer.name = s;
newCustomer.type = Customer.TYPE_UNKNOWN;
customers.add(newCustomer);
}

// Extraktion eines Kunden nach dem Zufallsprinzip


int randomPosition =
(int) Math.random() * customers.size();
String oneCustomer =
(String) customers.get(randomPosition);
System.out.println(oneCustomer.equals(newCustomer));
System.exit(0);
}
}

1. Das Beispiel ist bewusst hier platziert.

92
eclipse_v01.book Seite 93 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

Anschließend die zugehörige Customer-Klasse:

package com.entwickler.eclipsebuch.kapitel2.examples;

public class Customer {

public static final int TYPE_UNKNOWN = -1;


public static final int TYPE_NORMAL = 0;
public static final int TYPE_GOOD = 1;
public static final int TYPE_VIP = 2;
public String id = null;
public String name = "";
public Integer age = null; //0;
public int type = 0;
public Customer() {
}
}

Wir finden hier im Sourcecode die typischen Java-Probleme eines 1.4er Sourcecodes. Die
ungetypte Verwendung von Listen und das übermäßige explizite Typecasting lassen die
Main-Methode, die später zur Laufzeit fehlschlagen wird, im Compile trotzdem sauber er-
scheinen.
Die Customer-Klasse selbst, durchsetzt mit zahlreichen Konstanten, kann keine weiteren
Informationen aufnehmen. Da wir die Customer-Instanzen später persistent in die Daten-
bank schreiben wollen, fehlen uns hier Metainformationen über Tabellennamen und Spal-
ten, die wir zu den Customer-Attributen speichern könnten.

2.10.2 Migration Java 1.4 nach 5.0


Beginnen wir nun mit der Migration des 1.4er Source nach 5.0. Sollten Sie bislang die JDK-
5.0-Unterstützung für obigen Source nicht aktiviert haben, holen Sie dies bitte wie oben be-
schrieben nach.

Abb. 2.46: Warnhinweis wegen Verwendung eines Raw Type

Im Editor erscheint in der Zeile des Hinzufügens des Kunden zur Liste bereits ein erster
Warnhinweis, dass der Listenzugriff ungetypt erfolgt (siehe Abb. 2.46). Leider gibt es hier-
zu keinen QuickFix, der das Problem lösen könnte. Aber wir können mit der rechten Maus-
taste auf der Kundenliste und durch die Wahl des Menüpunkts REFACTOR | INFER GENERIC
TYPE ARGUMENTS von Eclipse ermitteln lassen, welcher Listentyp für die customer variable

93
eclipse_v01.book Seite 94 Montag, 30. Januar 2006 12:02 12

Java 5 – Features

der richtige wäre. Klicken Sie auf den Button PREVIEW, um die Auswirkungen dieses Re-
factoring in Eclipse zu prüfen. Die Auswirkungen sind in Abb. 2.47 zu sehen.

Abb. 2.47: Infer Generic Type Arguments-Preview-Dialog

Nach der Bestätigung des Refactoring mit OK ist der Warnhinweis verschwunden, dafür
bekommen wir jetzt den erwarteten Compile-Fehler, der in der Java-1.4-Variante zur
ClassCastException geführt hätte (siehe Abb. 2.48)

Abb. 2.48: Type-Konvertierungsfehler

Wir ändern die Zeile entsprechend und ersparen uns ab jetzt das Casting, womit der Com-
pile-Fehler behoben wäre.

Customer oneCustomer = customers.get(randomPosition);

Um den Sourcecode nun noch etwas Java-5-konformer zu machen, ändern wir das Argu-
menten-Array der main-Methode auf die varargs-Variante, ersetzen den Integer-Kon-
struktor durch einen Autoboxing-Aufruf und den Zugriff auf die random()-Methode über
einen statischen Import.

94
eclipse_v01.book Seite 95 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

Der daraus folgende Sourcecode sieht wie folgt aus:

package com.entwickler.eclipsebuch.kapitel2.examples;

import java.util.ArrayList;
import java.util.List;
// statt Math.random()
import static java.lang.Math.random;

public class JavaExamples {

// statt String[] args:


public static void main(String... args) {
List<Customer> customers = new ArrayList<Customer>();
Customer newCustomer = null;
for (int i=0;i<args.length;i++) {
String s = args[i];
newCustomer = new Customer();
// statt new Integer(32)
newCustomer.age = 32;
newCustomer.name = s;
newCustomer.type = Customer.TYPE_UNKNOWN;
customers.add(newCustomer);
}
int randomPosition = (int) random() * customers.size();
Customer oneCustomer = customers.get(randomPosition);
System.out.println(oneCustomer.equals(newCustomer));
System.exit(0);
}
}

Ebenfalls als neues Sprachfeature dürfte die foreach-Schleife von Java 5 bekannt sein. Für
sie gibt es selbstverständlich entsprechende Wizards für ihre Erzeugung. Was aber, wenn
man bereits eine „altmodische“ For-Schleife wie in unserem Beispiel besitzt?
Klicken Sie mit der Maus auf das Schlüsselwort for und klicken Sie Ÿ-1 an. Eclipse
bietet uns daraufhin eine Umwandlung an. Wir wählen CONVERT TO ENHANCED FOR LOOP aus
und Eclipse refactored den Source entsprechend zu:

for (String s : args) {


newCustomer = new Customer();
newCustomer.age = 32;
newCustomer.name = s;
newCustomer.type = Customer.TYPE_UNKNOWN;
[...]

95
eclipse_v01.book Seite 96 Montag, 30. Januar 2006 12:02 12

Java 5 – Features

Nehmen wir uns nun der Typzuweisungszeile an. Wir wechseln in die Customer-Klasse
und ersetzen die Typdefinition durch folgendes Statement:

public CustomerState type = CustomerState.TYPE_UNKNOWN;

Eclipse kritisiert nun zu Recht, dass die Klasse CustomerState vollkommen unbekannt
ist. Über den QuickFix (Ÿ-1 oder Klick auf das gelbe Lämpchen) wählen wir nun die
Erzeugung einer Enumeration (CREATE ENUM CUSTOMERSTATE). Nach Bestätigung des
Enumeration-Wizard erzeugt Eclipse eine entsprechende Aufzählung. Wir wechseln in die
Enumeration und ergänzen weitere mögliche Status.

package com.entwickler.eclipsebuch.kapitel2.examples;

public enum CustomerState {


TYPE_UNKNOWN, TYPE_NORMAL, TYPE_GOOD, TYPE_VIP;
}

In der JavaExamples-Klasse erscheint nun erwartungsgemäß ein Compile-Fehler, dass die


Integer-Zuordnung fehlerhaft ist (siehe Abb. 2.49). Wir wählen dabei die Klassenergän-
zung mittels Ÿ-Leertaste zu CustomerState.

Abb. 2.49: Fehlerhafte Enumerationszuordnung

Wechseln Sie schließlich in die Customer-Klasse. Sie sollte später – so war die Vorgabe zu
Beginn – zu Persistenzzwecken verwendet werden. Hierfür ist es sinnvoll entsprechende
Annotations als Aspektbeschreibungen der Persistenz einzuführen. Wir tun dies durch Er-
gänzung entsprechender Annotationstatements im Source, der danach wie folgt aussieht:

package com.entwickler.eclipsebuch.kapitel2.examples;

public class Customer {

@Persistent(tableName="customer", columnName="id")
public String id = null;

@Persistent(tableName="customer", columnName="name")
public String name = "";

96
eclipse_v01.book Seite 97 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

@Persistent(tableName="customer", columnName="age")
public Integer age = 0;

public CustomerState type = CustomerState.TYPE_UNKNOWN;


}

Da die hier eingeführte Annotation mit dem Namen @Persistent grundsätzlich im JRE
nicht definiert ist, markiert Eclipse sie korrekterweise als fehlerhaft. Auch hier hilft uns
wieder der QuickFix mit Ÿ-1 auf dem @Persistent-Marker weiter.

Abb. 2.50: Erzeugte Annotation

Wir erzeugen über CREATE ANNOTATION 'PERSISTENT' die neue Annotation. Den Annotation-
Wizard-Dialog bestätigen wir mit OK. Beim Erzeugen der Attribute ist Eclipse allerdings
noch weniger hilfreich, hier müssen wir die neuen Annotationelemente per Hand anlegen.
Die fertige Annotation sieht wie folgt aus:

package com.entwickler.eclipsebuch.kapitel2.examples;

public @interface Persistent {


String tableName();
String columnName();
}

Selbstverständlich bietet Eclipse in den unterschiedlichen Views entsprechende Icons und


Symbole an, um die neuen Sprachelemente Enumerations und Annotations auch zu erken-
nen (siehe Abb. 2.51). Eine ebenso gute Unterstützung kann man auch in den Suchfunk-
tionen und im Debugger erwarten.
An dieser Stelle wollen wir den kleinen Ausflug in Richtung Java 5 vorerst auch wieder be-
enden.

97
eclipse_v01.book Seite 98 Montag, 30. Januar 2006 12:02 12

Customizing Eclipse

Abb. 2.51: Darstellung der Java-5-Sprachelemente im Package Explorer

2.11 Customizing Eclipse


Kommen wir nun zur individuellen Anpassung von Eclipse an die eigenen Bedürfnisse.
Zur Konfiguration von Eclipse existieren viele Einstellungsmöglichkeiten, so dass ein
Blick in die Eclipse-Hilfe (Abschnitt 2.2) immer lohnt. Für die einfache Nutzung von
Eclipse ist es nicht notwendig, alle Einstellungen zu kennen. Für die Festlegung gemein-
samer Standards in Oberflächen und Workflows oder auch für spezielle Problemstellungen
und den Einsatz von weiteren Plug-Ins machen sie aber durchaus Sinn.

2.11.1 Customizing perspective


Über das Menü WINDOW ist der Unterpunkt CUSTOMIZE PERSPECTIVE erreichbar. In diesem
Dialog kann eingestellt werden, welche Icons, welche Menüpunkte und welche Plug-In-
Komponenten an welchen Stellen ein- oder besser ausgeblendet werden sollten.
Im Register SHORTCUTS wird für unterschiedliche Menüs definiert, welche Shortcuts benö-
tigt werden. Im COMMANDS-Register daneben finden sich Befehlsgruppen, wie beispiels-
weise Such- oder Debug-Funktionen, die zu Einheiten verschmolzen aktiviert oder deakti-
viert werden können. Hierbei ist zu beachten, dass die Einstellungen sich auf die aktuelle
Perspektive (im Beispiel die Java-Perspektive) auswirken und nicht auf alle Perspektiven.
Wenn eine Perspektive auf diese Art und Weise individualisiert worden ist, kann sie mit ei-
nem logischen Namen versehen und über SAVE PERSPECTIVE AS im WINDOW-Menü gespei-
chert werden.
Über das WINDOW-Menü lassen sich die Einstellungen auch zurücknehmen. Ebenso können
wir hier die geöffneten Editorfenster steuern, geöffnete Perspektiven schließen und zusätz-
liche Views und Perspektiven öffnen, was wir in den vergangenen Abschnitten bereits
mehrmals praktiziert haben.

98
eclipse_v01.book Seite 99 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

2.11.2 Eclipse individuell starten


Auch wenn es für die meisten Anwendungen nicht von so großer Bedeutung ist, kann es in
speziellen Fällen durchaus wichtig sein, das Starten von Eclipse selbst zu konfigurieren.
Die meisten Hürden werden einem von den Default-Einstellungen abgenommen. Die nun
folgenden Hinweise beziehen sich auf die Windows-Variante von Eclipse. Linux- und
MacOS-Varianten haben grundsätzlich gleiche Möglichkeiten, unterscheiden sich dann
aber je nach Plattform durch unterschiedliche Startup-Shells.
Gründe für eine spezielle Konfiguration könnten u.a. sein:
1. Eine alte Eclipse-Version wird auf eine neuere upgegradet und der Workspace der alten
Version soll unverändert in der neuen Version weiterverwendet werden.
2. Bei der Arbeit mit Eclipse tauchen OutOfMemory-Fehler auf und es soll daher sicher-
gestellt werden, dass Eclipse bei der Arbeit genügend Arbeitsspeicher zur Verfügung
hat.
3. Man möchte individuell entscheiden, mit welcher Java-Version aus welchem Verzeich-
nis Eclipse als Java-Anwendung selbst läuft.
Eclipse können über Startparameter (in Windows zumeist mit Desktop-Verknüpfungen und
deren Parametern realisiert) weitere Argumente mitgegeben werden. Die allgemeine Rei-
henfolge lautet dabei:
eclipse [platform options] [-vmargs [Java VM arguments]]
Zu beachten ist, dass Eclipse selbst zum Großteil eine Java-Anwendung ist, die lediglich
einzelne plattformspezifische Anteile (im Wesentlichen die Präsentationsoberfläche) be-
sitzt. Wenn der darunter liegenden Java-VM-Instanz beim Starten bestimmte Parameter
mitgegeben werden sollen (wie beispielsweise die Größe des nutzbaren RAM-Speichers),
so erfolgt dies über den zweiten genannten Parameter, die vmargs.
eclipse -vmargs -Xmx512M
Die Plattformoptionen, die beim Start von Eclipse mitgegeben werden können, sehen wir
uns jetzt in der Übersicht an.
-arch architecture
Definiert die Prozessorarchitektur, in deren Umgebung Eclipse läuft. Die hier eingestellten
Informationen werden den Plug-Ins für optimale Arbeit zur Verfügung gestellt. Mögliche
Werte: x86, sparc, PA-RISC, ppc.
-application applicationId
Definiert den Einstiegspunkt in Eclipse, mit dem Eclipse hochfährt (normalerweise: org.ec-
lipse.core.runtime.applications).
-configuration configurationFileURL
Lokation des Eclipse-Konfigurationsfiles in Form einer URL. Das Konfigurationsfile wird
erstellt, wenn Eclipse installiert oder aktualisiert wird.
-consolelog
Spiegelung des Eclipse-Error-Logs auf die Console, in der Eclipse gestartet wurde. Dies ist
hilfreich in Kombination mit dem Debug-Parameter.

99
eclipse_v01.book Seite 100 Montag, 30. Januar 2006 12:02 12

Customizing Eclipse

-data workspacePath
Pfad zum Workspace, dem Ort, an dem Eclipse neue Projekte standardmäßig erzeugt und
deren Einstellungen abspeichert. Normalerweise unterhalb des Eclipse-Verzeichnisses.
-debug [optionsFile]
Startet Eclipse im Debug-Modus und lädt Optionen dafür über den Dateinamen.
-dev [classpathEntries]
Startet Eclipse im Entwicklungsmodus. Die mit Kommas separierte Liste von Klassenpfad-
einträgen wird jedem Plug-In übergeben.
-keyring keyringFilePath
Zusammen mit der Passwortoption kann hier eine Autorisationsdatenbank angesprochen
werden.
-nl locale
Übergabe der Locale-Information, die Plug-Ins über den Aufruf BootLoader.getNL()
zur Verfügung steht. Für Deutsch ist das Standard-Locale de_DE.
-nolazyregistrycacheloading
Deaktiviert die Ladeoptimierung der Plug-In-Registry. Standardmäßig werden Extension-
Konfigurationselemente nur auf Anforderung vom Registry Cache geladen, diese Option
initiiert das vollständige Laden beim Startup von Eclipse.
-noregistrycache
Umgeht die Benutzung des internen Plug-In Registry Cache.
-nosplash
Startet Eclipse ohne Splash Screen.
-os operatingSystem
Definiert das Ziel-Betriebssystem, auf dem Eclipse läuft. Normalerweise kommt dies aus
der os.name-Umgebungsvariablen, z.B.: win32, linux, hpux, solaris, aix.
-password password
Passwort der Autorisationsdatenbank, benutzt in Verbindung mit der –keyring-Funktion.
-perspective perspectiveId
Perspektive, die beim Starten geladen werden soll.
-plugincustomization propertiesFile
Lokation des Property File mit den Default-Einstellungen für die Plug-Ins.
-refresh
Kompletten Workspace-Refresh beim Starten von Eclipse durchführen.
-showlocation
Option zur Anzeige der Lokation des Workspace in der Fenstertitelzeile.
-vm vmPath
Lokation des JRE (java.exe), mit dem diese Eclipse-Instanz gestartet wird.

100
eclipse_v01.book Seite 101 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

2.12 Eclipse Preferences


Über das Menü WINDOW | PREFERENCES lassen sich individuelle Anpassungen bezüglich
Oberflächenaussehen und Sourcecodehandling einstellen, wobei diese Einstellungen stets
für die komplette Oberfläche gelten und sich zum Teil projektweise noch einmal konfigu-
rieren lassen. Wie das geht, haben wir bereits im Beispielprojekt gesehen.
Jede einzelne der Funktionen zu beschreiben, würde dieses Kapitel sprengen. Dennoch
lohnt es sich, einen Blick auf die Möglichkeiten des Customizen zu werfen, besonders
wenn Sie im Team eine gemeinsame Entwicklungsumgebung und Richtlinien aufstellen.
Trotzdem ist dies nur eine Schnelleinführung, die nicht den Anspruch eines Referenzwerks
erhebt. Im linken Baum lassen sich thematisch geordnet einzelne Konfigurationen verän-
dern, was besonders für individuelle Einstellungen von Plug-Ins oft sehr hilfreich ist (siehe
Abb. 2.52). Die seit Eclipse 3.1 existierende Suchfunktion in der Combobox vereinfacht
sehr das Auffinden von einzelnen Einstellungen im kompletten Preferences-Baum.

Abb. 2.52: Eclipse-Preferences-Dialog

101
eclipse_v01.book Seite 102 Montag, 30. Januar 2006 12:02 12

Eclipse Preferences

In großen Projekten mit vielen Entwicklern kommt es häufig vor, dass The-
men wie „wie ist zu dokumentieren“ oder „wie ist der Sourcecode zu forma-
tieren“ und „wie sieht Eclipse beim Öffnen aus“ zu Grundsatzentscheidun-
gen beim Umgang mit der Entwicklungsumgebung werden. In diesem Fall

empfiehlt es sich, eine Konfiguration als Standardeinstellung zu definieren


und über die Import- bzw. Export-Funktionen im Menü FILE | IMPORT/EXPORT
| PREFERENCES zu exportieren bzw. so zu hinterlegen (z.B. in einem SCM-
System), dass alle Entwickler die gleiche Umgebung vorfinden, auch wenn
einer von ihnen mal am Rechner des anderen arbeiten muss.

General-Properties
Unter dem Oberpunkt GENERAL verbergen sich alle für die Eclipse-Oberfläche relevanten
Einstellungen.

Tabelle 2.5: General-Properties

Property Eigenschaften/Funktionen

Appearance Wechsel zwischen unterschiedlichen Presentations und Themes, z.B.


zur Aktivierung des alten 2.1er Eclipse Look and Feel.
Daneben finden sich hier die Einstellungen für Farben und Schriften
unterschiedlicher Eclipse-Elemente (Colors/Fonts).
Einzelne Plug-Ins verwenden zuweilen eigene Icons für Dateien, um
deren Zustand abzubilden. In den LABEL DECORATORS können diese
Icons ein- bzw. ausgeschaltet werden.

Capabilities Capabilities beschreiben die GUI-Elemente, die ein Benutzer für eine
bestimmte Aufgabe sehen soll. Über den Advance-Button können ein-
zelne Elemente aktiviert und deaktiviert werden.
Content Types Zuordnung von Dateiinhalten (versus Dateiendungen, siehe Unter-
punkt EDITORS | FILE ASSOCIATIONS) zu entsprechenden Editoren.

Compare/ Einstellungen der Compare-Funktionen, d.h. von Sourcecode-Teilver-


Patch gleichen mit älteren Versionen

Editors In diesem Unterpunkt werden die Editoren, d.h. die gleichzeitig geöff-
neten Sourcecode-Fenster, konfiguriert.
Hilfreich, um den Überblick über die Masse an offenen Fenstern nicht
zu verlieren, ist das Feature CLOSE EDITORS AUTOMATICALLY. Ansonsten
öffnet Eclipse für jede Klasse ein neues Fenster, was schnell unüber-
sichtlich wird.
Die Funktion TEXT FILE ENCODING ist für Entwickler gedacht, die an ein
bestimmtes Encoding bei der Dateibearbeitung gebunden sind, bei-
spielsweise in Zusammenhang mit der Entwicklung von JSPs oder
XML.
Über den Unterpunkt SPELLING können Sie zusätzliche Wörterbuchprü-
fungen einschalten, um z.B. Ihre Kommentare prüfen zu lassen.

102
eclipse_v01.book Seite 103 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

Tabelle 2.5: General-Properties (Forts.)

Property Eigenschaften/Funktionen

File Hier werden Dateiendungen und zugehörige externe Editierungssoft-


Associations ware miteinander verknüpft (z.B. Adobe Acrobat Reader und PDF).

Keys Zur Wahl persönlicher Tastenkombinationen dient dieser Unterpunkt.


Fast jede aufrufbare Aktion kann mit einem entsprechenden Shortcut
hinterlegt werden, der sie ausführt. Eine Emacs-Vorbelegung wird mit-
geliefert. Sehr komplexe Tastenkombination sind hier hinterlegbar. Die
Zuordnung lässt sich als CSV-File exportieren.

Perspectives Wahl der Einstiegsperspektive (z.B. Java- oder Debug-Ansicht beim


Starten) und Konfiguration, wie zwischen den einzelnen Perspektiven
und Views umgeschaltet werden soll.

Search Einstellungen für die Suchfunktionen in Eclipse.

Startup and Einstellung, welche Plug-Ins beim Start von Eclipse automatisch akti-
Shutdown viert werden sollen. Besonders, wenn mehrere ähnliche Plug-Ins
benutzt werden, die sich gegenseitig in der Arbeit behindern oder
überschreiben könnten, ist zu überlegen, welche für die Arbeit wirklich
benötigt werden.

Webbrowser Einstellungen zum zu verwendenden Webbrowser.

Workspace Build Order: Definition der Reihenfolge der in einem automatischen


Build durchlaufenen Projekte.
Linked Resources: Definition von Variablen, die Pfade zu verbundenen
Ressourcen definieren und z.B. in Launch configs verwendet werden
können.
Local History: Eine nützliche Funktion für jene, die kein Sourcecode-
Management zur Verfügung haben, um Versionen von Dateien verwal-
ten zu können, ist die LOCAL HISTORY. Hier wird definiert, wie viele und
wie lange sich Eclipse Änderungen an Dateien merken soll.
Wenn ein SCM-System unabhängig von Eclipse im Einsatz ist, kann
vermutlich gut auf eine solche Funktion verzichtet werden. Ist man
dagegen mit dem Laptop unterwegs, empfiehlt es sich, den Zwischen-
speicher für die Änderungshistorie etwas höher anzusetzen. Das ver-
sehentliche Löschen kompletter Dateien beispielsweise kann damit
allerdings nicht rückgängig gemacht werden.

Ant-Properties
Eclipse stellt mit Ant, einem Subprojekt von der Jakarta Apache Group1, eine Erweiterung
zur Verfügung, die Sie in Routinearbeiten wie Buildmanagement, also dem Erstellen von
auszuliefernden Java-Archiven (jar-Files), Web-Archiven (war-Files) oder EJB-Archiven
(ear-Files), unterstützt.

1. http://ant.apache.org/

103
eclipse_v01.book Seite 104 Montag, 30. Januar 2006 12:02 12

Eclipse Preferences

In vielen Unternehmen gehört der Einsatz von Ant als Teil des Build- und Deployment-Pro-
zesses nach der Entwicklung des fertigen Sourcecodes zum Standardrepertoire. Eclipse 3.1
unterstützt dabei mittlerweile sogar das Debuggen in solchen Ant-Skripts.
Innerhalb der Ant-Properties in den Preferences von Eclipse kann Ant individuell konfi-
guriert werden, angefangen von den jar-Files, die beim Start von Ant einbezogen werden,
bis hin zu der Reihenfolge der so genannten Tasks und Targets, also der Sequenzfolge der
Aktionen, die in XML definiert werden. Zusätzlich gibt es Möglichkeiten, das Syntaxhigh-
lighting und die Formatierung des XML zu beeinflussen.

Tabelle 2.6: Ant-Build-Preferences

Property Eigenschaften/Funktionen

Editor Einstellungen des Editors, mit dem das Build-XML-File, in dem die
Aktionsdefinitionen erfolgen, erstellt wird. Zusätzlich werden Vorlagen für
Ant-Tasks in Form von Templates mitgeliefert und können selbst definiert
werden.

Runtime Konfiguration des Ant selbst, welche Libraries einbezogen werden und
welche Aktionen (Tasks) im Build-Prozess mit welchen Umgebungsvariab-
len verfügbar sind. Eclipse bringt selbst eine ganze Reihe eigener Tasks
mit, die eng mit der Oberfläche zusammenarbeiten und Eclipse-Besonder-
heiten per Ant zugreifbar machen.

Help-Properties
Einstellung, wie die interne Online-Hilfe dargestellt werden soll. Hier kann man entschei-
den, ob die Hilfe intern oder im externen Browser zum Vorschein kommt.

Install/Update-Properties
Die Install- und Update-Möglichkeiten sind oft weitreichender, als man glaubt. Eclipse
bringt selbst ein Updatemanagement mit, das es erlaubt, Versionssprünge von Softwaretei-
len von Eclipse oder seinen Plug-Ins übers Internet quasi automatisch zu vollziehen, ohne
jedes Mal alles von Neuem herunterzuladen und zu installieren. Eclipse unterstützt sowohl
automatische Updates als auch die im Unternehmen öfter notwendige Proxy-Konfigura-
tion. Für die damit verbundene Konfiguration dienen die Unterpunkte in diesem Zweig des
Preferences-Baums.
In größeren Teams, die vielleicht sogar über entsprechende Entfernung an einem gemein-
samen Sourcecode arbeiten und eigenentwickelte Plug-Ins benutzen, kann die Definition
und Bereitstellung von neuen Versionen in einem zentralen Webserver reizvoll sein, um alle
Entwickler in Richtung Softwareumgebung auf dem gleichen Stand zu haben und Diskus-
sionen um unterschiedliche Eclipse- und Plug-In-Versionen zu vermeiden.

104
eclipse_v01.book Seite 105 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

Java-Preferences
Den Unterpunkt JAVA-PREFERENCES wollen wir an einigen Stellen genauer beleuchten, be-
sonders dort, wo es um Codegenerierung und -formatierung geht, aber auch dort, wo die
Compile-Einstellungen teilweise über „Leben und Tod“ der lauffähigen Software entschei-
den können.

Tabelle 2.7: Java-Preferences

Property Eigenschaften/Funktionen

Appearance Wie sollen Java-Elemente (Klassen, Methoden, Variablen) angezeigt


werden, wie sehen deren Signatur und die Abhängigkeiten zwischen
ihnen und die Formatierung der Reihenfolge der Elemente im Source-
code aus?
Das Aktivieren der Checkbox SHOW METHOD RETURN TYPES ist oft von Vor-
teil. Damit wird bestimmt, dass innerhalb der Signaturanzeige in einem
Baum auch der Rückgabetyp angezeigt wird.
Unter MEMBER SORT ORDER, einem der Unterpunkte, stellen wir ein, wie
die einzelnen Java-Elemente in ihrem Sourcecode beim Formatieren mit
Eclipse erscheinen sollen. Oft erscheint es sinnvoll, in fremdem Source-
code zu wissen, ob die Instanzvariablen beispielweise ganz oben im
Code kommen oder ganz unten.

Build path Wer nativ gerne mit der Java Runtime umgeht, der weiß, dass man ent-
weder über die Variable CLASSPATH oder den Parameter -cp der Java-
VM die notwendigen Libraries und Pfade definiert. Eclipse schert sich
allerdings intern um extern definierte Umgebungsvariablen wie
JAVA_HOME wenig. Für diesen Zweck können hier logische Namen
erfasst werden, die auf Pfade oder Dateien außerhalb verweisen und in
einem Eclipse-Projekt genutzt werden sollen. Dies ähnelt sehr den linked
resources aus dem Workbench-Abschnitt. Nicht selten vermischt Eclipse
auch beide Begrifflichkeiten.
Eclipse unterstützt die Definition eigener User Libraries. Damit sind
Sammlungen von Bibliotheken (JAR/ZIP-Files) gemeint, die immer
gemeinsam verwendet werden und daher zu einer Benutzerbibliothek
gehören. Diese Bibliotheken können dann z.B. in Launch configs schnel-
ler eingebunden werden, als alle Jar-Files von Hand zusammenzusam-
meln.
In der Registerleiste NEW PROJECT lässt sich entscheiden, mit welcher
Sourcecode/Bytecode-Trennung und welchem JRE für ein neues Projekt
als Standard begonnen werden soll. Die Möglichkeit der Trennung von
Sourcecode und generierten Classfiles sollte genutzt werden, da so die
Übersicht und das Deployment von Classfiles stark vereinfacht werden.
Hierzu wird unter SOURCE AND OUTPUT FOLDERS der Punkt FOLDERS aktiviert.
Ob als Output-Verzeichnis bin akzeptiert wird und class oder classes
gewählt wird, ist Geschmackssache.

105
eclipse_v01.book Seite 106 Montag, 30. Januar 2006 12:02 12

Eclipse Preferences

Tabelle 2.7: Java-Preferences (Forts.)

Property Eigenschaften/Funktionen

Code Style Eclipse besitzt seit Version 3 einen komplett überarbeiteten Code format-
ter. Die Komplexität der Code-Formatierungsoptionen hat damit zuge-
nommen. Es lassen sich eigene Profile für Code-Formatierungen abspei-
chern und die häufig verwendeten Sun Conventions werden als Build-In
ebenfalls unterstützt. Hier steht die Tür für teamübergreifende Codie-
rungsvorgaben offen. Wie man diese z.B. prüfen kann, beschreibt der
Abschnitt über Checkstyle im Kapitel über Qualitätssicherung.

Code Style/ Die Generierung von Sourcecode ist ähnlich wichtig wie die Formatie-
Code rung in größeren Projekten. Oft muss ein Copyrighthinweis am Anfang
Templates jeder Klasse erscheinen, der Kommentar von Methoden muss jeweils in
der gleichen Form aufgebaut sein oder aus dem Namen der Variablen
muss deren Typ erkennbar sein. Wer projekt- oder unternehmensweite
Vorschriften hat, kann diese hier niederlegen. Darunter fallen auch Logi-
ken, wie z.B. Getter-/Setter-Methoden oder andere Methodenrümpfe
generiert werden müssen.

Code Style/ Hier wird eingestellt, in welcher Reihenfolge Eclipse die Importstate-
Organize ments im Sourcecode organisieren soll.
Imports

Compiler/ Hier verbergen sich umfangreiche Einstellungen für die Prüfung des
Building, erzeugten Codes. Dazu zählen einfache „Schönheitsprüfungen“, poten-
Errors and zielle Fehlerkonstellationen bis hin zu überflüssigem Code und Java
Warnings Standard Edition 5-Compile-Konfigurationen.

Debug Die Funktionen zum Thema Debuggen sind begrenzt. Trotzdem macht
z.B. ein Ausschalten der Sourcecode-Teile, für die man keinen Source-
code besitzt, beim Debuggen (STEP FILTERING) oft Sinn, besonders wenn
Sourcecode-Teile native vorliegen, also gar nicht in Java geschrieben
sind. Da Eclipse normalerweise bei komplexen Datentypen Schwierig-
keiten hat, den Inhalt von Objekten sinnvoll anzuzeigen, empfiehlt sich
auch die Einstellung von DETAIL FORMATTERS. Ebenso ist es möglich, für
den Debugger die logischen Strukturbäume (logical structure) einzelner
Klassen zu definieren, was die Anzeige in der Variables View deutlich
vereinfacht.
Interessant sind in den Debug-Einstellungen auch die aktivierten Debug-
Informationen, die Sie beim Build besser nicht deaktivieren sollten, wenn
Ihnen an einem reibungslosen Testen etwas liegt.

106
eclipse_v01.book Seite 107 Montag, 30. Januar 2006 12:02 12

2 – Wegweiser durch Eclipse

Tabelle 2.7: Java-Preferences (Forts.)

Property Eigenschaften/Funktionen

Editor Der Editor ist sicherlich die mächtigste Funktion bei der Codeerzeugung
im herkömmlichen Sinne. Die Entwickler von Eclipse haben sich große
Mühe gegeben, dem Editor viel Intelligenz beizubringen, angefangen
von automatischen Zeilenumbrüchen, über das automatische Auffinden
von Variablen und Methoden bis hin zu impliziter Javadoc-Kommentar-
generierung, der automatischen Erzeugung von öffnenden und schlie-
ßenden Klammern sowie Java-Syntaxhighlighting. Daneben gibt es
einfachere Entscheidungen z.B. über Schriftart und Schriftfarbe von
Java-Elementen im Sourcecode und ob Zeilennummern eingeblendet
werden sollen.
Besonders die Definition von eigenen Templates, also Vorlagen für
bestimmte Codeabschnitte, kann die Entwicklung beschleunigen. Einfa-
che For-Schleifen oder If-Konstrukte sind bereits definiert und können
über den entsprechenden Template-Namen mit anschließender Tastatur-
kombination Ÿ+Leertaste aufgerufen werden. Aber auch komplexere
Strukturen in Richtung Pattern lassen sich erzeugen, indem sie über den
Button NEW im Unterpunkt TEMPLATES selbst anlegt werden.
Unter der Registerkarte FOLDING können Standardeinstellungen für das
neue Sourcecodefolding-Feature definiert werden. Ebenfalls integriert in
Eclipse ist die Funktion, durch Klick auf Source-Elemente das Auftau-
chen bestimmter Syntaxelemente (Typen, lokale Variablen, Konstanten,
Methoden-Exits – sehr hilfreich bei mehreren Returnstatements pro
Methode) markieren zu lassen (MARK OCCURRENCES). Eclipse nimmt
sogar Wörterbücher entgegen und kann so neben dem Source auch die
Kommentare auf Rechtschreibung prüfen (SPELLING).

Installed Mit der Fähigkeit, mehrere unterschiedliche virtuelle Maschinen in unter-


JREs schiedlichen Versionen vielleicht sogar von verschiedenen Herstellern
bedienen zu können, hat man sich über Jahre hinweg in vielen Java-
IDEs ausgesprochen schwer getan. Eclipse ist diesbezüglich recht offen
und in den installed JREs können die Pfade zu eigenen JREs, deren
Libraries, Sourcecodes und API-Dokumentationen erfasst und verwaltet
werden.

JUnit In diesem Dialog wird JUnit konfiguriert. Stack Trace-Filter für JUnit-
Klassen sind hier standardmäßig aktiv.

Properties Einstellungen des Property File Editors.


File Editor

107
eclipse_v01.book Seite 108 Montag, 30. Januar 2006 12:02 12

Eclipse Preferences

Run-/Debug-Preferences
In den Run- und Debug-Preferences wird das Verhalten von Eclipse beim Start von Java-
Anwendungen im Run- oder Debug-Modus und die Arbeit mit den Launch Configurations
(siehe Abschnitt 2.7) konfiguriert.
Auf zwei Unterpunkte des Run/Debug-Baums soll dabei noch kurz eingegangen werden,
da die anderen eigentlich selbst erklärend sein sollten.

Console-Optionen
Obwohl die Console als View nicht mehr als ein Outputmedium zu sein scheint, lassen sich
hier sehr schöne Effekte erzielen.
FIXED WIDTH CONSOLE, also eine feste Konsolenbreite, macht je nach Output, der dort er-
scheint, Sinn, wenn im Output keine Zeilenumbrüche erzeugt werden. Zum Beispiel ist
eine feste Breite zumeist hilfreich (80 Zeichen ist Default).
Wird hingegen viel in die Konsole rausgeschrieben, wird einen LIMIT CONSOLE OUTPUT sehr
schnell behindern. Hierdurch kann es passieren, dass eine Anwendung wie abgestürtzt er-
scheint, in Wirklichkeit aber nur der Buffer der Konsole voll war. Jeder muss für sich ent-
scheiden, wohin er loggen will. Wenn man ein Framework wie LOG4J oder Apaches Com-
mons Logging benutzt, wird man auf die Konsole vielleicht sowieso nicht angewiesen sein
und parallel noch in eine Datei schreiben, wenn nicht, muss man sich Gedanken machen, ob
80000 Zeichen ausreichen und in welcher Farbe welcher Outputstream angezeigt werden
soll.

Andere Preferences
Es wurden hier die wichtigsten Einstellungen nur kurz angesprochen und von Milestone zu
Milestone, den sich Eclipse in den letzten Jahren weiterentwickelte, kamen neue Check-
boxen und Features hinzu. Für die einen mag es Spielerei sein, für die anderen eine riesige
Maschinerie aus kleinen Schräubchen, an denen man drehen kann. Mit einigen sinnvollen
davon kann man sich sicherlich das Leben und die Arbeit bei der Erzeugung von Java-
Sourcecode wesentlich erleichtern.

108
eclipse_v01.book Seite 109 Montag, 30. Januar 2006 12:02 12

3 Eclipse als Modellierungswerkzeug

von Lars Wunderlich

3.1 Einleitung ins Thema


UML-Modellierung sowie die Stichwörter Design und Architektur gehören in Unterneh-
men oft zum Alltag. Im Folgenden wird zum Thema Modellierung das sowohl kommer-
zielle als auch frei zugängliche Produkt Omondo EclipseUML vorgestellt.
Im Anschluss daran schauen wir kurz auf die derzeit im Eclipse-Projektumfeld erhältlichen
Produkte EMF, GEF und UML2.
Leider ist es nicht möglich, den vollständigen Funktionsumfang von EclipseUML zu prä-
sentieren, u.a. auch deshalb, weil dies kein Buch über das jeweilige Tool ist, sondern über
Eclipse und weil es helfen soll, verschiedene Facetten der Plug-Ins kennen zu lernen. Wir
werfen daher einen kurzen Blick auf Omondo EclipseUML. Jeder Leser muss für sich
selbst entscheiden, ob er oder sie nun dieses oder eines der kommerziell verfügbaren Pro-
dukte verwenden möchte. Einige von ihnen finden Sie in Tabelle 3.1.

Tabelle 3.1: Eclipse UML Produkte

Produkt Adresse

Visual Paradigm for UML – http://www.visual-paradigm.com/product/vpuml/


Eclipse Integration integrations/eclipse.jsp

Argo2Ecore – Argo UML http://sourceforge.net/projects/argo2ecore


nach Ecore EMF Konverter

Objecteering UML/ http://www.objecteering.com/


Java Enterprise Edition packaging_enterprise_edition_java.php

objectiF Eclipse Edition http://www.microtool.de/objectif/en/


prod_eclipse.asp

UMLet Eclipse Plugin http://homepage.mac.com/martin.auer/umlet/

Poseidon for UML Prof. Edition http://gentleware.com/

MagicDraw UML http://www.magicdraw.com/

Together Developer/ http://www.borland.com/us/products/together/


Designer 2006 for Eclipse index.html#architect

109
eclipse_v01.book Seite 110 Montag, 30. Januar 2006 12:02 12

OMONDO EclipseUML

3.2 OMONDO EclipseUML


Leider bereitete die aktuelle, freie Beta-Version 2.1 einige Probleme bei der Installation.
Die anschließende Dokumentation stellt daher nun das kommerzielle Allround-Studio-
Paket der 2.1er Beta (samt GEF/EMF/UML2 und Datenbank-Plug-Ins rund 88 MB groß)
vor. Zusätzlich zum Download der Studio-Version benötigen Sie auch die Eclipse 3.1-Ver-
sion separat, die Sie bestenfalls vorinstallieren.
Die nachfolgende Dokumentation bezieht sich auf Omondo Eclipse Studio 2.1.0
(EclipseUML 2.1.0.20050927 Studio beta).

3.2.1 Versionen von EclipseUML


Omondo bietet zwei Produktversionen von EclipseUML an:
1 EclipseUML Free Edition bietet alles, um mit der Modellierung in Eclipse zu starten.
Die Version ist kostenlos und ein Tool, das die Integration in die GEF-, EMF- und
UML2-Frameworks nutzt.
2 Die als „EclipseUML Studio“ verfügbare Enterprise Edition erweitert den Funktions-
umfang der Free Edition um die Unterstützung für UML 2, J2EE und diverse Appli-
cation Server, Datenbankmodellierung für die gängigen Datenbankhersteller, Pattern-
unterstützung, Ausbau der bisherigen UML-Features sowie Metrikunterstützung (siehe
Abb. 3.1.).

Abb. 3.1: Architekturüberblick – OMONDO

110
eclipse_v01.book Seite 111 Montag, 30. Januar 2006 12:02 12

3 – Eclipse als Modellierungswerkzeug

EclipseUML ist bereits seit längerem ein häufig verwendetes UML Plug-In für Eclipse. In
diesem Abschnitt konzentrieren wir uns auf die primären UML-Modellierungsfunktiona-
litäten und sehen von den erweiterten Features der Studio-Version ab. Wenn Sie einen
Feature-Überblick über die verschiedenen Versionen wünschen, können Sie diesen in der
Funktionsübersicht erhalten1.

3.2.2 Download und Installation von Omondo EclipseUML


EclipseUML kann in der Free Edition2 kostenlos heruntergeladen werden, die Studio Edi-
tion ist ebenfalls auf der Omondo-Webseite als Trial (derzeit für 30 Tage) zu bekommen.
Weitere Dokumentationen zu EclipseUML sind unter www.tutorial-omondo.com verfüg-
bar.
Beide Versionen setzen auf dem EMF (Eclipse Modelling Framework3), dem GEF (Gra-
phical Editing Framework4) und dem auf dem EMF aufsetzenden UML2-Profil (UML2-
Eclipse-Projekt5) von Eclipse auf, die momentan nicht mit Eclipse standardmäßig ausgelie-
fert werden. Omondo bietet auf seiner Webseite eine Auto-Installer-Jar-File-Version an, in
der bereits das Plug-In selbst und beide Frameworks enthalten sind, so dass das Produkt
nach dem Download mit einem Doppelklick installiert werden kann. Nach der Installation
von Eclipse 3.1 und der Installation von EclipseUML kann das Produkt im Einsatz getestet
werden.

3.2.3 Klassendiagramme erstellen


Wir nehmen uns zur Visualisierung der Fähigkeiten von EclipseUML Studio eine recht ein-
fache Modellierung von ein paar Klassen vor. Dabei wird der Aufwand möglichst gering
gehalten.
Nach der Installation von EclipseUML fordert Eclipse gegebenenfalls den Entwickler auf,
sich selbst noch einmal neu starten zu dürfen, um das neue Plug-In zu integrieren. Danach
legen wir ein neues Java-Projekt mit dem Namen Abteilungsbaum über FILE | NEW |
PROJECT an und erzeugen über NEW | OTHER ein UML CLASS DIAGRAMS in diesem Projekt
(siehe Abb. 3.2).
Bereits hier sehen wir, dass EclipseUML die wesentlichen UML-Diagrammtypen unter-
stützt. Da viele Entwickler die meiste Arbeit mit Klassendiagrammen durchführen, wählen
wir UML CLASS DIAGRAMM aus und klicken auf NEXT. Auf der folgenden Maske geben wir
als Namen für das Diagramm overview.ucd an. Unter SELECT YOUR DIAGRAM PRESEN-
TATION MODE aktivieren wir die Checkboxen ASSOCIATION und DEPENDENCY und klicken auf
FINISH.

1. http://www.omondo.com/features.html
2. http://www.omondo.com
3. http://www.eclipse.org/emf/
4. http://www.eclipse.org/gef/
5. http://www.eclipse.org/uml2/

111
eclipse_v01.book Seite 112 Montag, 30. Januar 2006 12:02 12

OMONDO EclipseUML

Abb. 3.2: UML-Projektauswahl in Eclipse

EclipseUML legt für jedes neu erzeugte Diagramm eine eigene Datei im Dateisystem an.
Sollen selbst erstellte Diagramme auch anderen Entwicklern zur Verfügung gestellt wer-
den, müssen wir diese gemeinsam mit dem Sourcecode publizieren. EclipseUML erhebt
zwar den Anspruch, aus dem Sourcecode einzelne Diagramme erzeugen zu können. Den-
noch werden einzelne Layoutinformationen separat in diesen Dateien abgelegt. Zur Editie-
rung der UCD-Datei zeigt EclipseUML eine eigene View an, in der wir jetzt unsere rudi-
mentären Klassen modellieren.

Abb. 3.3: Create a package

112
eclipse_v01.book Seite 113 Montag, 30. Januar 2006 12:02 12

3 – Eclipse als Modellierungswerkzeug

Unterhalb der Editorleiste befinden sich Toolbar-Icons, die es erlauben, einzelne Elemente
im Diagramm zu erzeugen. Die jeweiligen Funktionen können durch Überfahren mit der
Maus visualisiert werden.
Über das Icon CREATE A PACKAGE legen wir ein neues Package mit dem Namen ex-
ample.tree an (siehe Abb. 3.3).
Wenn wir einen Blick auf den Package Explorer werfen, sehen wir, dass EclipseUML ein
Package mit diesem Namen angelegt hat. Alle folgenden Aktionen, die wir visuell im
Diagramm vornehmen, werden stets synchron auch in entsprechenden Dateien und Java-
Elementen ausgeführt, so dass ein zusätzlicher Generierungsschritt vom Diagramm zum
Modell entfällt.
Als Nächstes legen wir im Package example.tree ein Interface Unternehmensteil so-
wie die Klassen Abteilung und Mitarbeiter an. Wir wählen das neu angelegte Package
example.tree an und erzeugen mittels der rechten Maustaste OPEN | CREATE/OPEN
CLASS DIAGRAM ein Diagramm für das Package, wohinein wir die neuen Typen legen kön-
nen.
Dazu klicken wir nun auf die entsprechenden Toolbar-Icons und ziehen ein Rechteck an der
Stelle mit der Maus, an der diese im Package erzeugt werden sollen.
Wir lassen die beiden Klassen das Interface Unternehmensteil implementieren, wofür
wir auf das Generalisierungs-Icon und jeweils einmal auf die Klasse sowie einmal auf das
Interface klicken (siehe Abb. 3.4.).

Abb. 3.4: Klassendiagramm – Interface-Implementierung

Anschließend klicken wir mit der rechten Maustaste auf das Interface. EclipseUML bietet
zu jedem Element eine ganze Reihe von Funktionen an. Im oberen Bereich befinden sich
Sourcecode-Aktionen und Refactor-Maßnahmen für das Interface, darunter im Wesentli-
chen Menüpunkte, die auf die Visualisierung des Diagramms (Zoom, gepackte Ansicht,
Anzeige von Abhängigkeiten und Vererbung, Reihenfolge der Elemente etc.) eingehen
(siehe Abb. 3.5).

113
eclipse_v01.book Seite 114 Montag, 30. Januar 2006 12:02 12

OMONDO EclipseUML

Abb. 3.5: New Method

Über den Menüpunkt PREFERENCES am Ende des Menüs lassen sich weitere Eigenschaften
des Interface – darunter dessen Sichtbarkeit – steuern.

Abb. 3.6: AddElement-Dialog

114
eclipse_v01.book Seite 115 Montag, 30. Januar 2006 12:02 12

3 – Eclipse als Modellierungswerkzeug

Wir legen jetzt über NEW | METHOD eine erste Methode an. Als Name für die Methode wird
addElement eingetragen (siehe Abb. 3.6).
Über den ADD-BUTTON im Parameterbereich fügen wir dann einen Parameter hinzu, dessen
Name newElement lautet und dessen Typ example.tree.Unternehmensteil heißt.
Der Vollständigkeit halber fügen wir über ein Exceptions-ADD eine java.lang.Illega-
lArgumentException (eigentlich überflüssig) hinzu. Im Register JAVADOC können wei-
tere Kommentare für diese Methode erfasst werden.
Die neu generierte Methodensignatur lautet nun:

public void addElement(Unternehmensteil newElement)


throws java.lang.IllegalArgumentException;

Drei weitere Methoden werden auf die gleiche Art und Weise im Interface ergänzt:

// entfernt ein existierendes Element aus der Struktur


public void removeElement(Unternehmensteil oldElement);
// listet alle Elemente über einen Iterator auf
public java.util.Iterator getElements();
// zeigt den Namen des Elementes an
public String getName();

Da die Klassen Mitarbeiter und Abteilung das Interface implementieren, wechseln


wir in den Sourcecode der jeweiligen Klassen, klicken die kleine Fehlerlampe an und wäh-
len ADD UNIMPLEMENTED METHODS. Danach wird der jeweilige Sourcecode mit Ÿ+S ab-
gespeichert. Hierbei aktualisiert Eclipse stets die Ansicht.
Anschließend benutzen wir eine Assoziation bzw. Komposition, die zwischen der Klasse
Abteilung und dem Interface Unternehmensteil erzeugt wird. Hierzu klicken wir zu-
nächst das Assoziations-Icon, dann die Klasse Abteilung und anschließend das Interface
Unternehmensteil an. EclipseUML öffnet daraufhin ein Fenster, um diese Connection
zu beschreiben.
Ziel ist es, in der Klasse Abteilung einen Vektor von Unternehmensteilen, die die darin
enthaltenen Elemente beschreiben, zu modellieren. Die Assoziation hat zwei Enden, auf
der einen Seite das Interface Unternehmensteil (1st Association End), das die Abteilung
selbst nicht kennt, und auf der anderen Seite das Abteilungsende (2nd Association End),
das 1 bis n Unternehmensteile beinhalten kann (siehe Abb. 3.7).
Wir öffnen zunächst das Register 1ST ASSOCIATION END und deaktivieren die Checkbox
NAVIGABLE, um auszudrücken, dass vom Unternehmensteil nicht zur Abteilung navigiert
werden soll. EclipseUML beachtet dann dieses Assoziationsende nicht weiter. Das Child-
Element kennt bei dieser Konfiguration seinen Parent also nicht.

115
eclipse_v01.book Seite 116 Montag, 30. Januar 2006 12:02 12

OMONDO EclipseUML

Abb. 3.7: Assoziationsbeziehungen

Wir klicken das Register 2ND ASSOCIATION END an. Es beschreibt die Beziehung zwischen
Abteilung und Unternehmensteilen. Wir lassen hier die Checkbox NAVIGABLE aktiviert und
tragen als Namen unternehmensteile ein. Dann wird die Multiplicity auf * gesetzt,
denn eine Abteilung kann ein oder mehrere Unternehmensteile enthalten, und auf BROWSE
gedrückt, um den Typ der Sammlung von Elementen auf java.util.Vector zu ändern.
Den Association Type stellen wir noch auf COMPOSITION und klicken anschließend auf OK.
EclipseUML erzeugt jetzt in der Klasse Abteilung ein private-Attribut vom Typ Vec-
tor mit dem Namen Unternehmensteile und dazu public-Zugriffsmethoden.
Auf diese Weise erhalten wir ein zugegebenermaßen sehr simples Klassendiagramm. Die
Erarbeitung der Business-Logik dahinter fehlt noch (die add-, remove- und die getName-
Methoden haben noch keinen Inhalt). Allerdings haben wir die grobe Baumstruktur skiz-
ziert und ohne weitere Programmieraufwände generiert (siehe Abb. 3.9).

116
eclipse_v01.book Seite 117 Montag, 30. Januar 2006 12:02 12

3 – Eclipse als Modellierungswerkzeug

Abb. 3.8: 2nd Association End

Abb. 3.9: Fertiges Klassendiagramm

117
eclipse_v01.book Seite 118 Montag, 30. Januar 2006 12:02 12

EMF/GEF und UML2

EclipseUML ist außerdem behilflich beim Reverse Engineering, bei der Erzeugung von
Klassendiagrammen aus Sourcecode-Strukturen. Man kann sich schnell einen Überblick
auch bei unbekannten Java-Klassen verschaffen, indem man einzelne Java-Klassen auf die
Diagrammoberfläche zieht und per Menünavigation ihre Superklassen, Interfaces usw. ein-
blendet und somit weitere Abhängigkeiten aufzeigt.
EclipseUML speichert seine Metadaten über Beziehungen zwischen Klassen oder Daten
über Rollennamen in den Diagrammen im Source mit speziellen Tags im Javadoc ab. Dies
ermöglicht die im Gegensatz zu früheren Versionen sehr nahe Bindung zwischen Source-
code und UML-Diagramm, was auch die erneute Erzeugung der Diagramme aus dem
Source vereinfacht.

3.3 EMF/GEF und UML2


Hinter diesen drei Akronymen verstecken sich drei unter der Eclipse-Webseite erhältliche
Erweiterungsprojekte, die zu wesentlichen Bestandteilen der Eclipse-UML-Plattformen
geworden sind.

3.3.1 Eclipse Modeling Framework (EMF)


Das EMF ist das so genannte Modellierungs- und Codegenerierungs-Framework. Grundla-
ge für die Arbeit mit dem EMF ist ein Basismodell, das auf abstrakter Ebene Beziehungs-
geflechte vorwiegend in XMI beschreibt.
XMI ist die XML Metadata Interchange-Beschreibung, ein OMG-Standard1 für den Meta-
datenaustausch. Einer der Hauptanwendungszwecke von XMI ist die Unterstützung von
UML-Metamodellen (MOF: Meta Object Facility). Dies dient dem Austausch und der Se-
rialisierung von UML-Modellen und ihrem Austausch zwischen Applikationen.
EMF selbst entwickelte sich aus der Spezialisierung von MOF für größere und umfang-
reiche Entwicklungsprojekte, angepasst auf die speziellen Bedürfnisse der Programmier-
sprache Java.
EMF erlaubt nun die Bearbeitung und Anzeige solcher mittels Java Annotations, XML-
Dokumenten oder XML-Tools (wie Rational Rose oder Poseidon) erstellten Metabeschrei-
bungen.
Die entsprechenden EMF-Versionen stehen auf der Eclipse-Homepage zum Download be-
reit2. Tabelle 3.2 zeigt die verschiedenen, erhältlichen EMF-Versionen und die zugehörigen
Eclipse-Versionen, unter denen sie einsetzbar sind. Die SDK-Version umfasst derzeit ca.
22 MB und enthält Beispiele und Dokumentation.

1. http://www.omg.org/cgi-bin/doc?formal/05-05-01
2. http://www.eclipse.org/emf/

118
eclipse_v01.book Seite 119 Montag, 30. Januar 2006 12:02 12

3 – Eclipse als Modellierungswerkzeug

Tabelle 3.2: EMF- und zugehörige Eclipse-Versionen

EMF-Version Eclipse-Version J2SE-Version

2.2.0 3.2.0 1.4.2 oder 5.0

2.1.1, 2.1.0 3.1.1, 3.1.0 1.4.2

2.0.4/ 2.0.3/ 2.0.2, 2.0.1, 2.0.0 3.0.2, 3.0.1, 3.0.0 1.4.2

Nach der Erzeugung eines entsprechenden EMF-Modells (z.B. über die manuelle Erzeu-
gung via XML) erlaubt es ein spezieller EMF-Generator, Java-Implementierungsklassen
zum Modell zu erzeugen. Die so erzeugten Modellklassen können ohne Beeinträchtigung
von Modellierungsansätzen jederzeit wieder neu generiert werden.
Das EMF unterteilt sich in drei Hauptbereiche:
쐌 Einen Core-Framework-Anteil samt Metamodell (Ecore) u.a. zur Beschreibung und
Persistenz der EMF-Objekte
쐌 Ein Framework zur Erzeugung von EMF-Editoren namens EMF.Edit
쐌 Ein Code-Erzeugungssystem namens EMF.Codegen samt grafischer Benutzerober-
fläche
Das EMF ermöglicht also umfangreiche Editierung und Manipulation von Metamodellen
und die Generierung von Sourcecode-Fragmenten daraus. Im Download eingeschlossen
sind sehr umfangreiche Tutorials und Beispiele zum Umgang mit dem EMF und dem Im-
und Export in andere Applikationen.
Was EMF nicht mitliefert, ist hingegen der für viele Anwendungen so typische grafische
Editor für UML-Programme. Besonders, wer sich hier ein integriertes Produkt mit allen
Raffinessen von Forward und Reverse Engineering wie Omondos EclipseUML wünscht,
findet im EMF eher eine Basisplattform zur Erzeugung und zum Umgang mit einem Hin-
tergrundmodell. Umfangreiche Anleitungen und Dokumentation finden sich beispielsweise
auf dieser Webseite1.
Nicht umsonst verwendet Omondo noch ein zweites Eclipse-Framework, das wir unter dem
Namen GEF bei Eclipse ebenfalls herunterladen können.

3.3.2 Graphical Editing Framework (GEF)


Das GEF erlaubt erwartungsgemäß in Ergänzung zum EMF die Erstellung entsprechender
grafischer Editoren. Notwendig hierfür ist ein entsprechendes Applikationsdatenmodell,
aus dem heraus sich das GEF zur Visualisierung bedienen kann. Das GEF folgt dabei in sei-
nem Design der typischen Modell-View-Controller-Architektur, wie wir sie beispielsweise
auch im Java-Swing-Umfeld als typische Implementierungsvariante für die Kommunika-
tion zwischen Modell und Oberfläche finden.

1. http://www.eclipse.org/emf/docs.php

119
eclipse_v01.book Seite 120 Montag, 30. Januar 2006 12:02 12

EMF/GEF und UML2

Das GEF besteht aus zwei Anwendungsbestandteilen:


쐌 Einem Layout/Rendering-Teil zur Anzeige der Modelle auf dem User Interface (org.
eclipse.draw2d). Dort finden wir typische Elemente wie Layer, Cursor- und Tooltip-
support, Border oder Druckunterstützung.
쐌 Zweiter Bestandteil ist das org.eclipse.gef, eine Anwendungskomponente, die Funktio-
nalitäten wie Paletten, Controller, Undo/Redo-Support und Integration von Modell und
UI unterstützt.
Typische Anwendungsfälle für das GEF sind alle Formen von Diagrammvisualisierungen,
GUI Buildern, UML-Programmen oder Texteditoren.
Das GEF kann auf der Eclipse-Webseite samt entsprechender Beispiele heruntergeladen
werden1. GEF- und Eclipse-Version werden dabei in jeweils synchronen Versionsnummern
angeboten. Für Eclipse 3.1 benötigen Sie also normalerweise auch ein GEF 3.1, falls Sie an
den entsprechenden Funktionalitäten interessiert sind.

3.3.3 UML2
Die Unified Modelling Language führte mit ihrer Version 2.0 eine Reihe von Erweiterun-
gen ein. In diesem Kontext findet sich auch die heute notwendige Unterstützung für UML
Profiles, der individuellen Ergänzung der Modelle durch Metatags, die z.B. in modellge-
triebener Anwendungsentwicklung genutzt werden kann.
Das UML2-Projekt bietet nun eine auf EMF-aufbauende Implementierung für den UML-
2.0-Standard an. Hierfür bietet UML2 ein entsprechendes Metamodell an.
Die UML2-Versionsnummern laufen dabei kongruent zur jeweiligen Eclipse 3.x-Version.
UML2 1.1 ist somit kompatibel zu Eclipse 3.1. Zum Drucklegungszeitpunkt ist Version 2.0
für Eclipse 3.2 in Entwicklung.
Das rund 11 MB große Download-Paket kann wie die beiden genannten Vorgänger auch
von der Eclipse-Seite heruntergeladen werden2.

1. http://www.eclipse.org/gef/ - derzeit in Version 3.1.1 für Eclipse 3.1 und als Milestonerelease für die Eclipse
3.2 Version
2. http://www.eclipse.org/uml2/

120
eclipse_v01.book Seite 121 Montag, 30. Januar 2006 12:02 12

4 Qualitätssicherung in Eclipse

von Lars Wunderlich

4.1 Einführung ins Thema


Qualitätssicherung ist oft ein Stiefkind vieler Softwareprojekte, egal, ob alleine oder im
Team entwickelt wird. Dennoch taucht der Begriff im Kontext des so genannten magischen
Dreiecks des Projektmanagements auf. Dieses besagt, dass sich die Zieldefinitionen Bud-
get/Ressourcen, Zeit und Qualität gegenseitig beeinträchtigen.
Was bedeutet das? Konkretes Beispiel: Wenn die Zeit knapp wird, kommt es zu Abstrichen
an der Qualität. Genau das Gleiche kann passieren, wenn einfach die Manpower ausgeht,
wenn das Budget nicht ausreicht und somit das selbst gesteckte Qualitätsziel verfehlt wird.
Wir wollen uns kurz die Gründe für fehlende Softwarequalität ansehen und die Chancen,
die bei der Verwendung von Eclipse in diesem Bereich bestehen.

4.2 Wie äußert sich ein Mangel an Qualität?

4.2.1 Mangelnde Sourcecodetests


Einiges an Literatur zum Thema Projektmanagement verweist auf den wichtigen Block des
Testens innerhalb des Projektplans und dennoch sind viele Projektleiter der Meinung, man
könne nach der Realisierung mit der Auslieferung beginnen. Normalerweise geschieht dies
sogar zum Wohlwollen der Entwickler, die ihren eigenen Sourceteil bereits während der
Entwicklung Tests unterzogen haben und gar nicht glauben wollen, dass dort Fehler sein
könnten, wenn auch vielleicht sogar nur fachlicher Art.
Als Bugtracking-Tool bezeichnet man ein Tool, in welchem der Kunde seine Fehler doku-
mentieren und der Bugfixing-Workflow nachverfolgt werden kann. Das Produkt Bugzilla
lässt sich beispielsweise auch in Eclipse integrieren. Dank dieser modernen Techniken wird
oftmals Struktur in die Fehlerbehandlung gebracht. Nichtsdestotrotz wird Bugtracking und
Change Management häufig erst nach Projektende eingesetzt. Um es deutlicher zu formu-
lieren: Oftmals ist das Kind „Qualität“ schon in den Brunnen gefallen, wenn mit dem Tes-
ten begonnen wird. Die Aufwände der daraus resultierenden Aktivitäten sind im Gegensatz
zum Einsatz der Resourcen, die während der Entwicklungsphase dafür notwendig wären,
um einiges höher.
Für mehr technisch orientierte Tests auf einer sehr detaillierten Ebene empfiehlt sich das
JUnit-Framework, welches in Eclipse bereits integriert ist und das bereits in Kapitel 2 kurz
vorgestellt wurde. Von ihm gibt es auch diverse Derivate u.a. zum automatisierten Testen
von Swing-Applikation (JFCUnit) oder Java Enterprise Edition-Komponenten wie Web-
seiten oder Enterprise Java Beans (EJBs) beispielweise.
Daneben gibt es eine ganze Reihe kommerzieller Produkte, die aber zumeist wenig Integra-
tion in Eclipse als Plug-Ins bieten.

121
eclipse_v01.book Seite 122 Montag, 30. Januar 2006 12:02 12

Wie äußert sich ein Mangel an Qualität?

Größtes Problem in diesem Kontext bleibt allerdings die problematische Umsetzung vor al-
lem umfangreicher Softwaretests, die eine ganze Reihe von Anwendungsschichten in ei-
nem oder mehreren Testfällen durchlaufen. Hier sind oftmals umfangreichste Konfigura-
tionen vonnöten, um zum gleichen Zeitpunkt verschiedenste angebundene Fremdsysteme
und Ressourcen (wie Datenbanken oder HTTP-Verbindungen) zu einem definierten Zeit-
punkt auf einen gemeinsamen Stand zu bringen. In diesen Fällen kann das „Stubbing“ also
das Wegschneiden von bestimmten Kommunikationsverbindungen und Ersetzen durch de-
finierte, fixe, gegebenenfalls auch leicht dynamische Antwortergebnisse weiterhelfen. Für
solche Funktionalitäten bietet Eclipse derzeit allerdings keine inhärente Unterstützung an,
wohl finden sich aber diverse Open-Source-Produkte dazu im Internet.

4.2.2 Mangelnde Tests der fachlichen Funktionalität


Mit JUnit-Tests können wir einzelne Klassen oder Interaktionen zwischen diesen testen.
Sie helfen auf Basis von komponentenübergreifenden Problemen aber wie gesagt nur noch
bedingt weiter, da die Menge der Datenkonstellationen häufig unüberschaubar wird.
Daneben gilt es, in der Software die fachlichen Anforderungen abzudecken. Dass dies ge-
schieht, gehört eher in den Bereich des Projektmanagements. Wird streng nach einem ob-
jektorientierten Vorgehensmodell verfahren, sollten sich aus den Use Cases (Anwendungs-
fällen) die Test Cases, also die Testfälle für das System, abbilden lassen. Je nachdem, wie
spezifisch die Dokumentation des Use Case ist, desto eher kann die Einhaltung in den Test
Cases dokumentiert werden. Neben der Anwendung von Modellierungswerkzeugen kön-
nen fachliche Tests nur sehr bedingt technisch abgebildet werden.

4.2.3 Eingeschränkte Funktionalität


Wenn die Seite des Budget- und Ressourcendreiecks besonders hart drückt, wird oft gänz-
lich auf das Testen verzichtet, um den Einführungstermin zu halten und sich letztlich im
Nachgang auf die Kritik des Kunden einzurichten. Teilweise wird auch zusammen mit dem
Kunden die Funktionalität reduziert. Nicht selten ist dies die Folge falschen Projektma-
nagements, was letztendlich mit mangelnden Tests einhergeht. Häufig sind Projektziele
nicht klar definiert, die Motivation sinkt auf den Nullpunkt, die Projektsteuerung hat ver-
sagt oder (was ja auch vorkommen soll) den Projektleitern/innen sind Budget, Ressourcen
oder Geld so weit gestrichen worden, dass sie mit dem Rücken an der Wand stehen.
Bei Erstellung dieser Dokumentation wurde deshalb auch nach Projektmanagement-Plug-
Ins für Eclipse gesucht, z.B. zur Ressourcen- und Projektstrukturplan-Abb.. Leider existie-
ren hier zurzeit nur sehr wenige Plug-Ins. Einen Lichtblick könnte z.B. das AT-Project1
zukünftig bieten, das solche projektleitungsorientierten Aufgabengebiete im Rahmen von
Eclipse unterstützt. Da sich das Tool jedoch noch in einer sehr frühen Entwicklungsphase
befindet, wird es im Folgenden nicht vorgestellt.

1. http://www.atreides-technologies.com/

122
eclipse_v01.book Seite 123 Montag, 30. Januar 2006 12:02 12

4 – Qualitätssicherung in Eclipse

4.2.4 Begrenzte Sourcecode-Qualität


Als Beispiel sei hier zunächst von einem 6000 Zeilen umfassenden BASIC-Programm be-
richtet. Dieses besaß, soweit erkennbar, nur einen gravierenden Bug. Nach acht Stunden
Suche wurde der Fehler vom Entwickler gefunden. Es war eine unglücklich benannte Me-
thode, die der Einfachheit halber nur „C“ genannt worden war und die sich (wie in dama-
liger Basic-Manier gern üblich) neben mehreren anderen Methodenaufrufen auch noch in
einer Zeile wiederfand.
Hieran ist zu erkennen, dass die Qualität des Sourcecodes und der Dokumentation eine we-
sentliche Rolle bei der Arbeit mit dem Quellcode spielen. Niemand liest gerne lange An-
leitungen, aber dort, wo der Sourcecode so unleserlich erscheint, bleibt außer Debuggen oft
keine Möglichkeit, zum Kern von Problemen und Lösungen vorzustoßen.
Während der Einzelprogrammierer hofft, dass er in zwei, drei Monaten sein Programm
noch versteht, ihm einleuchtet, was die Variablen bedeuten und warum hier ein IF die
Schleife mit einem BREAK verlässt, kann ein solches Handeln im Team zu massiven Ver-
zögerungen in der Projektarbeit und bei der Wartung führen.
Die Qualität des Sourcecodes und der Dokumentation kann sehr leicht durch die Anwen-
dung von Reviews erhöht werden.

4.2.5 Reviews und Softwareunterstützung


Als gutes Mittel zur Qualitätssicherung sind Reviews auf mehreren Ebenen möglich, z.B.:
1. Auf der Ebene der fachlichen Anforderungen (sind diese vollständig und spezifisch ge-
nug, um ihr Problemfeld abzugrenzen?). Hier kann Software allerdings wenig Hilfestel-
lung bieten.
2. Auf der Ebene der Architektur und des Softwaredesigns (Verteilung der Komponenten,
Klassen, Pattern-Anwendung). An dieser Stelle empfiehlt sich die Verwendung von
Reverse Engineering, das heißt der Übertragung des Sourcecodes in visuelle Notatio-
nen, um einen schnellen Einblick in die Strukturen der Software zu gewinnen (siehe Ka-
pitel 3).
3. Auf der Ebene der erstellten Dokumentation (Artefakte). Es gibt zahlreiche Eclipse-
Plug-Ins, die Dokumentationen unterschiedlicher Natur zusammenführen oder aus
Sourcecodedokumenten eigene Ergebnisartefakte erstellen. Die Anwendung ist aber si-
cherlich sehr projektindividuell.
4. Auf der Ebene des Sourcecodes. Die Qualität kann z.B. durch entsprechende Restrik-
tionen erreicht werden (wie lang ist eine Zeile, wie sind Namenskonventionen, welche
Elemente muss ein Javadoc enthalten?). Dafür werden Tools verwendet, die den
Sourcecode prüfen (z.B. Checkstyle1), oder es werden die Restriktionen des Compilers
bereits entsprechend eingestellt, so dass dieser im Build-Prozess entsprechende Errors
oder Warnings auswirft. Eine andere Möglichkeit ist die Visualisierung der Sourcecode-
Qualität durch die Anwendung von Audits und Metriken. Beides wollen wir uns mittels

1. http://checkstyle.sourceforge.net/

123
eclipse_v01.book Seite 124 Montag, 30. Januar 2006 12:02 12

Eclipse zur Qualitätssicherung

entsprechender Tools erzeugen lassen. Auch die Prüfung der Sourcecode-Abdeckung


(wie viel von meinem Sourcecode wird überhaupt verwendet und wie viel ist toter
Code?) führt oft zu sehr interessanten Interpretationen.
Die beiden Themen Testen und Reviews werden oft etwas vernachlässigt und spielen in vie-
len Projektverläufen (und Büchern) zu Unrecht eine sehr untergeordnete Rolle. Aber gera-
de Produktqualität ist ein ganz wesentlicher Marketing- und Erfolgsfaktor bei der Soft-
wareerstellung.

4.3 Eclipse zur Qualitätssicherung


Eine recht gute Qualität lässt sich erreichen, wenn wir bereits während des Verlaufs der
Softwareentwicklung Techniken zu deren Sicherung anwenden und nicht erst dann, wenn
es zu spät ist. Eclipse bietet aufgrund der Refactoring- und Modellierungsmöglichkeiten so-
wie der Anwendung von Sourcecode-Stilrichtlinien und JUnit-Tests die Möglichkeit, qua-
litätssichernde Maßnahmen schon bei der Entwicklung vor allem im Team durchzusetzen
und nicht erst in der Endphase oder nach dem Projekt.

4.3.1 Testen mit JUnit


Der technische Einsatz eines JUnit-Tests ist nicht sehr schwierig. Ein Beispiel dafür findet
sich im Einführungskapitel 2 dieses Buchs. Schwieriger erscheint die Frage, wann und wo
JUnit-Tests verwendet werden sollen. JUnit eignet sich besonders gut für das Testen von
Klassen und deren Methoden. Es gibt eine sehr tiefe und feingranulare Ebene, aber je weiter
der Komponentenbegriff gespannt wird (Packages, Modules/Components, Application, ...),
desto mehr Implikationen mit bestimmten Testdaten gibt es und die Menge der Testfälle
vervielfacht sich.
Der Grund dafür findet sich an zwei Stellen:
1. Datenkonstellationen sind oft nur schwer reproduzierbar. Das erschwert die Möglich-
keit, JUnit im Deployment-Prozess z.B. im Ant-Build für komplexe Tests einzusetzen.
2. Beim Design der Komponenten wurden die Schnittstellen zu anderen Komponenten
möglicherweise überfrachtet. Selbstverständlich kann jede Komponente interne Fehler
haben, aber ein JUnit-Test wird normalerweise nur über eine Schnittstelle (Interface
bzw. public-Methoden von Klassen) angewendet. Je mehr public-Methoden existie-
ren, desto länger dauert das Erfinden von Testmethoden und Szenarien. Das heißt, ist
das Design einer Komponente ungünstig gewählt, kann der JUnit-Test zu einer schein-
bar unendlichen Prozedur der Definition von möglichen Testfällen führen.

Es sollten wiederholbare JUnit-Tests für Klassen bzw. Interfaces geschrie-


ben werden, die wiederum auch von anderen benutzt oder aufgerufen
werden sollten. Am Ende in der Kundentestphase oder auch schon vorher
kann für die Fehler, die als Bugs identifiziert wurden, oft schnell ein JUnit-

Test, der den Bug wiederholbar erzeugt, geschrieben werden. Erst dann
sollte der Fehler korrigiert und der Fehlertest in das Build-Skript integriert
werden.

124
eclipse_v01.book Seite 125 Montag, 30. Januar 2006 12:02 12

4 – Qualitätssicherung in Eclipse

Die Unterstützung für JUnit sowie für das Ant-Build-Tool wird, wie bereits erwähnt, durch
Eclipse mitgeliefert.

4.3.2 Audits mit Checkstyle


Ein recht bekanntes Plug-In zur Prüfung von Sourcecode gegen entsprechende Richtlinien
heißt Checkstyle. Checkstyle1 selbst ist aber ein Standalone-Produkt, das sich u.a. sehr
leicht in einen Ant-Build-Prozess integrieren lässt. Für die Einbindung in IDEs gibt es ent-
sprechende Plug-Ins. Eines davon wollen wir uns an dieser Stelle näher ansehen.
Als Plug-In verwenden wir im Beispiel das Atlassw Eclipse Checkstyle2 für Checkstyle
(hier in Version 4.0.0 beta 6).
Nach dem Download der Datei und dem Entpacken ins Plug-Ins-Unterverzeichnis von
Eclipse starten wir Eclipse neu, um es nutzen zu können.
Checkstyle integriert in die Preferences von Eclipse (Menü WINDOW | PREFERENCES) eine ei-
gene Einstellungsseite (siehe Abb. 4.1), in der Checkstyle-Konfigurationen definiert wer-
den können. Das ist die Menge der Regeln/Regelprüfungen, die auf einen Satz von Java-
Dateien angewandt werden sollen.

Abb. 4.1: Checkstyle Preferences Page

1. http://checkstyle.sourceforge.net/
2. http://sourceforge.net/projects/eclipse-cs

125
eclipse_v01.book Seite 126 Montag, 30. Januar 2006 12:02 12

Eclipse zur Qualitätssicherung

In der Abbildung sehen wir zudem, dass mehrere Konfigurationen gleichzeitig angelegt
und konfiguriert werden können und in welchen Projekten derzeit die Checkstyle-Prüfun-
gen aktiv sind. Als Beispiel- und gleichzeitige Default-Einstellung sind die recht restrikti-
ven, aber allgemein anerkannten Sun Code Conventions1 standardmäßig hinterlegt.
Über den NEU-Button lassen sich weitere Konfigurationen anlegen bzw. über den KONFIGU-
RIEREN-Button modifizieren. Im Dialog werden unter einem logisch eindeutigen Namen die
Regeln und der Schweregrad des Bruchs der jeweiligen Regel erfasst (siehe Abb. 4.2).

Abb. 4.2: Checkstyle Check Configuration Editor

Regeln können hinzugefügt, geändert und gelöscht werden, wobei die jeweilige Art der Er-
fassung der Regeleigenschaften im Wesentlichen vom Regeltyp bestimmt wird, was die
Freiheit für neue Regeln wiederum einschließt. Da die Sun Code Conventions bereits sehr
breitgefächerte Regeln bereitstellen, stellt sich die Frage, welche dieser Regeln wie streng
gelten sollen.
Unter die Themenbereiche fallen Javadoc Kommentarverwendung, Namenskonventionen,
Header- und Importvorgaben, Verletzungen von Größenbeschränkungen, Whitespace-Ver-
wendung, Anwendung von Modifiern, Konventionen für das Schreiben von geklammerten
Codeblöcken, schwierige Codestellen (verdeckte Variablen, komplexe Zuweisungen und

1. http://java.sun.com/docs/codeconv/

126
eclipse_v01.book Seite 127 Montag, 30. Januar 2006 12:02 12

4 – Qualitätssicherung in Eclipse

Prüfungen), Klassendesign (Interfaces, Final Classes, Sichtbarkeit), J2EE-Prüfungen (EJB-


Anwendungen, Returntypes, Exceptions) und Usage (unbenutzte Variablen, einmalige Zu-
weisungen), um nur einzelne Beispiele zu nennen.
Die erfassten Regelkonfigurationen lassen sich über die Preferences Page von Checkstyle
als interne oder externe XML-Konfigurationsdateien speichern und so auch in Eclipse-In-
stallationen anderer Projektmitglieder ohne Probleme erneut laden.
Das Checkstyle-Plug-In liefert zu den Regeln die recht ausführliche Checkstyle-Dokumen-
tation der bereits verfügbaren Regeln mit, die zum besseren Verständnis um eigene Kom-
mentare in Eclipse ergänzt werden können.
Nach der Zusammenstellung einer entsprechenden Konfiguration von Regeln wechselt
man in Eclipse in das auf diese Regeln zu prüfende Java-Projekt bzw. nach Selektion im
Package Explorer über die rechte Maustaste zu dessen Properties. In den Properties des
Projekts findet sich nun auch ein Unterpunkt mit der Bezeichnung Checkstyle, in welchem
die Konfigurationen mit zu definierenden „Datei-Sets“ assoziiert werden können. Im Bei-
spiel in Abb. 4.3 ist dies mit dem aus Kapitel 2 bekannten Jakarta ORO-Projekt unter Ver-
wendung der Sun Conventions getan worden.
Mehrere dieser Zuordnungen können parallel für ein Projekt erfasst und aktiviert oder
deaktiviert werden. Die Erstellung der Datei-Sets erfolgt über HINZUFÜGEN bzw. EDITIEREN
in einem eigenen DATEI-SET EDITOR.

Abb. 4.3: Zuordnung von Checkstyle Configuration zu Datei-Sets

Checkstyle bindet sich in den Compile und den Build-Prozess von Eclipse ein. Das kann bei
einem langsamen Rechner oder einer großen Dateimenge durchaus zu Verzögerungen in
der Arbeit führen. Daher sollte genau überlegt werden, welche Regeln wirklich sinnvoll

127
eclipse_v01.book Seite 128 Montag, 30. Januar 2006 12:02 12

Eclipse zur Qualitätssicherung

sind, da jede Einzelprüfung selbstverständlich einen Teil der Compile-/Build-Zeit in An-


spruch nimmt, und wie viele Verletzungen parallel geprüft werden sollen.
Nach Durchlauf des Build finden sich die Ergebnisse in der Problems View von Eclipse
wieder. Hierfür klicken wir auf das Toolbar-Icon für Filters (die drei nach rechts zeigenden
Pfeile) und wählen die Checkbox für Checkstyle an. Unter Umständen muss die Menge der
gleichzeitig anzeigbaren Fehler erhöht werden, weil (wie hier im Beispiel von Jakarta
ORO, siehe Abb. 4.4) das Plug-In über 6800 Verstöße gegen die Konventionen gefunden
hat. Dies zeigt deutlich, dass ein gewissenhafter Umgang mit dem Thema Stilrichtlinien
notwendig ist, auch um die Produktivität in der Teamarbeit nicht unnötig durch Konven-
tionen zu mindern und gleichzeitig die Sourcecode-Qualität bereits bei der Erstellung zu
sichern.
Bereits seit Eclipse 3.0 verfügt Eclipse über einen auf die Sun Code Conventions abge-
stimmten Formatter, der auch komplexere Formatierungsregeln unterstützt, so dass die
manuelle Nachpflege des Sourcecodes für die Einhaltung von Richtlinien weiter abnehmen
sollte.

Abb. 4.4: Checkstyle-Ergebnisse in der Problems View

4.3.3 Sourcecode-Metrics
Der Übergang von Stilrichtlinien, wie wir sie in Abschnitt 4.3.2 gesehen haben, zu Metri-
ken ist fließend. Während eine Sourcecode-Konvention vorgeben könnte, dass eine Klasse
nicht mehr als 1000 Zeilen Sourcecode besitzen darf, versuchen Metriken auf etwas ab-
strakterer Ebene die Sourcecode-Beschaffenheit in Zahlenmaterial greifbar zu machen,
allerdings auch unter Anwendung von Grenzen und Schwellenwerten, bei deren Über-
schreitung das Tool entsprechende Nachrichten hinterlässt.

128
eclipse_v01.book Seite 129 Montag, 30. Januar 2006 12:02 12

4 – Qualitätssicherung in Eclipse

4.3.4 Metrics 1.3.6


Ein frei verfügbares Tool zur Analyse des Sourcecodes ist Metrics von Frank Sauer, das
zum Erstellungszeitpunkt dieser Dokumentation in der Version 1.3.6 für Eclipse 3.1 zur
Verfügung stand. Das rund 1,8 MB große Programm kann von seiner Sourceforge-Herstel-
lerseite1 per automatischem Installations-Download heruntergeladen werden. Dazu müssen
wir in Eclipse in den Dialog HELP | SOFTWARE UPDATES | FIND AND INSTALL... wechseln und
als neue Remote-Seite die oben angegebene URL-Adresse eintragen. Nach Bestätigung
und Ausführung der Suche können wir das Produkt installieren und nach einem Neustart
der IDE ab jetzt in Eclipse 3.1 einsetzen.
Der Hersteller liefert eine kurze Hilfe in HTML-Form als Integration in die Eclipse-Hilfe
mit. Sie entspricht derzeit exakt dem Inhalt der Webseite, auf der dieses Programm vertrie-
ben wird. Die meisten Funktionen erschließen sich allerdings schnell auch von selbst. Der
Autor hat dabei sein auf dem CPL 1.0-Lizenzmodell basierendes Produkt u.a. mit der
Touchgraph2-Visualisierung ergänzt, was – wie wir später noch sehen werden – die Abhän-
gigkeiten zwischen einzelnen Klassen und Klassenverbänden sehr schön deutlich macht.
Nach der Installation findet sich in den Eclipse Preferences (Menü WINDOW | PREFERENCES)
ein Unterpunkt zum Thema METRICS PREFERENCES (siehe Abb. 4.5).

Abb. 4.5: Preferences-Metrics-Complexity

1. http://metrics.sourceforge.net/update
2. http://www.touchgraph.com/

129
eclipse_v01.book Seite 130 Montag, 30. Januar 2006 12:02 12

Eclipse zur Qualitätssicherung

Wir finden dort eine ganze Reihe von Wertparametern, die im Hintergrund in der Metrik-
datenbank zu den jeweiligen Projekten bzw. Klassen ausgewertet und gespeichert werden.
Die Erzeugung und Ermittlung der notwendigen Metrikdaten erfolgt gemeinhin nach dem
Compile.
Hierfür integriert das Tool einen eigenen METRICS BUILDER, der beim Build des Projekts
angesprochen wird. Wir aktivieren ihn, indem wir das zu vermessende Java-Projekt im
PACKAGE EXPLORER auswählen und seine Eigenschaften (RECHTE MAUSTASTE | PROPERTIES)
aufrufen.
In dem Baumpunkt METRICS findet sich die dazu notwendige Aktivierungscheckbox. Sie er-
gänzt den in Abb. 4.6. zu sehenden METRICS BUILDER im Projekt, der im Anschluss an den
JAVA BUILDER zum Einsatz kommt.
Grundsätzlich ist Metrics nur in der Lage, kompilierte Java-Sourcen in Java-Projekten in
Eclipse zu vermessen. Eine Unterstützung für weitere Programmiersprachen finden wir
hier in der aktuellen Version nicht.

Abb. 4.6: Projekteigenschaften eines gemessenen Projekts

Kommen wir zurück zur Liste der Messwerte, die Metrics in einem entsprechenden Source-
code veranschlagt und prüft. Sie kann für die Reporterstellung der Metriken beliebig um-
sortiert werden. Tabelle 4.1 zeigt die Abkürzungen und eine kurze Erläuterung ihrer Bedeu-
tung1.

Tabelle 4.1: Metrikwerte aus Metrics

Abkürzung Name Bedeutung

CA Afferent Coupling Anzahl der Klassen außerhalb eines Package, die


auf Klassen innerhalb eines Package verweisen.

CE Efferent Coupling Gegenteil von CA: Anzahl der Klassen innerhalb


eines Package, die auf andere außerhalb verwei-
sen. Die Werte von CA und CE kennzeichnen die
Geschlossenheit und Kapselung eines Package
gegenüber Einflüssen von außen und umgekehrt.

1. auch nochmal nachzulesen in der dem Produkt beiliegenden Hilfedokumentation

130
eclipse_v01.book Seite 131 Montag, 30. Januar 2006 12:02 12

4 – Qualitätssicherung in Eclipse

Tabelle 4.1: Metrikwerte aus Metrics (Forts.)

Abkürzung Name Bedeutung

DIT Depth of Inheritance Beschreibt die Anzahl der Vererbungslevel zwi-


Tree schen der aktuellen Klasse und java.lang.
Object.

LCOM* Lack of Cohesion Misst den Zusammenhalt einer Klasse nach der so
of Methods genannten Henderson-Sellors-Methode.

MLOC Method lines of Summe aller nicht leeren und Nicht-Kommentar-


code Zeilen innerhalb der Methodenblöcke.

NOC Number of Classes Gesamtzahl aller Klassen innerhalb des gewählten


Sichtbarkeitsbereichs.

NOF Number of Fields/ Gesamtzahl der Felder innerhalb des gewählten


Attributes Bereichs.

NOI Number of Gesamtzahl der Interfaces innerhalb des gewähl-


Interfaces ten Bereichs.

NOM Number of Methods Gesamtzahl der Methoden innerhalb des gewähl-


ten Bereichs.

NORM Number of Over- Gesamtzahl der von Elternteilen überschriebenen


ridden Methods Methoden innerhalb des gewählten Bereichs.

NSC Number of Children Gesamtzahl der direkten Sub-/Implementierungs-


klassen einer Klasse/von Interfaces.

RMA Abstractness Anzahl der abstrakten Typen (Klassen und Inter-


faces) geteilt durch die Anzahl aller Typen des
Package.

RMD Normalized Dis- | RMA + RMI - 1 | : Messwert, der bei gutem


tance from Main Package-Design nahe null liegen sollte.
Sequence

RMI Instability CE / (CA + CE)

TLOC Total lines of code Gesamtzahl der Codezeilen eines Typs ohne leere
und Kommentarzeilen.

VG McCabe Cyclo- Zyklomatische Komplexität nach McCabe, die die


matic Complexity Anzahl der potenziell unterschiedlichen möglichen
Programmflüsse und Äste innerhalb eines Source-
code-Teils bewertet.

WMC Weighted Methods Summe aller VG-Werte für alle Methoden einer
per Class Klasse.

131
eclipse_v01.book Seite 132 Montag, 30. Januar 2006 12:02 12

Eclipse zur Qualitätssicherung

Hier ist bereits zu erkennen, welche Arten von Prüfungen uns im Bereich der Metrikanalyse
begegnen. Sollte dies noch nicht ausreichen, empfehle ich z.B. einen Blick auf die IBM
Alphaworks-Seite und das dort zu findende Produkt „Structural Analysis for Java“.
Neben der Einstellung der Farben, die Metrics verwenden soll und einer Konfigurierbarkeit
der LCOM*- und NORM-Wertermittlung erlauben es die Preferences von Metrics auch,
die ermittelten Metrikwerte als Warnungen in den Problems auszuweisen.

Abb. 4.7: Schwellenwerte für Problemanzeige

Dies ist recht angenehm, da man sich die Suche in den unterschiedlichen Tabellen erspart,
die oftmals nur dann erfolgt, wenn es gerade nötig ist oder Zeit vorhanden war.
Abb. 4.7 zeigt hier die Einstellungstabelle für die verschiedenen Metrikwerte. Ob man sie
intensiv verwendet, dürfte davon abhängen, welche Bedeutung man den Zahlen selbst zu-
weisen möchte.
Die erzeugten Daten können schließlich zur weiteren Verarbeitung (z.B. als Entwicklungs-
statistik eines Programms in einem Refactoring-Prozess oder innerhalb der verschiedenen
Entwicklungsstadien) als XML-Report gespeichert werden. Wie diese Speicherung erfol-
gen soll, kann ebenfalls in den Preferences des Produkts konfiguriert werden.

132
eclipse_v01.book Seite 133 Montag, 30. Januar 2006 12:02 12

4 – Qualitätssicherung in Eclipse

4.3.5 Metrikreports und Graphen


Kommen wir nun von den Einstellungen des Programms zu deren konkreter Anwendung
für ein Java-Projekt in Eclipse.
Die Nutzung für ein Java-Projekt kann – wie bereits beschrieben – erfolgen, sobald die
entsprechende Funktion in den Projekteigenschaften aktiviert wurde. Die Anzeige der
zugehörigen Metrikwerte erfolgt über eigens dafür konzipierte Views. Sie sind über das
Menü WINDOW | SHOW VIEW | OTHER... im Baum unter dem Punkt METRICS zu finden (siehe
Abb. 4.8).

Abb. 4.8: Metrics Views

Bei Metrics 1.3.6 zeigten sich bei mir hin und wieder kleinere Refresh-Pro-
bleme beim Anzeigen der Tabellenstatistiken. Sie ließen sich durch einen
Workaround und den Wechsel in die Projekteigenschaften und zurück
allerdings auch schnell beheben.

Metrics bietet zwei Ansichten, jeweils für den Überblick auf reiner Klassen- und einmal für
die Ansicht auf Package-Ebene. Hierfür finden sich jeweils eine Tabelle und ein zugehöri-
ger Graph. In der Abb. 4.9 können wir die dafür aufklappbaren Tabellenelemente anhand
des Beispiels von Jakarta ORO sehen.
Jede Tabellenview besitzt ein Graph-Icon, um auf Visualisierungssicht umzuschalten. Hier
ist dann sehr gut zu erkennen, welche Abhängigkeiten zwischen den einzelnen Klassen be-
stehen. Durch Doppelklicken auf den jeweiligen Klassennamen wandelt sich der Graph und
konzentriert sich auf das angewählte Element und dessen referenzierte Objekte (siehe Abb.
4.10).
Ebenfalls über die Tabellenview bietet Metrics auch den besagten Export der XML-Daten
an. Abb. 4.11 zeigt einen Ausschnitt des exportierten XML.

133
eclipse_v01.book Seite 134 Montag, 30. Januar 2006 12:02 12

Eclipse zur Qualitätssicherung

Abb. 4.9: Metrik-Analysetabellen

Abb. 4.10: Abhängigkeitsgraph in Metrics

Abb. 4.11: Beispiel-XML aus Metrics

134
eclipse_v01.book Seite 135 Montag, 30. Januar 2006 12:02 12

4 – Qualitätssicherung in Eclipse

Leider ist ein Import bestehender Daten hier nicht vorgesehen, auch lassen sich die erzeug-
ten Graphen nicht per Produkt speichern.

4.3.6 Ant-Integration
Metrics bietet schließlich neben der Integration als Eclipse-Plug-In auch eine Unterstüt-
zung für das Buildtool Ant an. Eine ant-metricstask.jar-Datei hilft hier, das Produkt in den
Compile-Prozess einzubinden.
Die Ausgabe der erzeugten Metrikdaten erfolgt hierbei ähnlich wie in der reinen Eclipse-
Integration durch XMLs. Wer also in Nightbuild-Prozesse oder zur weiteren Verarbeitung
in Webseiten derartige Daten automatisch sammeln möchte, kann die Erstellung leicht au-
tomatisieren.
Für weiterführende Informationen und Beispiel-Ant-Task-Aufrufe ist ein Blick in die mit-
gelieferte Dokumentation empfehlenswert.

4.3.7 Bleiben Sie kritisch


Bei der Metrikanalyse gilt der berühmte alte Satz aller Statistiken: „Trauen Sie keiner Sta-
tistik, die Sie nicht selbst gefälscht haben“.
Wer sich eine Weile intensiv mit Java, Java-Architekturen und Lösungsvarianten beschäf-
tigt hat, wird bemerken, dass es unendlich viele Lösungsmöglichkeiten zu geben scheint,
dass aber auch immer gleichartige Probleme dabei auftauchen.
Größtes Hindernis ist oft: Programme sind zu komplex und zu schwer zu verstehen.
Oder andersherum formuliert: Software mit weniger Sourcecode enthält potenziell weniger
Fehler. Ob wir einen solchen Kandidaten vor uns haben, lässt sich bereits schnell nach einer
Sichtung der Lines of Code ermitteln. Stellen wir noch die Anzahl der Vererbungshierar-
chieebenen, die Anzahl der Methoden und z.B. die zyklomatische Komplexität daneben,
wissen wir schnell, in welchen Bereichen der Software das Risiko von Fehlern am höchsten
ist. In einem Architekturbuch las ich einmal Folgendes: Fehler zeigen ein gruppendynami-
sches Verhalten. Dort wo viele auftauchen, finden sich zumeist noch mehr. Also wer eine
Aussage über die Qualität einer Softwarekomponente haben will, sollte vielleicht statt in
ein Metriktool hin und wieder mal in das zugehörige Bugtracking-Produkt schauen.
Zweites Problem: Mit fortschreitender Entwicklung werden Dokumentation und Test po-
tenziell vernachlässigt. Dies trifft besonders auf die Maintenance- (also die Betriebs-)Phase
zu, in der oft nicht mehr die ursprünglichen Entwickler fremden Source und fremde Mo-
delle ändern und anpassen müssen oder sollen. Das Verhältnis zwischen echten Lines of
Code und reiner Java-Klassenlänge lässt einen ganz guten Rückschluss darauf zu, wie viel
prozentual dokumentiert wurde. Aber Vorsicht, das sagt nicht aus, ob hier gut, angemessen
oder primitiv dokumentiert wurde. Es sagt auch nichts darüber aus, ob der Sourcecode
selbst sprechende Variablen enthält, die den Sinn der dort hinterlegten Logik gegebenen-
falls auch ohne expliziten Javadoc verständlich machen. Ebenfalls wichtig: Die Dokumen-
tation öffentlicher Schnittstellen ist gemeinhin wichtiger als die privater Klassen und

135
eclipse_v01.book Seite 136 Montag, 30. Januar 2006 12:02 12

Eclipse zur Qualitätssicherung

Methoden. Derartiges kann von einem Metriktool nicht oder nur kaum messbar gemacht
werden.
Drittes Problem: die so genannte „Hegemonie der dominanten Dekomposition“. Diesen
Begriff hat man vielleicht schon einmal gehört, wenn man sich mit MDSOC (multi-dimen-
sional separation of concerns) oder aspektorientierter Programmierung beschäftigt hat. Es
geht um die Fragestellung, ob es möglich ist, eine Aufgabe oder einen Logikteil immer ein-
deutig einer bestimmten Sourcecode-Stelle (Klasse, Interface oder Methode) zuzuordnen.
Was hat das nun mit Metriken zu tun?
Es gibt Metriken, die den Zusammenhalt von Packages und deren Klassen bewerten, die
prüfen, ob es zirkuläre Abhängigkeiten oder Beziehungen gibt, die allgemein als uner-
wünscht und verwerflich erachtet werden. In späteren Phasen eines Projekts oder bei zu-
nehmender Größe einer Anwendung nimmt das Problem zu, ohne größere Refactoring-
Aufwände eine klare Aufgabentrennung und Bestimmung beizubehalten. Dass dies so
schwierig ist, liegt auch an der Java-Sprache an sich.
Um die Metrikwerte korrekt bzw. aussagekräftig bewerten zu können, müssen wir also das
Gesamtbild der Anwendung im Auge behalten. Welche Architektur haben wir, wie alt ist
die Software, in welcher Teamkonstellation ist sie entstanden, wie viel Sourcecode wird
automatisch generiert und nicht geschrieben, welches Ziel verfolgen wir mit der Analyse
dieser Zahlen überhaupt?
Metriken haben den Charme, PowerPoint-Folien mit Qualitätsbalken füllen zu können und
damit die echten Probleme von Projekt und Sourcecode gleichzeitig zu verschleiern. Ich
hatte selbst beispielsweise in einigen meiner entwickelten Komponenten einen sehr hohen
Komplexitätswert von einem Metriktool ausgewiesen bekommen, bis man mich darauf hin-
wies, man könne das Tool sehr gut austricksen, indem man viele öffentliche Getter- und
Setter-Methoden erzeugt. Unnützer Sourcecode, der nie verwendet würde, aber bei Ma-
nagementpräsentationen gut ankäme.

136
eclipse_v01.book Seite 137 Montag, 30. Januar 2006 12:02 12

5 Build Management mit Eclipse

von Herbert Brückner

5.1 Aufgaben des Build Managements


Der Begriff Build Management wird in sehr unterschiedlichen Ausprägungen verwendet
und beschreibt meist Aufgaben in den Bereichen Kompilierung und Auslieferung von Re-
leases und Patches. Weiter ist er eng verknüpft mit den Themen Versionsverwaltung und
Konfigurationsmanagement. Wir wollen hier eine typische Situation und Fragestellungen
aufzeichnen, die im Rahmen des Build Managements behandelt werden. Dabei wird der
Begriff etwas weiter gefasst als der Begriff des eigentlichen Build, welcher häufig als Syn-
onym für ein intelligentes Kompilieren des Projekts oder Teile hiervon gesehen wird.
Nahezu jede professionelle Softwareentwicklung wird sich mit dem Build Management be-
schäftigen müssen, sobald die entwickelte Software auf einem anderen System betrieben
wird als dem, auf dem es entwickelt wurde. Üblicherweise findet schon die Softwareent-
wicklung auf mehreren Systemen statt: Auf einer oder mehreren Entwicklungsumgebun-
gen und Testumgebungen (z.B. je eine Umgebung pro Betriebssystem) oder auf einer oder
mehreren produktiv eingesetzten Umgebungen. Hieraus ergeben sich verschiedene Fragen:
쐌 Wie macht man aus Quelldateien und Konfigurationsdaten ein Stück Software? (Build)
쐌 Wie bringt man das Stück Software X auf die Umgebung Y? (Deployment)
쐌 Wie stellt man sicher, dass die Software X auch auf der Umgebung Y läuft? (Tests, Ver-
sionierung)
쐌 Was ist zu tun, wenn man die Software auf mehrere Systeme Y1, Y2, Y3 ... bringen
muss? (Build- und Konfigurationsmanagement)
쐌 Man will die Umgebung Y so aufbauen, wie sie am 03.12.1994 betrieben wurde. Wie
geht das? (Release-Management, Versionsverwaltung, Betrieb)
Wir wollen nun einen Vorschlag zur Struktur und den Prozessen im Build Management ma-
chen und aufzeigen, wie Eclipse dies technisch unterstützt.
Zunächst wird auf die direkt in Eclipse vorhandenen Hilfen und Werkzeuge eingegangen,
die insbesondere den Build-Prozess bei Java-Entwicklungen unterstützen. Im Anschluss
werden weitere Tools und Konzepte vorgestellt, die sich auf komplexere Prozesse beziehen,
und deren Unterstützung dargelegt. Dies zeigt auf, wie die bereits beschriebenen Funktio-
nen integriert genutzt werden können.
Zunächst noch zwei grundsätzliche Hinweise:
쐌 Sehr viele Arbeitsschritte im Build Management sind Standardschritte, die sich regel-
mäßig wiederholen. Sofern sie über die mitgelieferten automatischen Funktionen hin-
ausgehen, sollte man deshalb frühzeitig überlegen, ob und wie diese Schritte automati-
siert ausgeführt werden können!
쐌 Nachvollziehbarkeit ist oberstes Gebot! Es sollte immer klar sein, wie ein Stück Soft-
ware erstellt, eine Umgebung installiert und konfiguriert ist und wie dieser Zustand er-
reicht wurde.

137
eclipse_v01.book Seite 138 Montag, 30. Januar 2006 12:02 12

Vom Quellcode zum Programm: Build

Das hier vorgestellte Build Management System baut auf drei Komponenten auf:
1. Versionsverwaltung: Diese wird im Wesentlichen durch das CVS-System unterstützt.
Neben der Versionierung einzelner Dateien wird hier festgelegt, was zu einem „Paket“
(z.B. volles Release, Patch, Zusatzkomponenten) gehört, welches ausgeliefert werden
kann. In einem Exkurs wird die Einbindung von CVS in Eclipse dargestellt.
2. Konfiguration, Auslieferung und Testen: Hier kommen CVS, Ant1 und das automati-
sche Testen gemeinsam zum Einsatz. Das Build Management-Werkzeug Ant wird The-
ma eines zweiten Exkurses sein.
3. Dokumentation: Hier kommen Ant- und Eclipse-Features zum Einsatz. Es wird mitge-
schrieben, welche Pakete auf welcher Umgebung wie installiert wurden.

5.2 Vom Quellcode zum Programm: Build


Insbesondere in der Java-Programmierung unterstützt Eclipse die Arbeit durch automa-
tisches oder manuelles Kompilieren (Build) des Quellcodes. In Eclipse beziehen sich die
eingebauten Build-Funktionen auf den Kernbereich des Kompilierens und des Ablegens in
einem Zielbereich (Deployment/Launch) sowie der automatischen Erstellung der Doku-
mentation mittels JavaDoc.
Eclipse übernimmt hierbei die Prüfung, ob ein Kompilieren notwendig ist, ob Abhängig-
keiten zu anderen Dateien zu beachten sind und wie das Ergebnis abgelegt und eingebun-
den wird.
Es werden zwei Arten unterschieden:
쐌 Das komplette Build löst ein Löschen der vorherigen Kompilate aus und baut alle in
Frage kommenden Ergebnisse (z.B. die .class-Dateien) aus den Quelldateien neu auf.
Die Abhängigkeiten untereinander werden automatisch in der Reihenfolge des Kompi-
lierens berücksichtigt, können aber auch explizit verändert werden.
쐌 Das inkrementelle (teilweise) Build bearbeitet nur veränderte Dateien und solche, die
davon abhängig sind. Veränderungen des Codes werden also in der Regel auch in Da-
teien berücksichtigt, die selbst nicht angefasst wurden. Allerdings ist nicht völlig aus-
zuschließen, dass eine Abhängigkeit nicht erkannt wurde und somit das Kompilat in
sich inkonsistent geworden ist.
Beide Arten können entweder auf den gesamten Arbeitsbereich angewendet werden oder
auf eine bestimmte Teilmenge von Projekten. Die Vor- und Nachteile der beiden Arten lie-
gen auf der Hand: Während durch ein komplettes Build sichergestellt ist, dass die Abhän-
gigkeiten der Quellen erfüllt sind, hat das inkrementelle Build erheblich geringere Ausfüh-
rungszeiten.
Eclipse führt daher automatisch ein inkrementelles Build aus, wenn eine Datei gespeichert
wird, während ein komplettes Build manuell angestoßen werden muss. Die Modi, ob und
wann ein Build automatisch ausgeführt wird, sind konfigurierbar. Es kann z.B. sinnvoll
sein, das automatische Build abzuschalten, wenn eine große Anzahl von zusammenhängen-

1. vgl. http://ant.apache.org

138
eclipse_v01.book Seite 139 Montag, 30. Januar 2006 12:02 12

5 – Build Management mit Eclipse

den Änderungen durchzuführen sind und man sich die Fehlermeldungen in den Zwischen-
stadien ersparen möchte. Der mögliche Preis ist dann gegebenenfalls eine lange Liste von
Fehlermeldungen am Ende der Änderungen. Daher ist zu überlegen, ob sich der Komplex
von Änderungen nicht in kleine Abschnitte aufteilen lässt, die wieder eine kompilierfähige
Stufe darstellen.

5.3 Einstellungen des Eclipse-Build


Bevor ein Build ausgeführt werden kann, sind eine Reihe von Angaben notwendig, was al-
les dazugehören und aktualisiert werden soll. Diese Einstellungen werden in den Projekt-
eigenschaften vorgenommen und können auf verschiedene Arten geöffnet werden:
쐌 Reiter FILE | PROJECT PROPERTIES | JAVA BUILD PATH
쐌 (Bei aktivem Navigator-Fenster) Tastenkombination Ç-Æ
쐌 Im Navigator-Fenster durch rechte Maustaste auf PROJECT | PROPERTIES
In dem sich öffnenden Dialog findet man in der linken Navigationsleiste vier Einträge, die
für das Build von Interesse sind:
쐌 Unter BUILDERS kann man das Werkzeug bestimmen, mit dem das Build vorgenommen
werden soll. Neben dem internen Java Builder können hier auch externe (eigene) Tools
eingebunden und konfiguriert werden.
쐌 Unter JAVA BUILD PATH bestimmt man, welche Quelldateien, Projekte und Bibliotheken
verwendet werden sollen, und die Reihenfolge des Einbindens.
쐌 Unter JAVA COMPILER sind die Einstellungen bezüglich der Kompatibilität interessant.
Weiterhin kann hier festgelegt werden, wie mit speziellen Fehlersituationen umgegan-
gen werden soll.
쐌 Unter JAVADOC LOCATION schließlich kann man einstellen, was mit der Dokumentation
geschehen soll.

5.3.1 Build Path-Einstellungen


Beginnen wir mit dem zweiten Eintrag, der Einstellung des Build-Pfads. Diese Einstellun-
gen sind schon zu Projektbeginn erforderlich, wenn in dem Projekt externe Bibliotheken
verwendet werden, um z.B. Auto-Vervollständigung und die Syntaxprüfung nutzen zu kön-
nen. Die Einträge können praktisch jederzeit geändert werden, allerdings sollte dann neu
kompiliert werden und es können Fehler auftreten.
Nach Anklicken des Eintrags erscheinen im rechten Teil des Fensters vier Reiter: Der erste
trägt den Namen SOURCE. Hier können wie in Abb. 5.1 dargestellt die Quellverzeichnisse
und -dateien festgelegt werden:
Mit ADD FOLDER können wir aus dem Projekt-Verzeichnisbaum weitere Verzeichnisse aus-
wählen und dem Build-Pfad hinzufügen. Es öffnet sich ein Fenster mit einer Navigations-
leiste und dem ausgewählten Projekt. Hier können nun weitere Pakete (Verzeichnisse) hin-
zugefügt werden.

139
eclipse_v01.book Seite 140 Montag, 30. Januar 2006 12:02 12

Einstellungen des Eclipse-Build

Abb. 5.1: Hauptmenü der Einstellungen zum Build

Mit der Auswahl CREATE NEW FOLDER können Verzeichnisse aus dem Dateisystem ausge-
wählt und mit einem eigenen Namen in den Projektordner aufgenommen werden. Dabei ist
zu bemerken, dass dies kein Kopiervorgang ist. Die Verzeichnisse und Dateien bleiben an
ihrem ursprünglichen Ort liegen und werden nur in den Projektordner verlinkt.
Mit EDIT können wir durch den Projektbaum navigieren und Unterverzeichnisse für das
Build auswählen. Wenn wir das Verzeichnis aufklappen, erhalten wir einen Filter, mit dem
genauer eingegrenzt werden kann, welche Dateien für das Build relevant sind. Durch Aus-
wahl des Filtersymbols und Anklicken des EDIT-Buttons können wir die Filterkriterien de-
finieren, wobei mit Hilfe von regulären Ausdrücken (Wildcards) bestimmte Namensmuster
für Dateien und Verzeichnisse angegeben werden können. (Hier empfiehlt es sich, bereits
eine Namenskonvention für die Quelldateien verwendet zu haben, so dass hier einfache Fil-
ter gesetzt werden können.)
Schließlich kann mit REMOVE wieder ein Verzeichnis vom Build entfernt werden.
Neben der Auswahl der Quellverzeichnisse kann im unteren Teil des Fensters noch ein
Ausgabeverzeichnis bestimmt werden. Es wird ein Standardverzeichnis festgelegt, welches
innerhalb des Workspace angelegt wird. Durch den BROWSE-Button kann dieses auch direkt
im Dateisystem ausgewählt werden.
Alternativ lässt sich auch durch Setzen des Häkchens ALLOW OUTPUT FOLDERS FOR SOURCE
FOLDERS jedem Quellverzeichnis ein eigenes Ausgabeverzeichnis zuordnen. Beim Setzen

140
eclipse_v01.book Seite 141 Montag, 30. Januar 2006 12:02 12

5 – Build Management mit Eclipse

des Häkchens wird ein zusätzlicher Eintrag im Verzeichnisbaum generiert, diesem kann
wieder mit EDIT ein Verzeichnis zugeordnet werden.
Der zweite Reiter in diesem Fenster erlaubt die Auswahl von ganzen Projekten, die für den
Build-Pfad verwendet werden sollen. Falls mehrere Projekte vorhanden sind, werden alle
außer dem aktuell ausgewählten hier angezeigt und können ausgewählt bzw. ausgenommen
werden.
Der dritte Reiter erlaubt die Auswahl und das Einbinden von Bibliotheken und Verzeich-
nissen. Es werden die bereits eingebundenen .jar-Dateien und Bibliotheken angezeigt. Da-
neben befinden sich Buttons zum Anpassen dieser Liste:
Mit ADD JARS können eigene Pakete (die bereits in einem Projekt des Workspace erstellt
wurden) eingebunden werden.
Mit ADD EXTERNAL JARS können Pakete aus anderen Quellen ausgewählt werden. Es öffnet
sich jeweils ein Fenster mit einem Arbeitsverzeichnis. In diesem kann zu dem gewünschten
Paket navigiert werden. Voreingestellt sind die Dateitypen *.jar und *.zip. Wenn das ge-
wünschte Paket erscheint, kann es ausgewählt (auch eine Mehrfachauswahl ist möglich)
und die Auswahl mit OK bestätigt werden.
Mit ADD VARIABLE lassen sich die bestehenden Java-Variablen verändern oder neue definie-
ren. Diese Variablen werden an den Compiler übertragen z.B. um weitere Verzeichnisse für
Libraries oder Komponenten einzubinden. Es erscheint ein Fenster mit den bereits definier-
ten Variablen und deren Werten. Mit EDIT kann die Liste als ganzes bearbeitet werden, d.h.,
es können Variablen neu definiert, ihre Werte verändert oder ganz gelöscht werden. Einige
Variablen sind jedoch als „reserved“ gekennzeichnet, d.h., sie können nicht hier editiert
werden, sondern sie werden aus andere Parametern abgeleitet.
Mit EXTEND lassen sich Verzeichnisse auf Basis einer Java-Variablen angeben, mit CONFI-
GURE VARIABLES schließlich kann man neue Variablen erstellen, ihnen Dateien und Ver-
zeichnisse als Werte zuordnen oder bestehende Einträge wieder löschen. Abb. 5.2 zeigt die
verschiedenen Fenster zur Einrichtung und Bearbeitung von Variablen.
Durch ADD LIBRARY gelangen wir zu einem kleinen Wizard, durch den wir zum einen die
JRE-Bibliothek(en) festlegen und zum anderen notwendige Plug-Ins einbinden können.
Mit ADD CLASS FOLDER schließlich fügen wir weitere Verzeichnisse von .class-Dateien aus
Projekten hinzu.
Sobald wir einen Eintrag aus der Liste der Pakete und Bibliotheken ausgewählt haben, ste-
hen noch die Buttons EDIT und REMOVE zur Bearbeitung bzw. zum Löschen des entspre-
chenden Eintrags zur Verfügung.
Eng verbunden mit diesem Reiter ist der vierte Reiter, bei dem wir die Reihenfolge des Ein-
bindens bestimmen können. Es wird die Liste der Pakete und Bibliotheken angezeigt. Dies-
mal können wir jedoch durch Auswählen von einem (oder mehreren) Listenelement(en)
mit den Buttons UP und DOWN ihre Reihenfolge bestimmen: Je weiter oben ein Eintrag
steht, desto früher wird er eingebunden. Dies hat dieselbe Auswirkung wie die Reihenfolge
der Dateien in der Classpath Systemvariable. Wenn gleichnamige Klassen in der Liste vor-
kommen, wird die Klasse verwendet, die in dieser Reihenfolge zuerst aufgeführt ist.

141
eclipse_v01.book Seite 142 Montag, 30. Januar 2006 12:02 12

Einstellungen des Eclipse-Build

Abb. 5.2: Einstellung von Java-Variablen

Zusätzlich können wir durch Selektieren von Einträgen festlegen, welche hiervon expor-
tiert werden, d.h. in von dem aktuellen Projekt abhängigen Projekten zur Verfügung stehen
sollen.
Die Reihenfolge des gesamten Build, d.h. über mehrere Projekte hinweg, kann weiterhin
festgelegt werden über:
Menü WINDOW | PREFERENCES | BUILD ORDER

5.3.2 Compiler und JavaDoc-Einstellungen


Wenn der Build Path definiert ist, sind bereits die wesentlichen Einstellungen getätigt. Es
kann jedoch sein, dass für das Build noch bestimmte Compiler-Optionen von Bedeutung
sind. Diese können unter dem dritten Menüpunkt eingestellt werden. Auch diese Einstel-
lung sollte schon zu Projektbeginn getroffen werden, um alle Möglichkeiten des Editors
und der Workbench nutzen zu können. Sie lässt sich aber jederzeit anpassen.
Zunächst können wir wieder bestimmen, ob die gewünschten Werte projektspezifisch oder
für den gesamten Arbeitsbereich festgelegt werden sollen. Die jeweilige Konfiguration er-
folgt dann analog. Für dieses Kapitel interessant sind die beiden letzten Reiter für Compi-
ler-Einstellungen:
Der Reiter COMPLIANCE AND CLASSFILES erlaubt die Einstellungen von Kompatibilitätsstufen.
Hier ist auszuwählen, ob das Ergebnis des Build gegebenenfalls auf einer anderen Maschi-

142
eclipse_v01.book Seite 143 Montag, 30. Januar 2006 12:02 12

5 – Build Management mit Eclipse

ne und/oder mit einer anderen Java-Version gestartet werden soll. Weiterhin kann hier ein-
gestellt werden, ob Zusatzinformationen, die zum Debugging wichtig sind, bei den erzeug-
ten .class-Dateien hinzugefügt werden sollen oder nicht.
Der Reiter BUILD PATH öffnet ein Fenster, in dem das Verhalten bei bestimmten Fehlersitu-
ationen während des Build bestimmt werden kann. Die möglichen Fehler sind:
쐌 Unvollständige Build-Pfade
쐌 Im Kreis laufende Abhängigkeiten
쐌 Mehrfache Quellen
쐌 Inkompatible Versionen von eingebundenen Quellen (Paketen, Bibliotheken usw.)
Für jede dieser Fehlerarten kann bestimmt werden, ob sie zum Abbruch des Build führen
(Error), lediglich als Meldung angezeigt werden (Warning) oder völlig unbeachtet blei-
ben (Ignore). Ob es bei einem Projekt gute Gründe gibt, weshalb die eine oder andere Feh-
lersituation toleriert werden kann, möge jeder für sich entscheiden. Insgesamt sollte man
aber vorsichtig mit dem Abschalten dieser Fehlerbehandlungen umgehen und sich klar ma-
chen, welche Risiken sich hieraus ergeben.
Die darunter liegenden Checkboxen erlauben noch weitere Einzelheiten. Die beiden ersten
Häkchen bestimmen, ob ein Fehler zu einem Abbruch führen soll und ob ein komplettes
Build mit einem Löschen der Ausgabeverzeichnisse beginnen soll. Diese beiden Einstel-
lungen sollten eigentlich obligatorisch sein. Die beiden nachfolgenden (Ausschluss von
Dateien durch reguläre Ausdrücke und Erlaubnis zu mehreren Ausgabepfaden) hängen von
den jeweiligen Projekterfordernissen ab.
Der Menüpunkt JAVADOC schließlich erlaubt die Einstellungen bezüglich der automatisch
generierbaren Dokumentation. Hier kann im Wesentlichen das Ausgabeverzeichnis für die
Dokumentation festgelegt werden. Dieser Eintrag wird von dem entsprechenden Wizard
benutzt.

5.3.3 Automatische und manuelle Builds


Ein Build kann entweder manuell ausgelöst werden oder automatisch, z.B. bei Änderungen
an Quelldateien, erfolgen. Der manuelle Build lässt sich wie folgt starten.
Durch Auswahl in der Hauptmenüleiste:
쐌 Reiter PROJECT | BUILD ALL für ein Build aller Projekte
쐌 Reiter PROJECT | BUILD PROJECT für ein Build des aktuellen Projekts
쐌 Im Navigator-Fenster durch rechte Maustaste auf das PROJEKT-ICON|BUILD PROJECT (er-
scheint nur, wenn das automatische Build deaktiviert ist)
쐌 Durch die Tastenkombination Ÿ-B
Wichtig, weil sonst gerne vergessen, ist das Sichern aller offenen Dateien vor dem Ausfüh-
ren des Build. Dies kann durch die Einstellung im Menü WINDOW | PREFERENCES | WORK-
BENCH | SAVE ALL MODIFIED RESOURCES erreicht werden. Diese Auswahl ist deshalb wichtig,
weil beim Build nur die abgespeicherte Version einer Datei berücksichtigt wird. Die letzten
Änderungen, die gerade geprüft werden sollen, werden ohne Sichern nicht berücksichtigt.
Ein Build kann auch automatisch ausgelöst werden und zwar durch die Einstellung:

143
eclipse_v01.book Seite 144 Montag, 30. Januar 2006 12:02 12

Einstellungen des Eclipse-Build

Menü WINDOW|PREFERENCES|WORKBENCH|PERFORM BUILD AUTOMATICALLY


In diesem Fall führt das Speichern einer geänderten Datei zum Kompilieren. Diese Einstel-
lung ist meistens wünschenswert. Nur beim Ausführen von komplexeren Änderungen,
wenn nicht alle zu speichernden Zwischenstände wirklich fehlerfrei kompilierbar sind,
kann sie lästig werden.

5.3.4 Weitere Konfigurationen


Neben der Standardkonfiguration können noch weitere Konfigurationen definiert und be-
nutzt werden. Sie werden beim Programmstart verwendet und ergänzen bzw. überschreiben
die Standardkonfiguration. Auf diese Weise ist es leicht möglich, eine Anwendung in ver-
schiedenen Konstellationen, z.B. mit verschiedenen Versionen von Bibliotheken oder Java-
Umgebungen, zu betreiben. Dies kann zum einen dazu dienen, Unterschiede in verschiede-
nen Zielumgebungen, auf denen ein Deployment erfolgen soll, auf einer Umgebung nach-
zustellen, um Fehler im Build zu erkennen und zu analysieren. Zum anderen können die
Konfigurationen auch für die gemeinsame Nutzung in einem Team bereitgelegt werden, so
dass sich nicht jedes einzelne Teammitglied mit Konfigurationsfragen auseinander setzen
muss.
Die Konfigurationen können unter RUN|RUN ... oder gleichermaßen unter DEBUG|DEBUG...
erstellt werden. Man erhält ein neues Fenster mit Einstellungsmöglichkeiten wie in Abb.
5.3 zu erkennen ist.

Abb. 5.3: Einstellungen zum Programmstart

144
eclipse_v01.book Seite 145 Montag, 30. Januar 2006 12:02 12

5 – Build Management mit Eclipse

Zunächst muss die Art der Anwendung ausgewählt werden. Dies erfolgt in der linken
Baumstruktur durch Anklicken einer Anwendungsart und Betätigen des NEW-Buttons. Es
wird ein neuer Unterpunkt angelegt; zu jeder Anwendungsart können auch mehrere Kon-
figurationen erzeugt werden. Zunächst lässt sich noch die Darstellungsart in Eclipse (Per-
spektive) auswählen, anschließend können unter den einzelnen Reitern folgende Einstel-
lungen getroffen werden:
쐌 MAIN: Hier wird das zu startende Projekt eingetragen sowie je nach Anwendungsart die
main-Klasse, das Applet oder die Hauptklasse zum Testen.
쐌 ARGUMENTS:: Hier werden die Kommandozeilenparameter zum Programm wie zur VM
eingestellt. Zusätzlich wird das zu verwendende Arbeitsverzeichnis bzw. im Fall von
Plug-In- oder Workbench-Anwendungen das Workspace-Verzeichnis festgelegt.
쐌 PARAMETERS: Dieser Reiter erscheint nur bei Java Applets, hier lassen sich Größe und
Name des Applet-Fensters festlegen sowie Applet-spezifische Parameter definieren.
쐌 JRE: Hier kann bei Applets, Anwendungen und Test eine der auf dem Rechner instal-
lierten JREs ausgewählt werden, mit der das Programm betrieben werden soll. Diese
kann durchaus eine andere als die von Eclipse verwendete sein. Für Workbench-An-
wendungen steht diese Auswahl unter dem Reiter ARGUMENTS zur Verfügung.
쐌 CLASSPATH: Dieser Reiter ist ebenfalls bei Applets, Anwendungen und Tests verfügbar.
Hier werden die Build Path-Einstellungen als Voreinstellungen übernommen und kön-
nen in analoger Weise modifiziert werden, vgl. Abschnitt 5.3.1. Außerdem lassen sich
die Systemeinstellungen (Bootstrap Entries) verändern, z.B. können auch hier
noch weitere Bibliotheken hinzugefügt und ihre Reihenfolge bestimmt werden. Der
Button ADVANCED bietet auch die Möglichkeit zur Bearbeitung von Variablen.
쐌 SOURCE: Dieser Eintrag bestimmt die Verzeichnisse und Bibliotheken, die nach Quell-
dateien durchsucht werden. Die Einträge im Build Path werden als Voreinstellung ver-
wendet, sie bilden hier eine Einheit, die nur als Ganzes entfernt werden kann. Es können
zusätzliche Verzeichnisse angegeben werden, z.B. die Quelldateien einer externen
Bibliothek, die für das Debugging untersucht werden soll.
쐌 ENVIRONMENT: (nicht bei Java Applets) Hier ist ein Zugriff auf die Systemvariablen ge-
geben. Mit dem Button SELECT werden die existierenden Systemvariablen aufgelistet
und sie können zum Editieren in die Liste aufgenommen werden. Mit NEW können neue
Variablen angelegt werden. Mit EDIT lassen sich die Werte der Variablen bearbeiten, wo-
bei die Werte sowohl explizit gesetzt als auch durch andere Variablen definiert werden
können. Schließlich kann hier entschieden werden, ob die eingestellte Liste die vorhan-
denen Systemvariablen lediglich ergänzen oder komplett ersetzen soll.
쐌 COMMON: Mit diesem Reiter kann die getroffene Konfiguration entweder zur lokalen
Verwendung gekennzeichnet werden oder aber zur gemeinsamen Benutzung Shared
Configuration bereitgelegt werden. Bei Letzterem wird ein Verzeichnis eingetragen, in
dem die Konfiguration zur Verfügung gestellt wird. Für diesen Eintrag sind nur Ver-
zeichnisse innerhalb des Projekts möglich. Andere Teammitglieder können diese Kon-
figuration nutzen, indem sie FILE | PROJECT PROPERTIES | BUILDERS | IMPORT anklicken und
dort auf die Konfiguration zugreifen. Die Konfiguration kann auch als externes Build
geladen werden, dies werden wir im Abschnitt 5.5.4 näher erläutern.

145
eclipse_v01.book Seite 146 Montag, 30. Januar 2006 12:02 12

Einstellungen des Eclipse-Build

Die folgenden Reiter stehen nur bei Konfigurationen für Plug-In-Test und der Workbench
zur Verfügung:
쐌 PLUG-INS: Zunächst wird hier ausgewählt, ob alle installierten Plug-Ins oder lediglich
eine Auswahlliste einbezogen werden sollen. Soll eine Auswahlliste verwendet werden,
so wird eine Liste aller installierten Plug-Ins angezeigt, durch das Setzen von Häkchen
können diese einzeln ein- bzw. ausgeschlossen werden. Hat man die Auswahl getroffen,
so werden mit Drücken der Schaltfläche ADD REQUIRED PLUG-INS die Plug-Ins ergänzt,
die zur Erfüllung von Abhängigkeiten zu den ausgewählten notwendig sind.
쐌 TRACING: Technik zur Fehlersuche und Programmanalyse, bei der Aufrufe und Status-
informationen bereitgestellt werden. Als Standardeinstellung ist das Tracing abgeschal-
tet, hier kann es aktiviert werden. Sofern ein Plug-In eine Tracing-Funktionalität unter-
stützt, lässt sich dieses hier für das Tracing auswählen und mögliche weitere Parameter
können gesetzt werden.
쐌 CONFIGURATION: Hier werden die Plug-Ins ausgewählt, die automatisch gestartet werden
sollen, und ihnen wird ein so genannten Start-Level zugeordnet. Der Start-Level be-
stimmt, in welcher Phase des Programmstarts das jeweilige Plug-In einbezogen wird.

5.3.5 Erzeugung anderer Ausgabetypen


Bisher haben wir die Konfiguration und das Build in Bezug auf das Kompilieren der Quell-
dateien betrachtet, als Ergebnis sind .class-Dateien in einem oder mehreren Verzeichnis-
bäumen erstellt worden. Häufig soll jedoch der kompilierte Java-Code als portables Päck-
chen verfügbar sein, um auf anderen Umgebungen verwendet zu werden. Eclipse bietet
hierzu eine Exportfunktion an, durch die verschiedene portable Formate, z.B. als .jar-Datei
oder als komprimiertes Archiv (.zip-Datei), erzeugt oder bearbeitet werden können. Wir
wollen dies nun näher betrachten und beginnen mit der Erstellung einer .jar-Datei. Dazu
sind die folgenden Schritte notwendig:
Zunächst wählen wir in der Ansicht PACKAGE EXPLORER eine oder mehrere (Unter-)Ver-
zeichnisse aus, die in der .jar-Datei enthalten sein sollen. (Diese Auswahl kann auch später
getroffen oder korrigiert werden.)
Anschließend rufen wir die Exportfunktion auf, z.B. durch
Hauptmenü FILE | EXPORT... oder durch
Rechte Maustaste im PACKAGE EXPLORER|EXPORT ...
Es öffnet sich ein Fenster mit einer Menüführung (Wizard). Zunächst ist in der rechten Aus-
wahl das Zielformat JAR FILE anzuwählen und mit NEXT zu bestätigen.
Im nächsten Fenster kann die Auswahl des Inhalts der .jar-Datei bestimmt bzw. bearbeitet
werden. Im linken Fenster werden die Projekte angezeigt. Diese können als Ganzes oder in
Teilen ausgewählt werden. Wurde im ersten Schritt eine Auswahl vorgegeben, so sind die
Werte hier entsprechend vorbelegt. Im rechten Fenster kann die Auswahl jeweils auf Datei-
ebene weiter detailliert werden. Unter diesem Fenster lässt sich für die Quelldateien be-
stimmen, ob sie selbst und/oder die daraus generierten .class-Dateien eingebunden werden.

146
eclipse_v01.book Seite 147 Montag, 30. Januar 2006 12:02 12

5 – Build Management mit Eclipse

Schließlich bestimmen wir die Zieldatei im unteren Bereich des Fensters. Durch weitere
Checkboxen wird die Komprimierung der Inhalte aktiviert sowie bestimmt, ob vorhandene
Dateien in einer bereits existierenden .jar-Datei überschrieben werden sollen.
Sobald alle Angaben gemacht wurden, wird die Schaltfläche FINISH aktiv und wir können
die Erzeugung des Exports fertig stellen lassen. Falls jedoch auch nach Ausfüllen aller Ein-
träge die Fläche nicht auswählbar wird, so hat Eclipse einen Fehler erkannt, eine entspre-
chende Meldung erscheint im Kopf des Fensters.
Der Wizard arbeitet in ähnlicher Weise auch für andere Exportformate, z.B. für .zip-Datei-
en, und das Anlegen von Verzeichnissen, wobei es Unterschiede lediglich in Details gibt.
So kann bei .zip-Dateien bestimmt werden, ob nur die ausgewählten Verzeichnisse angelegt
werden oder die Verzeichnisstruktur beginnend beim Projekt-Homeverzeichnis abgebildet
wird.

5.4 Build Management und Versionierung


Die bisherigen Ausführungen zum Thema Build Management betreffen jeden Entwickler,
auch wenn er nur kleinste Programme erstellt. Wenn jedoch die Programme größer werden,
sei es, dass mehrere Entwickler daran beteiligt sind oder dass verschiedene Anpassungen
und Entwicklungsstufen erstellt werden sollen, so kommen schnell Aspekte zum Build
Management hinzu, die ohne weitere Werkzeuge nur noch sehr mühsam zu behandeln sind.
Deshalb soll an dieser Stelle ein Exkurs zu dem Versionierungswerkzeug CVS („Concur-
rent Version System“) unternommen werden. CVS ist als Open Source Tool weit verbreitet
und etabliert, dessen Unterstützung durch Eclipse ist bereits als Standard eingebunden.
CVS unterstützt zwei Kernaufgaben, wie bereits aus dem Namen ersichtlich ist:
쐌 Concurrent: CVS ist ein Hilfsmittel, um das gleichzeitige Bearbeiten von Quellcode
durch mehrere Programmierer zu organisieren. Es hilft einerseits, das Team jeweils mit
dem neuesten Stand der Dateien zu versorgen, andererseits mögliche Konflikte bei der
Bearbeitung einer Datei durch verschiedene Teammitglieder zu erkennen und zu behe-
ben.
쐌 Version: CVS erlaubt es, auf alle älteren Versionen einer Datei wieder zurückzugreifen
sowie bestimmte Versionen der Dateien zu kennzeichnen, um deren Zuordnung zu Pro-
grammständen (z.B. „Beta-Release“, „Final Release“), Versionen („Eclipse 3.0“) oder
Entwicklungssträngen („Standard“, „Extended“, „additional features“) festzulegen.
Im folgenden Abschnitt soll auf die Einbindung von CVS in Eclipse und auf für das Build
Management relevante Aspekte eingegangen werden. Für allgemeine Informationen über
CVS möchten wir auf die entsprechende Literatur1 und Online-Quellen2 verweisen.

1. Frei verfügbar unter GPL ist das Buch „Open Source Development with CVS“ von Karl Fogel und Moshe Bar,
eine .pdf Version kann unter http://cvsbook.red-bean.com eingesehen und geladen werden.
2. http://www.cvshome.org

147
eclipse_v01.book Seite 148 Montag, 30. Januar 2006 12:02 12

Build Management und Versionierung

5.4.1 Exkurs: Versionierung mit CVS in Eclipse


Ein CVS-System ist eine Client-Server-Architektur. Der Server dient als zentrale Ablage-
und Steuerungseinheit für alle Dateien, die der CVS-Verwaltung unterliegen. Eine Datei ist
die kleinste Verwaltungseinheit im CVS, die Dateien werden in einer Verzeichnisstruktur
abgelegt. Die Gesamtheit der von CVS verwalteten Dateien wird als Repository bezeichnet.
Weiterhin verwaltet der Server die Benutzer und ihre Zugriffsrechte.
Mit dem Server kommunizieren beliebig viele Clients, sie können Dateien oder Verzeich-
nisse abrufen, hinzufügen oder aktualisieren sowie bestimmte Kennzeichen (Tags) an den
Dateien vornehmen und abfragen. Üblicherweise werden Dateien zur Bearbeitung durch
den Client in die lokale Entwicklungsumgebung kopiert und nach der Bearbeitung wieder
zum Server übertragen. Es kann also mehrere Kopien einer Datei auf lokalen Rechnern ge-
ben, der jeweilige Client (bzw. dessen Benutzer, sprich Programmierer) ist dafür verant-
wortlich, jeweils mit der letztgültigen Fassung zu arbeiten bzw. seine Änderungen zum Ser-
ver zu übertragen, damit auch die anderen diese zur Verfügung haben.
Eclipse hat einen CVS-Client eingebunden, mit dessen Hilfe es möglich ist, eine Verbin-
dung mit einem Server zu erstellen, Dateien vom Server abzuholen und Änderungen sowie
neue Dateien auf dem Server abzulegen. Da es beim Einrichten und bei der Pflege des CVS-
Servers keine Eclipse-spezifischen Aspekte gibt, soll dessen Installation hier nicht be-
schrieben werden; es wird von einem verfügbaren Server ausgegangen.1

Verbindung zum CVS-Server


Wenn ein CVS-Server eingerichtet und erreichbar ist, kann die Verbindung zum Repository
in Eclipse eingerichtet werden. Die Verbindungen werden in der Perspektive CVS REPOSI-
TORY EXPLORING eingerichtet und verwaltet. Durch Rechtsklick im linken Fenster und Aus-
wahl von NEW|REPOSITORY LOCATION wird ein Wizard gestartet.
Hier werden die konkreten Verbindungsparameter eingestellt. Die ersten Einträge sind der
Name oder die IP-Adresse des Servers und der Pfad zum CVS-Repository. Die Authentifi-
zierung benötigt den Namen und das Passwort des Benutzers, der bereits auf dem CVS-Ser-
ver eingerichtet sein muss. Mit der Option VALIDATE CONNECTION ON FINISH wird sofort die
Korrektheit der Parameter geprüft, indem eine Verbindung aufgebaut wird. Schlägt der Ver-
bindungsaufbau fehl, gibt es eine Fehlermeldung und man kann entscheiden, ob die Para-
meter korrigiert werden sollen oder trotzdem verwendet werden (z.B. wenn der Server tat-
sächlich nicht verfügbar ist). Ferner besteht die Möglichkeit, das CVS-Benutzerpasswort
zu speichern, was bequem ist, aber gegebenenfalls als Sicherheitsrisiko angesehen wird.
Nach dem Erstellen der Verbindung wird diese im linken Fenster angezeigt, wobei sich der
Name aus den mitgegebenen Parametern zusammensetzt. Dies kann durch einen eigenen
Namen ersetzt werden. Mit einem Rechtsklick auf die Verbindung kann die Auswahl PRO-
PERTIES erreicht werden, hier lassen sich die Verbindungsdetails einsehen und verändern.

1. Zum Einrichten des Servers und allgemeiner Informationen s. z.B. http://ximbiot.com/cvs/cvshome/docs/

148
eclipse_v01.book Seite 149 Montag, 30. Januar 2006 12:02 12

5 – Build Management mit Eclipse

Abb. 5.4: Anlegen einer Verbindung zum CVS-Server

In dem Wizard ist der Eintrag CONNECTION TYPE mit dem Wert „pserver“ vorbelegt. Dies ist
die Standardverbindungsart von CVS-Systemen, stellt möglicherweise jedoch ein Sicher-
heitsrisiko dar. Falls die Verbindung eine ssh-Verschlüsselung benutzen soll, kann dies hier
oder bei PROPERTIES eingestellt werden.

Projekt mit CVS-Unterstützung starten


Nach dem Aufbau der Verbindung gibt es üblicherweise zwei Möglichkeiten für den nächs-
ten Schritt: Entweder man möchte an einem bereits auf dem CVS-Server bestehenden Pro-
jekt mitarbeiten oder man möchte ein lokal begonnenes Projekt in das CVS-Repository
übertragen.
Ersteres wird durch Eclipse dadurch unterstützt, dass man durch Expandieren des Baums
zur Verbindung einen Einblick in das CVS-Repository erhält. Auf oberster Ebene enthält
der Baum vier Äste:
쐌 HEAD zeigt die bereits im CVS abgelegten Projekte an. CVSROOT ist der zentrale An-
kerpunkt jedes CVS-Repository, hier sind CVS-interne Dateien abgelegt, mit denen der
Programmierer im Normalfall nichts zu tun hat. Weiterhin wird hier die Verzeich-
nisstruktur des Repository angezeigt, wobei Dateinamen jeweils um letzte, aktuelle
Versionsnummern erweitert sind. Die oberste Ebene zeigt die CVS-Module an, typi-
scherweise entsprechen diese den vorhandenen Projekten.

149
eclipse_v01.book Seite 150 Montag, 30. Januar 2006 12:02 12

Build Management und Versionierung

쐌 BRANCHES gibt einen Überblick zu bestehenden Verzweigungen („Branch“). Darunter


versteht man eine Technik im CVS, welche parallele Entwicklungsstränge unterstützt.
쐌 VERSIONS erlauben einen Überblick zu mit einem Tag versehenen, d.h. gekennzeichneten
Versionen. Durch Vergleich der Versionsnummern von Dateien kann man erkennen,
dass es sich hierbei um ältere, fixierte Stände handelt.
쐌 DATES erlaubt einen Überblick über datumsbezogene Kennzeichnungen („Date Tags“).

Abb. 5.5: Properties und CVS-Struktur

Wenn nun ein Projekt aus dem CVS-Repository bearbeitet werden soll, muss zunächst eine
lokale Kopie der zugehörigen Dateien erstellt werden. Mit einem Rechtsklick auf den Pro-
jektordner und Auswahl von CHECK OUT AS ... wird ein Wizard zum Kopieren der Projekt-
daten gestartet. Man erhält ein Dialogfenster mit drei Auswahlmöglichkeiten:
쐌 CHECK OUT AS A PROJECT IN THE WORKSPACE: Mit den Dateien wurde auch eine Projekt-
konfiguration für Eclipse im CVS abgelegt, diese wird dann für das lokal zu erzeugen-
de Projekt verwendet. Das ist eine bequeme Art, den Programmierern nicht nur den
Quellcode, sondern auch zusätzliche projektspezifische Einstellungen zukommen zu
lassen. Nach dem Drücken von NEXT kann der zu verwendende Workspace ausgewählt
werden.

150
eclipse_v01.book Seite 151 Montag, 30. Januar 2006 12:02 12

5 – Build Management mit Eclipse

쐌 CHECK OUT AS A PROJECT CONFIGURED USING THE NEW PROJECT WIZARD: Es gibt keine Eclipse-
Projektkonfiguration (oder sie soll nicht verwendet werden) und es wird der Wizard zum
Einrichten eines neuen Projekts in Eclipse gestartet. Hier kann noch ein anderer Projekt-
name bestimmt werden, was aber normalerweise nicht notwendig ist.
쐌 CHECK OUT INTO AN EXISTING PROJECT: Es wird lokal kein neues Projekt angelegt, sondern
die Dateien werden einem bestehenden Projekt hinzugefügt. Dies kann sinnvoll sein,
kann aber auch leicht zu Konflikten in der Abgrenzung führen, wenn nicht alle Dateien
dem CVS zugeführt werden sollen.
Anschließend erfolgt die Auswahl der zu verwendenden Version. Über HEAD erhält man die
aktuellen Dateien, ansonsten kann über die Auswahl von Tags auf ältere Stände zugegriffen
werden. Mit FINISH wird der Dialog abgeschlossen und die lokalen Kopien der Dateien ste-
hen in der RESSOURCE-Perspektive zur Bearbeitung bereit.
Nun behandeln wir den umgekehrten Fall, dass ein lokales Projekt dem Repository hinzu-
gefügt werden soll.
Mit einem Rechtsklick im Navigator oder Explorer und der Auswahl TEAM|SHARE PROJECT...
wird ein Wizard zum Einfügen des Projekts gestartet. Zunächst kann bestimmt werden, ob
das Projekt in einem bereits verwendeten Repository abgelegt werden soll oder eine neue
Verbindung erstellt wird. Anschließend wird der Modulname für das CVS bestimmt oder
das Projekt kann einem Modul hinzugefügt werden. Die Standardauswahl ist hier, den Pro-
jektnamen auch als Modulnamen zu verwenden. Mit NEXT wird bereits der CVS-Server
kontaktiert, um zu prüfen, ob schon ein gleichnamiges Modul existiert. Ist dies der Fall, so
kann ausgewählt werden, mit welcher Version die Dateien verglichen werden sollen. Sollte
das Projekt nicht diesem Modul zugeordnet werden, so kann man mit BACK einen Schritt
zurückgehen und einen neuen Modulnamen festlegen.
Anschließend kann für einzelne Verzeichnisse oder Dateien angegeben werden, dass sie ex-
plizit nicht dem CVS zugeordnet werden sollen. Sinnvoll ist diese Auswahl z.B. in folgen-
den Fällen:
쐌 Es handelt sich um temporäre Dateien (z.B. eigene Notizen) ohne Bedeutung für andere
Teammitglieder.
쐌 Es handelt sich um Dateien mit starkem lokalen Bezug, die auf anderen Rechnern un-
passend oder sogar schädlich sind.
쐌 Es handelt sich um erzeugte oder binäre Dateien, z.B. Java Class-Dateien oder Archive,
die aus den .java-Dateien erstellt wurden.
Um den letzten Punkt besser zu verstehen, muss man wissen, dass CVS auf die Verwaltung
von Textdateien ausgerichtet ist und nur auf solchen die Änderungsverfolgung effektiv und
vor allem effizient in Speichernutzung und Übertragungsraten arbeitet. Bei Textdateien
wird die letzte Version der Datei gespeichert und alle anderen Versionen werden lediglich
durch ihre Unterschiede beschrieben. Bei binären Dateien erfolgt lediglich eine Prüfung
auf die bitgenaue Gleichheit; ist diese nicht gegeben, wird die gesamte Datei im System ge-
speichert bzw. zwischen Client und Server transferiert.
Ist diese Auswahl getroffen, kann der Prozess mit FINISH abgeschlossen werden; Eclipse
bietet noch die Möglichkeit zu einer Beschreibung des ersten Eintrags des Projekts.

151
eclipse_v01.book Seite 152 Montag, 30. Januar 2006 12:02 12

Build Management und Versionierung

Arbeiten mit CVS und Eclipse


Auf den ersten Blick dürfte man keinen Unterschied bei der Projektarbeit mit oder ohne
CVS feststellen. Dateien und Verzeichnisse können wie gehabt angelegt, editiert oder ge-
löscht werden und es ist auch nicht notwendig, ständig Verbindung zum CVS-Server zu be-
halten.
Allerdings ist CVS ein passives System, d.h., es geschieht nichts (sehr wenig) ohne Zutun
des Programmierers (was aber bei den meisten CVS-Systemen üblich ist). Jeder ist schon
selbst dazu angehalten, sich regelmäßig um einen Abgleich zu kümmern.
Wird im Navigator durch Rechtsklick auf ein Projekt, einen Ordner oder eine Datei die
Auswahl TEAM getroffen, so geben die oberen drei Punkte die wichtigsten bzw. häufigsten
Aktionen wieder. Diese sind:
쐌 UPDATE: Mit dieser Aktion wird die aktuelle Version vom CVS-Repository an den
Client übertragen und die von dem Programmierer durchgeführten Änderungen werden
eingefügt. Dies geschieht normalerweise automatisch, es können jedoch Konflikte auf-
treten, die sich durch den Algorithmus nicht auflösen lassen. In diesem Fall werden die
beiden Versionen im Vergleich angezeigt und der Programmierer ist gefragt, den Kon-
flikt selbst aufzulösen.
Mit UPDATE übernimmt ein Teammitglied die Änderungen, welche die anderen Mit-
glieder in das Repository übertragen haben. Dies sollte regelmäßig erfolgen, denn je
kürzer die Abstände zwischen den Update-Befehlen sind, desto geringer ist die Wahr-
scheinlichkeit von Konflikten.
쐌 COMMIT: Mit dieser Aktion überträgt der Programmierer seine Änderungen in das Re-
pository und es wird eine neue Version der Datei erzeugt. Zusätzlich muss ein Kommen-
tar zur Änderung gegeben werden.
Hierbei wird ebenfalls verglichen, ob nicht zwischenzeitlich eine andere Version auf
dem Server eingespielt wurde und ein Konflikt besteht. Der Befehl wird nur ausge-
führt, wenn die lokale Kopie auf der letzten Server-Version der Datei beruht. Deshalb
kann es ratsam sein, zuerst ein UPDATE aufzurufen und auftretende Konflikte zu bear-
beiten, bevor die eigenen Änderungen mit einem COMMIT übertragen werden.
Während ein UPDATE im Prinzip ungefährlich ist (sofern die anderen Teammitglieder keine
schwerwiegenden Fehler machen), ist das Ausführen des COMMIT-Befehls kritischer: Die
Änderungen können unvollständig sein oder Fehler enthalten und damit andere Teammit-
glieder in ihrer Arbeit behindern. Man kann daher als Faustregel „So oft wie möglich, so
selten wie nötig“ formulieren: Die Änderungen zwischen zwei COMMIT-Aufrufen sollten
nicht zu groß, vor allem aber in sich abgeschlossen und lauffähig (mindestens kompilier-
fähig, möglichst aber bereits in ihrer Funktion getestet) sein.
Diese beiden Befehle sind Kernbestandteil aller CVS-Clients. Eclipse bietet zusätzlich ei-
nen dritten Auswahlpunkt:
쐌 SYNCHRONIZE WITH REPOSITORY: Dies ist eine separate Perspektive, die eine Übersicht
und Details zu den Unterschieden der lokalen Kopie gegenüber den Versionen des Re-
pository bietet. Es handelt sich hierbei um eine Verbindung der beiden Kernaufgaben

152
eclipse_v01.book Seite 153 Montag, 30. Januar 2006 12:02 12

5 – Build Management mit Eclipse

UPDATE und COMMIT in einer gemeinsamen Oberfläche. Hier können Dateien verglichen
und Änderungen übernommen oder abgelehnt werden.
Während die beiden ersten Befehle essenziell sind, ist die Verwendung der Perspektive
SYNCHRONIZE WITH REPOSITORY eher eine Frage des Komforts, aber auch der Übung mit die-
ser Darstellungsweise. Während es bei der täglichen Arbeit eher eine Geschmacksfrage ist,
kann die Perspektive in komplexeren Situationen wie dem Zusammenführen von Verzwei-
gungen (BRANCH, siehe unten) hilfreich sein.

Abb. 5.6: Synchronize Perspective

Zwei Hinweise sind zum Verhalten des COMMIT noch hinzuzufügen, bezüglich des Um-
gangs des CVS mit neuen Dateien bzw. dem Löschen von Dateien.
Wird eine Datei oder ein Verzeichnis neu angelegt, so wird dies nicht automatisch ins CVS
übernommen, selbst wenn die Datei in einem Projekt unter CVS-Kontrolle angelegt wird.
Während bei vielen CVS-Clients der Entwickler die Datei explizit anmelden muss, erkennt
Eclipse beim COMMIT normalerweise neue Dateien und Verzeichnisse und fragt nach, ob sie
ins CVS übernommen werden sollen.
Eine Ablehnung dieser Option ist mit Vorsicht zu betrachten: Diese Auswahl wird notiert
und bei allen folgenden Arbeitsschritten übernommen. Soll die Datei später doch in das
CVS-Repository, so muss dies explizit angefordert werden, was aber auch schon mal gerne
vergessen wird.

153
eclipse_v01.book Seite 154 Montag, 30. Januar 2006 12:02 12

Build Management und Versionierung

Insgesamt ist man immer auf der sicheren Seite, wenn mit dem Anlegen einer Datei auch
sofort ihre Anmeldung zur CVS-Kontrolle erfolgt. Dies geschieht im Navigator durch
Rechtsklick auf den Dateinamen und Auswahl von TEAM | ADD TO VERSION CONTROL...
Beim Löschen einer Datei sollte man sich klar machen, dass diese im CVS-Repository le-
diglich als gelöscht markiert wird, jedoch nicht wirklich verschwindet. Immerhin ist sie ja
legitimer Bestandteil älterer Versionen. Das wirkliche Löschen einer Datei im CVS-Repo-
sitory ist ein schwieriger und meist auch fragwürdiger Vorgang, er wird nicht von Eclipse
unterstützt.
Neben diesen Hauptbefehlen für die tägliche Arbeit gibt es zwei weitere wichtige CVS-
Techniken, die für die mittel- und längerfristigen Aufgaben von Bedeutung sind: TAG und
BRANCH.
Mit einem TAG wird eine Version des CVS-Moduls bzw. des Projekts markiert, d.h. es wird
ein bestimmter Stand der Dateien als zu dieser Version gehörig gekennzeichnet. Dies ist
notwendig, da jede Datei einen eigenen Versionszähler besitzt; oft geänderte Dateien haben
also eine wesentlich höhere Versionsnummer als solche mit wenigen Änderungen. Um hier
den Überblick zu behalten, welche Einzelversionen der Dateien zu einer bestimmten Pro-
grammversion gehören, wird durch ein TAG in jeder Datei der jeweilige Stand ausgezeich-
net.
Ein TAG kann für ein gesamtes Projekt, Verzeichnisse oder einzelne Dateien vergeben wer-
den.
Man erreicht dies z.B. im Navigator durch Rechtsklick auf die Datei oder ein Verzeichnis
und Auswahl von TEAM | TAG AS VERSION... Dann wird man aufgefordert, einen Namen für
den TAG zu vergeben. Dieser sollte im Normalfall eindeutig und erklärend sein, z.B. worum
es sich bei dieser Version handelt („Test“, „Beta“, „Stable“ ...)
Wichtig ist, dass jeweils die lokalen Dateiversionen (also des Clients) mit dem TAG ver-
sehen werden. Dies können also ältere Stände sein als auf dem Server vorhanden sind, was
durchaus beabsichtigt sein kann, aber beachtet und überlegt werden sollte.
Es besteht aber auch die Möglichkeit, bereits vorhandene TAG-Namen wieder zu wählen.
Mit Auswahl der Checkbox MOVE TAG IF ALREADY EXISTS wird das Tag bei den Dateien, die
dieses TAG bereits besitzen, auf die neuere Version verschoben.
Dies ist eine einfache Möglichkeit, nachträglich noch Fehlerkorrekturen zu einer Version
hinzuzufügen, ist aber auch mit Vorsicht zu genießen: Nicht nur kann die Versionshistorie
verfälscht werden (z.B. hat ein bereits ausgeliefertes Programm andere Sourcen bei glei-
cher Versionsbezeichnung), auch kann so eine Kombination von Dateiversionen zusam-
mengestellt werden, die bis dahin nicht getestet worden ist.
Der CVS-Befehl zum Löschen eines TAG wird von Eclipse nicht unterstützt, was wohl als
Absicht und Sicherheitsmaßnahme angesehen werden kann.
Wie bereits in Abb. 5.5 erkennbar, kann man sich in der Perspektive CVS REPOSITORY EX-
PLORING alle Versionen des Projekts anzeigen lassen.

Während die bisher beschriebenen Funktionen mit etwas Übung leicht zu handhaben sind,
wird mit den Anlegen eines BRANCH (d.h. einer Verzweigung) oft eine höhere Komplexi-

154
eclipse_v01.book Seite 155 Montag, 30. Januar 2006 12:02 12

5 – Build Management mit Eclipse

tätsstufe in ein Projekt gebracht, deren Bewältigung nicht nur technischer Unterstützung
bedarf, sondern auch organisatorisch wohl überlegt sein soll.
Ein BRANCH unterstützt das parallele Arbeiten auf zwei oder mehr Entwicklungssträngen.
Oft gibt es eine Fortsetzung des bisherigen Projekts, die als Hauptzweig bezeichnet wird,
und eine Teilmenge von Dateien, die den BRANCH ausmachen. Dies kann z.B. notwendig
sein, wenn die Entwicklung einer neuen Version begonnen wird, während zugleich noch
Fehlerkorrekturen der bestehenden Version durchgeführt werden. Man kann einen oder
mehrere BRANCHES anlegen sowie auch einen BRANCH selbst verzweigen, wir wollen hier
aber nur den elementaren Schritt des Anlegens und Zurückführens eines BRANCH beschrei-
ben. Komplexere Situationen erfordern einen wesentlich höheren Organisationsaufwand,
sind aber technisch bzw. in Bezug auf die Durchführung in Eclipse lediglich eine mehrfa-
che Kombination der dargestellten Schritte.
Das Anlegen eines BRANCH in Eclipse wird erreicht durch Rechtsklick im Navigator und
Auswahl von TEAM|BRANCH... Man wird aufgefordert, sowohl einen Namen für den BRANCH
zu vergeben (BRANCH TAG) als auch einen TAG auf den Dateiversionen vor dem Anlegen des
BRANCH anzulegen. Der BRANCH TAG markiert im Gegensatz zu einem anderen TAG immer
die jeweils neueste Version der Dateien des BRANCH auf dem Server.
Der zweite TAG ist nicht in allen CVS-Clients erforderlich, empfiehlt sich jedoch, wenn spä-
ter einmal die Entwicklungsstränge wieder zusammengeführt werden sollen. Weiterhin
kann der Entwickler bestimmen, ob er ab sofort auf dem BRANCH arbeiten möchte. Falls er
sich dafür entscheidet, werden alle Änderungen bei einem COMMIT dem BRANCH zugeord-
net. Andere Entwickler arbeiten zunächst weiter auf dem Hauptzweig, sie müssen explizit
auf den BRANCH wechseln. Dies lässt sich auf folgende Arten erreichen:
쐌 Mit der Auswahl TEAM | SWITCH TO ANOTHER BRANCH OR VERSION... kann ein BRANCH aus-
gewählt und auf diesen gewechselt werden. Dies ist für den Wechsel auf den BRANCH zu
bevorzugen.
쐌 Der Entwickler lädt den BRANCH als neues Projekt mit anderem Namen in seinem Work-
space. Dies erlaubt es ihm, sowohl auf dem BRANCH als auch auf dem Hauptzweig zu ar-
beiten.
쐌 Schließlich kann der Entwickler noch das Projekt aus dem Workspace löschen und den
BRANCH aus dem Repository laden.
Die weitere Arbeit verläuft durch UPDATE und COMMIT wie bereits beschrieben, jedoch soll-
te sich der Entwickler immer im Klaren sein, auf welcher Verzweigung er Änderungen vor-
nimmt. Die Befehle beziehen sich immer auf den Zweig, dem die Datei zugeordnet ist. So
ist es nicht möglich, eine Datei des BRANCH zu verändern und dies in den Hauptzweig oder
einen anderen BRANCH zu übertragen.
Die Entwicklung mit mehreren Entwicklungssträngen kann einfach dadurch beendet wer-
den, dass ein BRANCH beendet wird, z.B. wenn die Fehlerbehebung einer alten Programm-
version eingestellt wird. Dies ist keine besondere Herausforderung, lediglich das Archivie-
ren des letzten Stands ist ratsam.
Häufig kommt es aber vor, dass zwei Entwicklungsstränge wieder zusammengeführt wer-
den sollen (MERGE), was eine sehr anspruchsvolle Aufgabe darstellen kann.

155
eclipse_v01.book Seite 156 Montag, 30. Januar 2006 12:02 12

Build Management und Versionierung

Zunächst sollte im Workspace die Version des Hauptzweigs vorhanden sein, auf der das
Zusammenführen stattfinden soll. Eclipse unterstützt diese Arbeit über die Auswahl
TEAM|MERGE... im Navigator.
Zunächst ist anzugeben, welcher BRANCH in welcher Version zurückgeführt werden soll.
Wird hier ein BRANCH TAG angegeben, so werden die jeweils letzten Versionen der Dateien
im BRANCH verwendet. Wird ein anderer „normaler“ TAG auf dem BRANCH gewählt, so wer-
den die markierten Versionen der Datei verwendet.
Mit den Hilfsfunktionen REFRESH TAGS und CONFIGURE TAGS lassen sich Details abfragen
oder es kann auf dem Server nach weiteren TAGS gesucht werden.
Weiter kann bestimmt werden, ob die SYNCHRONIZE View verwendet wird, um vorab das Er-
gebnis des MERGE-Prozesses einzusehen, oder dies direkt im lokalen Workspace ausgeführt
wird.
Nach dem Durchführen des MERGE-Prozesses ist das Ergebnis zu überprüfen, wobei die
TEAM SYNCHRONIZING-Perspektive hilfreich sein kann, vgl. Abb. 5.6. Hier können Dateien
strukturell und im Editor verglichen, Konflikte überprüft und aufgelöst und in das lokale
Projekt übernommen werden.
Die Arbeit wird durch ein COMMIT auf dem fertig gestellten Projekt abgeschlossen. Aller-
dings ist es sehr wichtig zu beachten, dass mit dem Auflösen von Konflikten längst nicht
alle möglichen Fehler behoben sind, die durch die Zusammenführung entstehen können.
Bevor also das COMMIT ausgeführt wird, sollte die entstandene Version gewissenhaft getes-
tet werden.
Auch wenn dieser Abschnitt mit dieser Annahme eingeleitet wurde: Ein MERGE bedeutet
nicht automatisch das Beenden der Arbeit an einem BRANCH. Es kann auch sinnvoll sein,
über MERGE die beiden Entwicklungsstränge in regelmäßigen Abständen zu synchronisie-
ren und beide Stränge weiterzuführen.
Technisch ist dies keine besondere Änderung, allerdings sollte bei der Durchführung des
ersten MERGE ein TAG auf dem BRANCH erstellt werden, welches die Version bei der Zusam-
menführung kennzeichnet. (Dies ist notwendig, weil das BRANCH TAG ja jeweils die neueste
Version bezeichnet, also keinen Anhaltspunkt mehr über die verwendeten Versionen lie-
fert.) Mit der Auswahl TEAM | MERGE... im Navigator wird das Zusammenführen gestartet,
wobei nun als START TAG das TAG auf dem BRANCH unmittelbar vor dem letzten MERGE an-
gegeben wird.
Beim zweiten (und jedem weiteren) MERGE wird ebenfalls ein entsprechendes TAG auf dem
BRANCH erstellt. Dieses wird beim nächsten MERGE als START TAG bestimmt, so dass sich die
Unterschiede jeweils nur aus den Änderungen seit der letzten Zusammenführung und nicht
mehr aus allen Änderungen seit Erstellen des BRANCH ergeben.
Neben diesen Kernfunktionen, die allen CVS-Clients gemeinsam sind, bietet Eclipse noch
eine Reihe weiterer Funktionen, die die Arbeit mit CVS komfortabel gestalten, zum Bei-
spiel:
쐌 Vergleich einer Datei in verschiedenen Versionen oder BRANCH mittels Rechtsklick und
Auswahl von COMPARE WITH | ANOTHER BRANCH OR VERSION, vgl. Abb. 5.6

156
eclipse_v01.book Seite 157 Montag, 30. Januar 2006 12:02 12

5 – Build Management mit Eclipse

쐌 Ersetzen einer Datei durch eine andere Version mittels Rechtsklick und Auswahl von
REPLACE WITH | ANOTHER BRANCH OR VERSION
쐌 Erzeugen eines Patch, d.h. einer Beschreibung der Unterschiede zwischen zwei Ver-
sionen, die verschickt und von anderen (die z.B. keinen eigenen Zugriff auf das CVS-
Repository haben) zum Abgleich verwendet werden kann, mittels Rechtsklick und Aus-
wahl von TEAM | CREATE PATCH...
Wir wollen allerdings diese Funktionen hier nicht mehr im Detail beschreiben, sondern
möchten dazu einladen, selbst etwas hiermit zu experimentieren und so eigene Erfahrungen
im Umgang mit diesen Techniken zu sammeln.

Trennen der Verbindung


Schließlich sei noch vermerkt, dass man auch die lokale Kopie eines Projekts wieder von
der CVS-Verwaltung abtrennen kann. Dies kann zum endgültigen Abschluss der Arbeit er-
folgen oder wenn z.B. der Server, auf dem das CVS betrieben wird, gewechselt wird. CVS
erzeugt auf dem Client zusätzliche Dateien mit internen Informationen („Metadaten“), u.a.
auch über die Server-Verbindung, so dass ein Wechsel des Servers nicht einfach durch Än-
derung der Verbindungsdaten erfolgen kann.
Zum Trennen der Verbindung wird im Navigator oder Explorer mit Rechtsklick zum Pro-
jekt die Auswahl TEAM|DISCONNECT getroffen. Ein Popup fragt nach, ob die CVS-Metadaten
gelöscht oder beibehalten werden sollen. Werden die Daten beibehalten, so ist es recht ein-
fach möglich, zu einem späteren Zeitpunkt die Verbindung zum Server wieder herzustellen
und die Dateien zu synchronisieren. Umgekehrt haben diese Metadaten ohne den Server
keine Bedeutung bzw. behindern das Anbinden an einen anderen CVS-Server.
Mit diesen Ausführungen ist der Exkurs zum Thema CVS beendet. Es wurden die wesent-
lichen Arbeitsschritte für die Verwendung von CVS in Eclipse erläutert. Für weiterführende
Informationen stehen frei verfügbare Quellen und Fachliteratur zur Verfügung. Im Folgen-
den wird CVS im Rahmen des Build Managements weiter betrachtet. Diesen Exkurs möch-
ten wir aber mit folgenden Hinweisen abschließen:
쐌 Für jede Softwareentwicklung, selbst bei kleinen Projekten und einzelnen Entwicklern,
ist eine Versionsverwaltung sehr ratsam, wenn nicht fast überlebenswichtig! Egal, ob
man auf ältere Stände zurückgreifen möchte oder parallele Entwicklungen betreibt, ein
geeignetes Tool kann hier viel Zeit, Aufwand und Ärger ersparen. Mit CVS steht ein
etabliertes Open-Source-Werkzeug zur Verfügung, dessen Einbindung in Eclipse pro-
blemlos und komfortabel ist.
쐌 Für die tägliche Arbeit stellt die Verwendung von CVS einen marginalen Zusatzauf-
wand dar, der sehr schnell vom Nutzen (z.B. einfacher Abgleich der Dateien im Team)
übertroffen wird.
쐌 Dennoch ist CVS nur ein Werkzeug und kein Allheilmittel: Zu seiner effizienten Nut-
zung im Projekt bedarf es Spielregeln (z.B. „Was gehört ins CVS, was nicht?“ oder
„Wann wird ein COMMIT ausgeführt?“) und organisatorischer Regelungen (z.B. „Wer
bearbeitet welchen Programmteil?“, „Was gehört zu welcher Version?“, „Was wird
parallel entwickelt?“).

157
eclipse_v01.book Seite 158 Montag, 30. Januar 2006 12:02 12

Build Management und Versionierung

5.4.2 Versionierung und Build Management


Schon bei der Frage „Können wir das Programm noch einmal in der Version 2.1 erstellen?“
wird klar, dass Build Management und Versionierung eng miteinander verknüpft sind. Zu-
nächst soll deshalb beschrieben werden, welche Bedingungen sich aus der Kombination der
beiden Aspekte für die jeweilige Gestaltung der Versionierung und des Build Managements
ergaben.
Betrachten wir zunächst, welche Zusatzbedingungen sich aus dem Build Management
heraus für das CVS ergeben:
쐌 Zu einer Auslieferung eines Programms können wesentlich mehr Dateien gehören als
im zugehörigen Java-Projekt bearbeitet werden: Dies können Ergebnisse anderer Pro-
jekte sein, grafische Elemente oder auch Dokumente wie Handbücher und Anleitungen.
Hier ist zu entscheiden, ob diese ebenfalls im CVS abgelegt werden oder anderweitig
verwaltet werden sollen. Als einfache Faustregel kann hier gelten: Alles, was bestimm-
ten Versionen zugeordnet ist, gehört auch ins CVS.
쐌 Weiterhin stellt sich die Frage, wie mit verwendeten Bibliotheken oder Programmen
umgegangen wird. Einerseits sind dies binäre Dateien, die sich wohl kaum jemals ver-
ändern, andererseits kann (z.B. bei langlebigen Projekten) sehr wohl eine Abhängigkeit
zur Programmversion bestehen. Generell möchten wir empfehlen, alles, was zu einer
Auslieferung gehört, auch zusammenhängend (sprich in CVS) abzulegen und zu ver-
walten. Ausnahmen ergeben sich aus zu großen Datenmengen insbesondere in Hinblick
auf die Datenübertragung zwischen Server und Client. Ein möglicher Kompromiss
kann hier sein, solche Dateien an einem von allen Teammitgliedern erreichbaren Ort
(z.B. ein Netzwerkfilesystem) abzulegen und im CVS eine Konfigurationsdatei anzule-
gen und zu pflegen, die genau beschreibt, welche Dateien zu welcher Version gehören.
쐌 Schließlich ist es noch ratsam, auch die Eclipse-Projektdateien im CVS zu verwalten.
(Dies geschieht schon automatisch, wenn ein Eclipse-Projekt komplett als CVS-Modul
angelegt wird.) Insbesondere die oben beschriebenen Build-Einstellungen stehen dann
jeweils zur passenden Version zur Verfügung.
Umgekehrt hat die Benutzung von CVS auch Einfluss auf die Struktur und Prozesse im
Build Management:
쐌 So ist es ratsam, den Compiler Output Path auf ein Verzeichnis zu legen, das nicht unter
CVS-Kontrolle steht. Zum einen kann CVS nicht gut mit den binären Dateien umgehen,
zum anderen wird so vermieden, dass mögliche inkonsistente Stände von .class-Dateien
ins CVS übertragen und so „verewigt“ werden. Ähnliches gilt für JavaDoc-Stände, auch
wenn es sich um Textdateien handelt, die somit wesentlich effizienter verwaltet werden.
쐌 Alle Angaben wie Pfade oder Verweise (Links) müssen so erfolgen, dass sie auf allen
Clients gültig sind. Dies ist entweder durch eine verbindliche Vereinbarung sicherzu-
stellen oder es werden alle Angaben relativ zu einem Projekt-Hauptverzeichnis getrof-
fen. Letzteres ist in jeden Fall vorzuziehen: Es entspricht dem Eclipse-Workspace-Kon-
zept, ist flexibler und weniger fehleranfällig.
쐌 Für Dokumente wie Handbücher oder Anleitungen ist zu überlegen, ob ein Format ge-
wählt werden kann, welches in CVS effizient verwaltet wird: Während eine Word-Datei

158
eclipse_v01.book Seite 159 Montag, 30. Januar 2006 12:02 12

5 – Build Management mit Eclipse

(normalerweise) als binär behandelt wird, werden HTML-Dateien wesentlich besser


unterstützt.
Sind diese Aspekte entsprechend geklärt und ist das Projekt so eingesetzt und konfiguriert,
dass es komplett im CVS-Repository enthalten ist, so besteht ein Build-Prozess im Prinzip
aus den folgenden Schritten:
1. CLEAR PROJECT: Die lokale Umgebung sollte für das Build möglichst frei sein. Am bes-
ten wird der vorhandene Stand des Projekts gesichert und das Projekt komplett gelöscht.
(Alternativ kann auch ein separater Workspace verwendet werden.)
2. CHECK OUT: Das Projekt wird in der gewünschten Version vom CVS-Repository ange-
fordert (siehe Seite 149).
3. BUILD: Mit der Auswahl PROJECT|BUILD ALL werden alle Dateien kompiliert und gege-
benenfalls JavaDoc erzeugt.
4. TEST: Die soeben erstellte Version wird, z.B. durch Aufrufen von JUnit-Tests, auf ihre
Korrektheit geprüft.
5. DEPLOY: Aus der lokalen Version wird ein Deployment-Paket zusammengestellt und zur
Auslieferung gebracht.
6. CLEAN UP: Mögliche Änderungen, z.B. Fehlerkorrekturen, werden an das CVS-Reposi-
tory übertragen. Anschließend wird die Umgebung wieder in den Ausgangszustand zu-
rückversetzt.
Mit den bisherigen Darstellungen zum Build Management kann dieser Prozess schon mit
den Eclipse-internen Mitteln aufgesetzt und zum Teil automatisiert durchlaufen werden.
Für kleine bis mittlere Projekte mit durchschnittlichen Anforderungen kann dies vollkom-
men ausreichen. Sollten jedoch die einzelnen Phasen komplexer werden oder zusätzliche
Anforderungen umgesetzt werden, kann der Einsatz von flexibleren und leistungsfähigeren
Build-Management-Werkzeugen sinnvoll werden. Dies wird von Eclipse schon durch die
Einbindung des Build-Management-Tool Ant aktiv vorbereitet und im nun folgenden Ab-
schnitt vorgestellt.

5.5 Exkurs: Build Management mit Ant


Nachdem bisher auf die Eclipse-interne Unterstützung des Build eingegangen wurde,
möchten wir nun die Tool-zentrierte Sicht etwas zurücknehmen und die Aufgaben des
Build Managements allgemein betrachten. Dabei soll der Praxisbezug nicht verloren ge-
hen, daher werden Beispiele und Anregungen auf Basis von Ant beschrieben und auch de-
ren Einbindung in Eclipse wird nicht vergessen.
Wenn das Programm, das wir in Eclipse bearbeiten, auf dem gleichen Rechner bzw. auf ei-
nem zugreifbaren Dateisystem betrieben wird, können wir das Build mit der Auslieferung
verbinden, indem wir in Eclipse als Zielverzeichnis des Build direkt das vorgesehene Ab-
lagesystem angeben. Dies ist nur dann ratsam, wenn wir das Programm praktisch allein ent-
wickeln und betreiben. Meist ist es die bessere Lösung, zunächst mit einer lokalen Instal-
lation zu arbeiten und die Übertragung auf das Zielsystem separat ausführen zu lassen.
Hierzu möchten wir Ant als Tool für das Build Management vorstellen.

159
eclipse_v01.book Seite 160 Montag, 30. Januar 2006 12:02 12

Exkurs: Build Management mit Ant

Die Auswahl von Ant in diesem Abschnitt liegt nicht allein darin begründet, dass Eclipse
eine direkte Unterstützung für Ant bietet. Der Erfolg von Ant als Build-Management-Tool
ist begründet in seiner Umsetzung als Java-Programm, seiner XML-basierten Konfigura-
tionssprache und seiner Plattformunabhängigkeit.
Wir wollen versuchen, die Struktur der Skripte darzulegen und Skriptauszüge so aufzube-
reiten, dass sie auch mit wenig Vorkenntnissen verständlich sind.
Wenn wir das Build etwas von dem direkten Programmieren trennen und als eigenen Schritt
betrachten, so sind eine Reihe von Aspekten zu beachten:1
쐌 Zusammenstellung aller Quelldateien: Was von einem einzelnen Programmierer viel-
leicht noch überblickt werden kann, wird spätestens in Projekten mit mehreren Mitar-
beitern schnell zur Falle: die Frage, welche Dateien in welcher Fassung benötigt werden
oder nicht. Zu einer Versionierung der Quelldateien gibt es hier kaum eine realistische
Alternative und auch ein zumindest minimaler Workflow, in welchem Stadium sich eine
Datei befindet („wird erstellt“, „bereit zum Test“, „fertig“), ist sehr sinnvoll.
쐌 Ausführung des Kompilierens: Was wir bereits bei den Einstellungen für das interne
Build vorgestellt haben, muss entsprechend auch für das Build mit Ant durchgeführt
werden. Dies betrifft sowohl die Reihenfolge des Kompilierens und die Beachtung von
Abhängigkeiten als auch die Strukturierung, welche Programmteile zusammenhängend
bzw. unabhängig voneinander kompiliert werden sollen.
쐌 Konfiguration für das Zielsystem: Je nachdem, für welche Maschinen das Ergebnis des
Build verwendet werden soll (oder auch das Build ausgeführt werden soll), kann sich
der Build-Vorgang z.B. durch die Verwendung unterschiedlicher Compiler bzw. Com-
piler-Versionen, Libraries oder maschinenspezifischer Programmteile erheblich unter-
scheiden. Diese Angaben sollten durch Parameter einstellbar sein und die einzelnen
Konfigurationen sollten abgelegt sein.
쐌 Nachvollziehbarkeit von ausgeführten Schritten und Fehlern: Je mehr Schritte während
des Build-Prozesses ausgeführt werden, desto schwieriger ist die Fehlersuche im Falle
eines misslungenen oder unvollständig ausgeführten Build. Daher ist es wichtig, dass
einerseits die Fehlermeldungen z.B. des Compilers aufgezeichnet werden, andererseits
die einzelnen Schritte mit sinnvollen Meldungen ihre Durchführung dokumentieren.
Am günstigsten ist das Führen einer zentralen Log-Datei, in der die Meldungen in der
chronologischen Reihenfolge eingetragen sind.2

1. Anm. des Autors: Es gibt eine ganze Reihe von Projekten und Erweiterungen zu Ant, die genau auf die im fol-
genden genannten Aspekte abzielen und Unterstützung z.B. bei der Erkennung von Abhängigkeiten oder der
automatischen Dokumentation bieten. Wir werden hier nicht auf diese Tools eingehen. Wer sich jedoch in
einem größeren Projekt mit diesen Fragen auseinandersetzt, dem sei empfohlen, einmal die externen Tools zu
Ant zu studieren. Dies ist zu finden auf der Ant Homepage http://ant.apache.org unter „RELATED PROJECTS“ und
„EXTERNAL TOOLS AND TASKS“.)
2. Über Größe, Detaillierungsgrad und Lesbarkeit dieser Datei kann man lange diskutieren. Nach Meinung des
Autors sind zu viele Einträge weniger störend als der Fall, dass die gerade hilfreiche Meldung nicht notiert
wurde. Werden die Einträge strukturiert, so kann man auch nachträglich leicht die wichtigen Stellen heraus-
filtern.

160
eclipse_v01.book Seite 161 Montag, 30. Januar 2006 12:02 12

5 – Build Management mit Eclipse

쐌 Dokumentation des Build: Je weiter das Build automatisiert ist, z.B. bei einem „Nightly
Build“, das regelmäßig um dieselbe Zeit beginnt und automatisch ausgeführt wird, des-
to wichtiger wird es, dass nicht nur ein Ergebnis vorliegt, sondern auch, wie dieses Er-
gebnis erzielt wurde. Daher sollten alle Daten notiert werden, die notwendig sind, um
das Build identisch zu wiederholen. Ein Großteil dieser Arbeiten kann wieder durch den
Einsatz einer Versionierung erfolgen, wenn z.B. im Build nur Dateiversionen mit einem
bestimmten TAG1 verwendet werden oder umgekehrt die Dateien durch das Build mit ei-
nem TAG versehen werden.
쐌 Deployment: Das Ergebnis des Build (kompilierter Code, Libraries u.a.) kann direkt im
Zielsystem abgelegt, in einem Austauschverzeichnis bereit gestellt oder noch auf ande-
re Umgebungen verteilt werden. Auch diese Schritte sollten automatisiert erfolgen, um
Fehlerquellen zu vermeiden.

5.5.1 Ant Projects, Targets und Tasks


Um ein Build durch Ant ausführen zu lassen, muss eine Konfigurationsdatei erstellt werden,
welche alle notwendigen Informationen und Prozessschritte enthält. Diese Datei wird als
XML-Datei erstellt, deren Hauptelement (Root) ein Project in Ant definiert. Unter einem
Project in Ant können wir uns einfach die Build-Aufgaben zu einem Eclipse-Projekt vor-
stellen. Es besteht aus einer Reihe von Targets, welche ein bestimmtes Teilziel des Build-
Prozesses definieren, z.B. das Kompilieren eines Pakets oder das Erstellen eines JavaDoc-
Dokuments. Will man ein Build oder Teilaufgaben hiervon ausführen, so wird das entspre-
chende Target aufgerufen. Für das Project wird ein Target als Default ausgezeichnet, d.h.,
dieses Target wird ausgeführt, wenn keine weiteren Angaben erfolgen.
Die Targets ihrerseits bestehen aus Tasks, in denen einzelne Befehle zur Erreichung des
Ziels definiert sind. So kann z.B. ein Target die Erstellung einer .jar-Datei sein, welches aus
den Tasks zum Kompilieren von Quelldateien, zum Kopieren in ein Verzeichnis und zum
Zusammenfassen zur .jar-Datei besteht. Die Targets können untereinander durch Abhän-
gigkeiten verknüpft werden; wird also ein Target aufgerufen, so werden automatisch auch
die Targets ausgeführt, von denen es abhängt.
Zusätzlich können Variablen, so genannte Properties, definiert werden, die bestimmte Wer-
te wie Verzeichnisnamen enthalten. Diese können global oder innerhalb eines Target ge-
setzt werden.

1. Vgl. Seite 154f. Aber auch ohne Versionsverwaltung sollte eine Methode benutzt werden, die jeweiligen
Stände der Dateien zu kennzeichnen oder zu fixieren.

161
eclipse_v01.book Seite 162 Montag, 30. Januar 2006 12:02 12

Exkurs: Build Management mit Ant

Der folgende Code-Auszug zeigt die wesentliche Struktur einer Konfigurationsdatei:

<project name="MeinBeispiel" default="alles" basedir=".">


<description> Einfaches Beispiel zur Struktur einer
Konfiguration. Mit basedir wird das
Hauptverzeichnis definiert.
</description>
<!-- Hier werden einige globale Properties definiert -->
<property name="src" location="src"/>
<property name="build" location="build"/>
<property name="dist" location="dist"/>
<target name="init">
<!-- Erzeuge ein Verzeichnis zum Erstellen des Builds und lese
das aktuelle Projekt aus dem CVS Repository -->
<mkdir dir="${build}"/>
<cvs cvsRoot=":pserver:herbert@localhost:/home/cvs"
package="myProject" dest="${build}"/>
</target>
<target name="compile" depends="init"
description="kompiliert den Code">
<!-- Kompiliert den Code aus ${src} und legt in ${build} ab -->
<javac srcdir="${src}" destdir="${build}"/>
</target>
<target name="dist" depends="compile"
description="Erstellt eine Distribution">
<!-- Erstellt ein neues Verzeichnis fuer Distribution, legt das
Kompilat in einem .jar-File dort ab -->
<mkdir dir="${dist}/lib"/>
<jar jarfile="${dist}/lib/MyProject.jar" basedir="${build}"/>
</target>
<target name="clean" depends="init"
description="Löscht alle erzeugten Verzeichnisse">
<delete dir="${build}"/>
<delete dir="${dist}/lib"/>
</target>
<target name="alles" depends="dist clean"
description="vollstaendige distribution">
<!-- Distribution wird durch Abhaengigkeit erstellt, hier wird
noch das temporaere Verzeichis aufgeraeumt -->
<delete dir="${build}"/>
<echo message="alles erstellt und build-Verzeichnis
aufgeraeumt!"/>
</target>
</project>

Auch wenn hier nichts Wesentliches passiert, kann dieser Code bereits in Ant geladen wer-
den. Dazu wird er in einer Datei gespeichert, die üblicherweise mit build.xml als Standard-
bezeichnung von Ant benannt und im Folgenden auch mit diesem Namen verwendet wird.

162
eclipse_v01.book Seite 163 Montag, 30. Januar 2006 12:02 12

5 – Build Management mit Eclipse

Startet man nun Ant ohne weitere Angaben, so wird in der ersten Zeile das Standard-Target
alles erkannt. Nun erkennt Ant den description-Eintrag und die Definitionen in den
Property-Einträgen, die hier als globale Variablen gesetzt werden. Die anschließenden
Targets werden nicht direkt ausgeführt, sondern Ant beginnt nun, das Target mit Namen
alles abzuarbeiten. Dies hängt aber von dem Target dist ab, also wird Ant zunächst die-
ses Target ausführen, bevor die Tasks in alles ausgeführt werden. Dieses hängt aber seiner-
seits vom Target compile ab und dieses wiederum von init. Somit wird durch die
Abhängigkeiten erzwungen, dass die Targets in der Reihenfolge init|compile|dist
ausgeführt werden.
Das Target clean wird anschließend in alles ausgewertet. Es hängt von init ab, da die-
ses Target jedoch schon ausgeführt wurde, kommt es nun nicht mehr zum Einsatz. Inner-
halb eines Aufrufs wird jedes Target nur einmal ausgeführt.
Wollen wir zum Beispiel nicht das Target alles ausführen, sondern nur bis zum Schritt
compile gehen, müssen wir Ant mit dem Aufruf ANT COMPILE starten. In diesem Fall wer-
den wegen der Abhängigkeiten nur init und compile ausgeführt.

5.5.2 Typische Ziele


Wenn man eine Reihe von (unabhängig voneinander entstandenen) Ant-Skripten liest, fällt
schnell auf, welche Aufgaben sich immer wiederholen und welche Targets somit wieder-
kehren. Es ist daher zu Beginn eines Projekts sehr sinnvoll, darauf zu achten, dass diese Tar-
gets sauber konzipiert und formuliert sind. Häufig wird es im Laufe des Projekts auch nicht
bei einem Ant-Skript bleiben. Daher sollte auch an die Wiederverwendung oder das Bereit-
stellen von Templates für diese Standardaufgaben gedacht werden. Immer wiederkehrende
Aufgaben, welche wir durch typische Namen für ihre Targets markieren, sind:
쐌 INIT oder CONFIGURE: Hier werden globale Einstellungen getroffen wie das Festle-
gen von Quell- und Zielverzeichnissen, die Auswahl des Compilers usw. Häufig werden
auch temporäre Verzeichnisse angelegt, die im Laufe des Build verwendet werden. Dies
wird auch gerne benutzt, um einen Versionsnamen oder Zeitstempel für das Ergebnis
anzulegen.
쐌 CLEANUP: Diese Aufgabe ist in gewisser Weise das Gegenteil zu INIT. Hier werden
temporär verwendete Dateien und Verzeichnisse wieder gelöscht und das System in ei-
nen definierten Zustand zurückgesetzt.
쐌 PREPARE: Diese Aufgabe schafft den definierten Ausgangsstatus für das Ausführen
des Build. Ein typischer Task an dieser Stelle ist das Beziehen der Quelldateien z.B. aus
dem CVS Repository. Dieser Punkt kann recht heikel werden, wenn zum Beispiel meh-
rere Stufen des Versionsstatus („alpha“, „beta“, „final“ oder „experimentell“, „stabil“,
„freigegeben“) erstellt werden sollen. Hier setzt ein erfolgreiches Build Management
auf jeden Fall eine saubere Versionsverwaltung voraus.
쐌 COMPILE: Diese Aufgabe übernimmt die Kernarbeitsschritte zur Erzeugung der Ziel-
dateien, seien es Klassenverzeichnisse, Libraries oder andere Ausgabedateien.
쐌 DEPLOY: Diese Aufgabe führt das Bereitstellen zum Deployment oder das Deploy-
ment selbst auf einer Zielumgebung aus. Bei einer Bereitstellung kann hier auch eine
Benachrichtigung über den ausgeführten Schritt erfolgen. Wird ein Deployment auf ei-

163
eclipse_v01.book Seite 164 Montag, 30. Januar 2006 12:02 12

Exkurs: Build Management mit Ant

ner Zielumgebung durchgeführt, so ist es ratsam, eine Überprüfung des Erfolgs dieses
Schritts vorzusehen.
쐌 TEST: Dies ist oft nicht ein einziges Target, sondern kann als Teilaufgabe nach Ab-
schluss der verschiedenen Targets vorkommen, z.B.:
– TEST_PREPARE: Kann feststellen, ob die benötigten Dateien aus dem Repository
gezogen wurden.
– TEST_COMPILE: Kann auf Fehlermeldungen und Warnungen des Compilers rea-
gieren und den Prozess beenden oder Nachrichten versenden.
– TEST_DEPLOY: Kann feststellen, ob eine Bereitstellung stattgefunden hat, also
z.B. eine Datei an einem bestimmten Ort erzeugt wurde, bzw. ob sich ein Programm
starten lässt. Es kann auch vorbereitete Testszenarien starten.
쐌 REPORT: Hier geht es im Wesentlichen darum, aus der möglicherweise unübersichtli-
chen Menge von Status-, Warn- und Fehlermeldungen einige Kernaussagen zu extrahie-
ren und diese gezielt darzustellen. Weiterhin gilt es, die darzustellenden Informationen
an die geeigneten Adressaten zu verteilen.
쐌 ALL: Die Standardaufgabe für ein komplettes Build.
Natürlich kommt man meist nicht mit einem einzigen Target pro Ziel aus, so ist es z.B. rat-
sam, jeweils ein Target compile pro Paket zur Verfügung zu stellen. Damit ergibt sich auch
wieder die Analogie zum internen inkrementellen bzw. kompletten Build. Als Konsequenz
hieraus sollten auch prepare und deploy paketweise arbeiten können. Und schließlich ist
noch zu beachten, dass Abhängigkeiten zwischen Paketen auch korrekt widergespiegelt
sind.

5.5.3 Ein Ant-Beispielskript


Nachfolgend ein kurzes Skript, das die genannten Ziele mit einfachen Anweisungen gefüllt
enthält:

<project name="MeinBeispiel" default="all" basedir=".">


<description> Einfaches Beispiel zu einem vollstaendigen Build
und Deployment. Es wird eine Version aus CVS ausgecheckt
und eine Test- bzw. Auslieferungsversion erstellt.
Eine Nachricht wird mittels des MailLoggers abgeschickt.
Das Skript soll daher mit der Option
"-logger org.apache.tools.ant.listener.MailLogger"
gestartet werden.
</description>
<!-- Hier werden einige globale und zielabhaengige Properties
definiert -->
<target name="initForTest" if="forTest" unless="forDist">
<!-- Initialisierung fuer Testbetrieb. Diese wird ausgefuehrt,
wenn ANT mit der Option '-DforTest' aufgerufen wird-->
<property name="TARGET_NAME" value="TestUmgebung"/>
<property name="TEST_INIT" value="true" />
<echo message="Initialisierung Test-Version durchgefuehrt!"/>
</target>

164
eclipse_v01.book Seite 165 Montag, 30. Januar 2006 12:02 12

5 – Build Management mit Eclipse

<target name="initForDist" if="forDist">


<!-- Initialisierung fuer Auslieferung. Diese wird ausgefuehrt,
wenn ANT mit der Option '-DforDist' aufgerufen wird-->
<property name="TARGET_NAME" value="Produktivumgebung"/>
<property name="DIST_INIT" value="true" />
<echo message="Initialisierung Auslieferung ausgefuehrt"/>
</target>
<target name="initReportForTest" if="TEST_INIT">
<!-- Initialisierung des Reports fuer Testbetrieb. Diese wird
ausgefuehrt, wenn ANT mit der Option
'-DforTest' aufgerufen wird-->
<property name="MailLogger.from" value="Ant Build Skript"/>
<property name="MailLogger.failure.to"
value="BuildManager@myproject.xxx, Programmer@myproject.xxx"/>
<property name="MailLogger.failure.subject"
value="Fehler beim Build der Testversion"/>
<property name="MailLogger.success.to"
value="BuildManager@myproject.xxx, Tester@myproject.xxx"/>
<property name="MailLogger.success.subject"
value="Neue Testversion erfolgreich erstellt"/>
<echo message="Initialisierung fuer Report der Test-Version
durchgefuehrt!"/>
</target>
<target name="initReportForDist" if="DIST_INIT">
<!-- Initialisierung des Reports fuer eine Distribution. Diese
wird ausgefuehrt, wenn ANT mit der Option
'-DforDist' aufgerufen wird-->
<property name="MailLogger.from" value="Ant Build Skript"/>
<property name="MailLogger.failure.to"
value="BuildManager@myproject.xxx, Programmer@myproject.xxx"/>
<property name="MailLogger.failure.subject"
value="Fehler beim Build der neuen Version"/>
<property name="MailLogger.success.to"
value="BuildManager@myproject.xxx, Nutzer@myproject.xxx"/>
<property name="MailLogger.success.subject"
value="Neue Version erfolgreich erstellt"/>
<echo message="Initialisierung fuer Report zur Distribution
durchgefuehrt!"/>
</target>
<target name="initReport"
depends="initReportForTest, initReportForDist">
<!-- Hier koennen fuer alle Versionen geltende Parameter, z.B.
die weiteren Mail-Parameter definiert werden -->
<echo message="Initialisierung fuer Report abgeschlossen!"/>
</target>
<target name="init"
depends="initForTest, initForDist, initReport">

165
eclipse_v01.book Seite 166 Montag, 30. Januar 2006 12:02 12

Exkurs: Build Management mit Ant

<!-- allgemeine Initialisierung, z.B. Setzen von Parametern,


die von der gewählten Version unabhängig sind.-->
<property name="src" location="temp/src"/>
<property name="build" location="temp/build"/>
<mkdir dir="${src}"/>
<mkdir dir="${build}"/>
<tstamp prefix="start">
<echo message="Initialisierung abgeschlossen!"/>
</target>
<target name="prepare" depends="init"
description="Vorbereitungen zum Kompilieren">
<!-- Bezieht den Code aus dem CVS Repository und legt ihn in
${src} ab -->
<cvs cvsRoot=":pserver:cvsname@cvs.projektserver.de:/home/cvs"
package="mypackage" dest="${src}" failonerror="false" />
<echo
message="Vorbereitung zur Versionserstellung abgeschlossen!"/>
</target>
<target name="compile" depends="prepare"
description="kompiliert den Code">
<!-- Kompiliert den Code aus ${src} und legt in ${build} ab -->
<javac srcdir="${src}" destdir="${build}"
failonerror="true" />
<available file="myProject/output.class"
property="Myclass.present"/>
<echo message="Kompilierung der Version abgeschlossen!"/>
</target>
<target name="javadoc" depends="prepare"
description="Erstellung der automatischen Dokumentation ">
<javadoc sourcepath="${src}" destdir="${build}" />
<echo message="Javadoc fuer Version erstellt!" />
</target>
<target name="failedTest" depends="compile"
unless="Myclass.present"
description="beendet die Ausfuehrung wenn das Kompilat
unvollständig ist">
<fail message="Build nach Kompilierfehler abgebrochen!"/>
</target>
<target name="test" depends="compile, failedTest"
description="testet das Kompilat">
<!-- Hier koennen z.B. JUnit-Tests gestartet und ausgewertet
werden -->
<echo message="Test der Version abgeschlossen!"/>
</target>
<target name="deployForTest" if="TEST_INIT"
description="Auslieferung einer Testversion">
<!-- Erstellt ein neues Verzeichnis fuer eine Test-Version, legt
das Kompilat in einem .jar-File dort ab -->

166
eclipse_v01.book Seite 167 Montag, 30. Januar 2006 12:02 12

5 – Build Management mit Eclipse

<mkdir dir="${dist}/lib"/>
<jar jarfile="${dist}/lib/MyProjectTest.jar"
basedir="${build}"/>
<echo message="Auslieferung der Test-Version abgeschlossen!"/>
</target>
<target name="deployForDist" if="DIST_INIT"
description="Auslieferung einer Distributionsversion">
<!-- Erstellt ein neues Verzeichnis fuer eine Distribution, legt
das Kompilat in einem .jar-File dort ab -->
<mkdir dir="${dist}/lib"/>
<jar jarfile="${dist}/lib/MyProject.jar" basedir="${build}"/>
<echo message="Auslieferung der Auslieferungs-Version
abgeschlossen!"/>
</target>
<target name="deploy"
depends="compile, javadoc, deployForTest, deployForDist"
description="Aufruf zum Erstellen einer Distribution">
<!-- Es koennen weitere versionsunabhaengige Schritte folgen -->
<echo message="Auslieferung abgeschlossen!"/>
</target>
<target name="cleanup" depends="init"
description="Entfernt temporaere Daten und Verzeichnisse">
<delete dir="${src}"/>
<delete dir="${build}"/>
<echo message="Temporaere Dateien und Verzeichnisse
aufgeraeumt"/>
</target>
<target name="all" depends="deploy,test,javadoc,cleanup"
description="vollstaendige distribution">
<!-- Distribution wird durch Abhaengigkeit erstellt, hier wird
das abschliessende Reporting erstellt -->
<echo message="Erstellung der Distribution beendet!"/>
<echo message="Temporaere Verzeichnisse wieder aufgeraeumt!"/>
</target>
</project>

Die einzelnen Aufgaben sind hier natürlich sehr einfach. Die Struktur kann als Gerüst ge-
nutzt werden und den individuellen Anforderungen entsprechend angepasst werden. Dieses
Skript ist entlang der Entwicklungsschritte zum Standard-Target geführt:
INIT à PREPARE à COMPILE à DISTRIBUTE à ALL
Diese Kette ist durch die Abhängigkeiten (Attribute: DEPENDS) definiert. Das Standard-Tar-
get ist noch erweitert durch die beiden Ziele TEST und CLEANUP. TEST hängt von COMPILE
ab und kann auch als selbstständiges Ziel aufgerufen werden. CLEANUP hängt nur von INIT
ab, daher kann es auch dann verwendet werden, wenn einer der Schritte PREPARE oder
COMPILE abgebrochen ist und wieder aufgeräumt werden soll.

167
eclipse_v01.book Seite 168 Montag, 30. Januar 2006 12:02 12

Exkurs: Build Management mit Ant

Da es keine weiteren Beziehungen zwischen TEST und CLEANUP gibt, werden diese beiden
Ziele in der aufgeführten Reihenfolge ausgeführt. Dies erlaubt es, dass die Tests auch auf
den Zwischenergebnissen laufen können und nicht allein auf dem Endergebnis. So können
Hilfsklassen o.Ä. bei den Tests zur Verfügung gestellt werden, die im Endprodukt nicht ent-
halten sein sollen.
Die Dokumentationserstellung ist hier durch JavaDoc in dem Target JAVADOC realisiert.
Sie ist lediglich von PREPARE abhängig, kann also auch erstellt werden, ohne dass COM-
PILE aufgerufen wird. Umgekehrt ist DEPLOY wieder von JAVADOC abhängig, so wird die
Dokumentation als Bestandteil der Auslieferung eingebunden.
Es werden zwei Versionsarten angeboten, eine Testversion und eine Auslieferungsversion.
Die Schritte, die unterschiedlich für die beiden Versionen sind, werden jeweils in einen ver-
sionsunabhängigen Teil und die versionsabhängigen Teile aufgespaltet. Der unabhängige
Teil, z.B. INIT, wird in den allgemeinen Abhängigkeiten aufgeführt. Die abhängigen Teile,
hier initForDist, initForTest, werden von INIT eingebunden.
Über eine Kommandozeilenoption wird bestimmt, welche Version erstellt wird. Es kann
nur eine Version pro Lauf erstellt werden. Dies wird ebenfalls mit dem INIT-Ziel geregelt
und durch eine PROPERTY weitergegeben. Somit ist ein Fehler durch die gleichzeitige Erstel-
lung von Versionen mit unterschiedlichen Parametern weitestgehend ausgeschlossen.
Für REPORT wurde in diesem Skript auf die eingebaute Funktion MailLogger zurückge-
griffen, um eine Benachrichtigung der betroffenen Personen (Programmierer, Tester, Nut-
zer) umzusetzen. Ein sehr leistungsstarkes Werkzeug ist durch die Unterstützung von
„Log4J“ bereitgestellt. Hierdurch können sehr individuell und gezielt Informationen über
den Fortschritt oder Fehler im Build gesammelt und aufbereitet werden.1
Es wurden noch einige weitere Funktionen und Möglichkeiten von Ant eingebaut, die bei
weitem nicht den vollen Funktionsumfang dieses Werkzeugs abbilden, sondern lediglich
einen Eindruck von den Einsatzmöglichkeiten vermitteln sollen:
쐌 Einige Targets sind mit Bedingungen verknüpft, z.B. in
<target name="deployForDist" if="DIST_INIT"
Dieser Zusatz bewirkt, dass das entsprechende Target nur ausgeführt wird, wenn die
genannte Property definiert ist. (Es ist eine Eigenart von Ant, dass hier die reine Exis-
tenz der Property das Kriterium zur Erfüllung der Bedingung ist, nicht deren Wert.)
Die Bedingung wird in dem Moment geprüft, wenn das Target entsprechend der
Abhängigkeiten ausgeführt werden soll. Die Negation der if-Bedingung ist durch
unless gegeben.
쐌 Das init-Target enthält die Zeile
<tstamp prefix="start">
Dies ist eine gängige Praxis zum Erzeugen einer Property, die einen String mit der
aktuellen Zeit oder dem Datum enthält. Dieser kann z.B. verwendet werden, um Datei-
namen mit Zeitstempel zu generieren.

1. Eine Beschreibung dieses Tools würde hier zu weit führen. Informationen und Downloads zu Log4J sind unter
http://logging.apache.org/log4j/docs/ zu finden.

168
eclipse_v01.book Seite 169 Montag, 30. Januar 2006 12:02 12

5 – Build Management mit Eclipse

쐌 Im compile-Target ist die folgende Zeile gesetzt:


<available file="myProject/output.class"
property="Myclass.present"/>
Diese bewirkt das Setzen der Property Myclass.present auf den Wert true, falls
die genannte Datei gefunden wird. Hier wird diese Anweisung als einfache Fehler-
erkennung zum Kompiliervorgang verwendet (wobei es sicherlich bessere Methoden
gibt). Wenn diese Property nicht gesetzt wird, wird das Target failedTest ausge-
führt, das durch die Zeile
<fail
message="Build Prozess nach Kompilierfehler abgebrochen!"/>
das Build mit einer Nachricht abbricht.

5.5.4 Ant in Eclipse: externe Builds


Wir möchten diesen Ausflug zu Ant und zum Build Management mit der Zusammenfüh-
rung von Ant und Eclipse abschließen und zeigen, wie Eclipse die Verwendung von Ant als
„externes Build“ unterstützt.
Als externe Builds werden in Eclipse alle Werkzeuge und Methoden bezeichnet, die ein
Build auf eine andere als in Abschnitt 5.3 beschriebene Weise erzeugen. Eine Methode
hierzu haben wir bereits in Abschnitt 5.3.4 kennen gelernt. Wenn nämlich eine SHARED
CONFIGURATION erstellt wurde, d.h., eine Konfiguration zum Build für die gemeinsame Nut-
zung durch andere Teammitglieder bereitgestellt wurde, so kann diese von dem Team als
externes Build eingelesen und verwendet werden.

Abb. 5.7: Aufruf eines Ant Build-Skripts

Zur Verwendung einer SHARED CONFIGURATION wählen wir das Menü RUN|EXTERNAL
TOOLS|EXTERNAL TOOLS.., hierzu siehe auch Abb. 5.7. Mit der Auswahl von PROGRAM und der

169
eclipse_v01.book Seite 170 Montag, 30. Januar 2006 12:02 12

Exkurs: Build Management mit Ant

Schaltfläche NEW können wir eine neue Konfiguration erstellen lassen. Unter dem Reiter
MAIN können wir im Feld BUILDFILE die Datei mit der gewünschten Konfiguration eintra-
gen. Falls die Datei fehlerhaft ist, wird eine entsprechende Meldung angezeigt, sonst wird
die Schaltfläche RUN aktiviert und wir können das Build hierüber starten.
Wir können eine Ant Build-Datei wie folgt aufrufen: Wir wählen die Build-Datei (meistens
build.xml) im Navigator aus.1 Zum Starten eines Build mittels Ant-Skript wählen wir RUN |
EXTERNAL TOOLS | RUN AS | ANT BUILD. Mit der Ausführung der Datei wird eine Console ge-
öffnet, in der die Meldungen und Fehlerhinweise angezeigt werden.
Die Integration von Ant in Eclipse geht allerdings noch weiter, so können wir Ant als Werk-
zeug für alle Builds verwenden, indem die automatischen Builds ebenfalls dieses externe
Tool verwenden. Zum Aktivieren von Ant für automatische Builds sind die folgenden Ein-
stellungen vorzunehmen:
In den PROJEKT PROPERTIES (zu erreichen z.B. durch Auswahl des Projekts in der Navigation,
dann mit der Tastenkombination Ç-Æ) wählen wir den zweiten Punkt BUILDERS aus.
Im rechten Fenster erscheint eine Liste der eingebundenen Builder. Standardmäßig ist hier
der interne Java Builder eingetragen. Der Java Builder ist automatisch konfiguriert, er kann
weder editiert noch deaktiviert werden (die Schaltfläche DISABLE ist abgeschaltet). Die Rei-
henfolge in der Liste entscheidet über die Ausführungsreihenfolge. Der Java Builder steht
standardmäßig an erster Stelle, wird also zuerst verwendet. Ein anderer Builder kann aber
mithilfe der Schaltflächen UP und DOWN auch an die erste Stelle gesetzt werden.
Mit der Schaltfläche NEW können wir neue Builder anlegen, wir wählen hier den Ant Build
aus und bestätigen mit OK. Es öffnet sich ein Fenster wie in Abb. 5.8 dargestellt, in dem die
Einstellungen des Builder vorgenommen werden. Wir können dieses Fenster mit OK schlie-
ßen. Wir sehen nun einen weiteren Eintrag NEW_BUILDER für Ant in der Liste der Builder.

Abb. 5.8: Konfiguration eines Ant Builder

1. Ggf. muss die Datei zuerst in ein zum Projekt gehörigen Verzeichnis verschoben werden.

170
eclipse_v01.book Seite 171 Montag, 30. Januar 2006 12:02 12

5 – Build Management mit Eclipse

Durch die Auswahl dieses Ant Builder werden auch die übrigen Schaltflächen aktiviert,
wovon wir nun die Schaltfläche EDIT näher betrachten wollen. Wird diese gewählt, so öffnet
sich wieder das Fenster für die Einstellungen des Ant Builder.
Unter dem Reiter MAIN werden die Hauptpfade eingestellt. Für die Dateien und Verzeich-
nisse kann entweder durch den Workspace gesucht werden, d.h., es wird die in Eclipse ver-
wendete Projektstruktur durchsucht, oder durch die reale Verzeichnisstruktur gesucht wer-
den. Es lassen sich aber auch bereits definierte Variablen verwenden.
Der erste Eintrag zeigt auf die zu benutzende Build-Datei von Ant (standardmäßig
build.xml). Der zweite Eintrag enthält das Hauptverzeichnis für das Build.
Der dritte Eintrag enthält die Aufrufparameter zum Start des Builder. Dies können zum
einen allgemeine Angaben sein, zum anderen können hier Variablen verwendet werden, die
im Kontext der Ausführung entsprechend ersetzt werden. Eine Liste der verfügbaren Vari-
ablen ist durch die Schaltfläche rechts vom Eingabefeld einzusehen. Ein Beispiel einer sol-
chen Variablen ist ${build_type}: Je nach Aufrufsituation wird diese Variable ersetzt
durch die Werte incremental, full, auto oder none. (Allerdings ist dies nur dann sinn-
voll, wenn in der Ant-Konfiguration auch diese Werte verwendet werden.) Bei einigen Va-
riablen, die Pfadangaben zum Inhalt haben, erscheint im unteren Teil des Fensters eine Aus-
wahl, ob die Standardpfade oder spezifische Einstellungen verwendet werden sollen, sowie
ein Navigationsfenster, in dem die Auswahl spezifischer Verzeichnisse vorgenommen wer-
den kann.
Schließlich ist hier noch ein Häkchen CAPTURE OUTPUT gesetzt, welches bewirkt, dass der
Ant-Output von Eclipse aufgenommen und angezeigt bzw. ignoriert wird.
Unter dem Reiter TARGETS werden die Targets mit ihrer Beschreibung (Description aus
der Konfigurationsdatei) aufgelistet und bestimmt, welche ausgeführt werden sollen. Hier
wird auch die Default-Auswahl aus dem Skript angezeigt, diese muss aber nicht übernom-
men werden, d.h., dieses Target muss nicht zwangsläufig ausgeführt werden. Es können
hier auch mehrere Targets ausgewählt werden, hierbei entscheidet die Reihenfolge das An-
klickens auch über die Reihenfolge ihrer Ausführung. Diese wird im unteren Teil des Fens-
ters aufgelistet und lässt sich dort auch nachträglich editieren. Der Hauptnutzen dieser Ein-
stellungen ist wohl darin zu sehen, dass hier verschiedene Builder für unterschiedliche
Zwecke konfiguriert werden können, welche alle auf dieselbe Datei build.xml zugreifen.
Zwei Checkboxen SORT TARGETS (Targets alphabetisch sortieren gegenüber Anzeige in der
Reihenfolge im build.xml) und HIDE INTERNAL TARGETS (Ausblenden der als intern erkannten
Targets) dienen der leichteren Bedienung dieses Fensters.
Mit REFRESH wird bestimmt, welche Ressourcen nach dem Build aktualisiert werden sollen.
Laut Voreinstellung wird keine Aktualisierung vorgenommen, was dazu führen kann, dass
eine neu erstellte Datei oder ein Verzeichnis nicht direkt z.B. im Navigator sichtbar ist. Es
werden hier verschiedene Stufen angeboten, vom gesamten Arbeitsbereich bis zu einzelnen
Verzeichnissen oder Dateien. Falls nicht konkrete projektspezifische Gründe dem entge-
genstehen, sollte man hier zur Vermeidung von Inkonsistenzen und Verwirrung eine groß-
zügige Einstellung treffen, d.h. den gesamten Arbeitsbereich oder das betroffene Projekt
aktualisieren lassen.

171
eclipse_v01.book Seite 172 Montag, 30. Januar 2006 12:02 12

Exkurs: Build Management mit Ant

Unter CLASSPATH, JRE und ENVIRONMENTS werden die Parameter für die Ausführung von
Ant bestimmt. Das Vorgehen für diese Einstellungen ist ganz analog zu den entsprechenden
Einstellungen für das interne Build. Hierbei können unter ENVIRONMENTS nur dann Einträge
vorgenommen werden, wenn für das Build eine andere JRE verwendet werden soll als für
den Workspace. Gegebenenfalls kann man unter JRE die Auswahl SEPARATE JRE einstellen
und die gleiche Version auswählen wie sie der Workspace verwendet. Möchte man Ant so-
wohl über Eclipse als auch eigenständig verwenden, so sollten diese Parameter möglichst
denen entsprechen, die auch für eine Ausführung des Build ohne Eclipse, z.B. über die
Kommandozeile, gelten, damit die Ausführung in beiden Fällen identisch verläuft.
Unter PROPERTIES lassen sich weitere Eigenschaften bestimmen. Diese können entweder
den globalen Properties aus Ant entsprechen (und sind hier nicht mehr veränderbar), hier
definiert werden oder aus anderen Property-Dateien eingelesen werden. Die Definition er-
folgt über ADD PROPERTY und besteht aus einem Namen und einem Wert, der entweder fest
bestimmt wird oder auf die in Eclipse vorhandenen Variablen aufbaut.
Schließlich kann unter BUILD OPTIONS festgelegt werden, wann dieses Tool für ein Build
verwendet werden soll. Hier können wir also zum Beispiel bestimmen, dass Ant für manu-
ell gestartete Builds verwendet wird, während bei automatischen Builds der interne Java
Builder benutzt werden soll. Die Einstellung ist eine Kombination aus dem Setzen der ent-
sprechenden Häkchen und der oben beschriebenen Reihenfolge, in der die externen und in-
ternen Builder aufgelistet sind. Tritt ein Ereignis wie der Aufruf eines manuellen Build ein,
so wird die Liste der definierten Builder von oben nach unten durchlaufen, bis der erste
Builder gefunden wird, bei dem das entsprechende Häkchen gesetzt ist.
Sind alle Angaben gemacht worden, kann die Eingabe mit dem OK-Button abgeschlossen
werden. Wird nun ein Build gestartet, so werden die ausgewählten Targets der Reihe nach
durchgeführt. Die Status- und Fehlermeldungen werden im Console-Fenster angezeigt.
Ant und eine existierende Konfigurationsdatei build.xml können aber auch noch auf andere
Weise in Eclipse integriert werden, indem es als ein eigenes Projekt aufgefasst wird. Hier-
bei beginnen wir über FILE | IMPORT | EXISTING ANT BUILDFILE ein neues Projekt. In dem er-
scheinenden Fenster können wir einen Projektnamen vergeben und die Datei build.xml mit
Pfad oder durch Navigation im Arbeitsbereich eintragen. Wird die Datei im Navigator aus-
gewählt und mit WINDOWS | SHOW VIEW | ANT geöffnet, so erkennt Eclipse die Struktur und
stellt diese als Outline dar (siehe unten links in Abb. 5.8).
Klicken wir mit der rechten Maustaste auf ein Target und wählen die Option RUN ANT ..., so
gelangen wir wieder zu dem Konfigurationsfenster wie in Abb. 5.8. Die Einstellungsmög-
lichkeiten entsprechen den bereits oben beschriebenen. Sie werden allerdings hier als eine
Konfiguration gespeichert. Zusätzlich steht ein Reiter COMMON zur Verfügung, mit dem die-
se Konfiguration wieder als SHARED CONFIGURATION gekennzeichnet und zur Benutzung im
Team abgelegt werden kann. Wir können auch mehrere Konfigurationen erstellen, ihnen
Namen geben und später weiterverarbeiten.
Zum Ausführen kann nun ein Target per Doppelklick aufgerufen werden. Falls eine einzige
Konfiguration existiert, wird sofort eine Console eingeblendet und das Target ausgeführt.
Bei mehreren Konfigurationen erscheint erst ein kleines Fenster, in dem ausgewählt werden
kann, welche Konfiguration für die Ausführung verwendet werden soll.

172
eclipse_v01.book Seite 173 Montag, 30. Januar 2006 12:02 12

5 – Build Management mit Eclipse

5.6 Zusammenfassung
Mit diesen Ausführungen möchten wir das Thema Build Management beenden. Wir haben
die internen Einstellmöglichkeiten von Eclipse zum Build kennen gelernt. Es wurde der
CVS-Client von Eclipse vorgestellt und die Verknüpfung von Versions- und Build Manage-
ment umrissen. Wir haben die typischen Aufgaben im Build Management kurz erläutert
und deren technische Umsetzung mit Hilfe von Ant angerissen. Schließlich wurden ver-
schiedene Arten der Integration von Ant als Beispiel eines externen Tools in Eclipse darge-
stellt.
Es sei noch einmal darauf hingewiesen, dass die Unterstützung von Ant in Eclipse eine her-
vorragende Ergänzung von Eclipse durch ein anderes gutes Werkzeug darstellt. Außerdem
können beide durch ihren Charakter als offene und erweiterbare Plattformen auf die indivi-
duellen Projektgegebenheiten zugeschnitten werden. Die effiziente Nutzung dieser Werk-
zeuge erfordert allerdings auch eine Struktur und vorausschauende Planung des Build als
ein sich wiederholender Prozess. In einem Entwicklungsprojekt sollte man sich damit früh-
zeitig um das Build Management Gedanken machen und sich mit den einzelnen Stufen und
geeigneten Werkzeugen vertraut machen. Wenn man mit einem guten Augenmaß den rich-
tigen Weg zu einer effizienten Lösung dieser Aufgaben findet, sind bereits einige Stolper-
steine und Fallen aus dem Weg geräumt und die eigentliche Entwicklung kann ungehindert
durchgeführt werden.

173
eclipse_v01.book Seite 174 Montag, 30. Januar 2006 12:02 12
eclipse_v01.book Seite 175 Montag, 30. Januar 2006 12:02 12

6 GUI-Design mit Eclipse

von Dr. Birgit Schwartz-Reinken

Für die Entwicklung von Graphical User Interfaces (GUIs) existieren verschiedene Biblio-
theken. Unabhängig davon, welche Bibliothek verwendet wird (AWT, Swing oder SWT),
ist die Entwicklung von GUIs in Java sehr schreibintensiv, sofern hierfür keine Werkzeuge
eingesetzt werden. Im Folgenden werden daher die beiden GUI-Designer Visual Editor für
Java und WindowBuilder vorgestellt, die ein visuelles Entwerfen von GUIs mit AWT-,
Swing- und SWT-Komponenten ermöglichen.
Die Darstellung beginnt jeweils mit der Beschreibung der Installation und des Aufrufs des
Designers. Anschließend wird erläutert, wie Komponenten hinzugefügt und entfernt, Ei-
genschaften und Layouteinstellungen vorgenommen und Ereignisse für Komponenten defi-
niert werden können. Da wir beim Visual Editor sowohl die Erstellung eines GUI mit
Swing- als auch die Erstellung eines GUI mit SWT-Komponenten beschreiben, ist anschlie-
ßend eine differenzierte Betrachtung erforderlich. Für beide Fälle werden zunächst die je-
weils unterstützten Komponenten und die Layouteinstellungsmöglichkeiten vorgestellt, be-
vor der automatisch generierte Quelltext und ein Beispiel betrachtet werden. Im Falle des
WindowBuilder beschränken wir uns auf die Beschreibung der Erstellung eines GUI mit
SWT-Komponenten, so dass wir direkt anschließend an die Betrachtung der Ereignisverar-
beitung den automatisch generierten Quelltext und ein Beispiel vorstellen.
Während für die Darstellung der GUI-Designer das AWT und die Swing-Technologie als
bekannt vorausgesetzt werden, erfolgt vor den Betrachtungen zur Erstellung eines GUI mit
SWT-Komponenten eine Einführung in SWT. Sie umfasst zunächst eine kurze Abgrenzung
der drei Grafikbibliotheken AWT, Swing und SWT. Anschließend beschreiben wir den
Aufbau einer Applikation. Daran schließt sich die Erläuterung einer Reihe wichtiger Klas-
sen sowie der Layoutmanager an. Als Letztes wird die Ereignisverarbeitung dargestellt.
Die Beschreibung von SWT erhebt hierbei nicht den Anspruch der Vollständigkeit. Viel-
mehr beschränken wir uns im Wesentlichen auf die Bereiche, die zum Verständnis des Ar-
beitens mit den Designern erforderlich sind.

6.1 Allgemeines
Die beiden im Folgenden vorgestellten GUI-Designer funktionieren nach dem gleichen
Prinzip. Eine Komponente wird dem GUI hinzugefügt, indem der Benutzer die gewünschte
Komponente mit der Maus in einer Liste anwählt und dann ebenfalls mithilfe der Maus in
einer baumartigen Darstellung des GUI oder in einer WYSIWYG-Darstellung an der ge-
wünschten Position platziert. Die Eigenschaften einer Komponente müssen ebenfalls nicht
im Quelltext eingegeben werden. Stattdessen steht ein Fenster zur Verfügung, in dem je-
weils die Eigenschaften einer Komponente eingestellt werden können. Des Weiteren unter-
stützen die GUI-Designer verschiedene Layoutmanager. Hinsichtlich der Ereignisverarbei-
tung wird der Benutzer insoweit unterstützt, als er für eine Komponente aus einer Liste von

175
eclipse_v01.book Seite 176 Montag, 30. Januar 2006 12:02 12

Der Visual Editor für Java

Ereignissen das gewünschte auswählen kann. Im Quelltext werden daraufhin das zuge-
hörige Interface sowie die zugehörige Methodendeklaration implementiert. Die weitere Im-
plementation muss dann durch den Benutzer im Quelltext vorgenommen werden.
Weiterhin ist bei beiden Designern jederzeit eine Synchronisation zwischen dem Quelltext
und den verschiedenen Sichten bzw. Fenstern der Designer möglich. Das heißt, Änderun-
gen, die in einer Sicht eines Designers gemacht werden, wie z.B. das Hinzufügen einer neu-
en Komponente oder das Ändern einer Eigenschaft, werden sofort im Quelltext angezeigt.
Umgekehrt werden aber auch Änderungen, die statt in einer Sicht direkt im Quelltext vor-
genommen werden und die die grafische Darstellung betreffen, in den Sichten des De-
signers angezeigt. Beim Visual Editor kann darüber hinaus die Synchronisation unterbro-
chen – in diesem Fall sind nur noch Änderungen im Quelltext möglich – und bei Bedarf
wieder gestartet werden. Ferner lässt sich die Zeitspanne zwischen Änderung und Synchro-
nisation variieren. Voreingestellt ist hier 1 sec.
Beide Designer verfügen
쐌 über eine Sicht, welche das GUI in einer baumartigen Struktur anzeigt,
쐌 über eine Sicht, welche das GUI in einer Art WYSIWYG-Darstellung anzeigt, und
쐌 über eine Sicht, welche die Eigenschaften einer angewählten Komponente anzeigt.
Des Weiteren erzeugen beide Designer eine Java-Quelltextdatei und keine weiteren Datei-
en. Dieses hat den Vorteil, dass bereits anderweitig erstellte Java-Quelltextdateien mit ei-
nem Designer weiterbearbeitet werden können.

6.2 Der Visual Editor für Java


Das Eclipse-Projekt Visual Editor stellt ein Framework bereit, um GUI Builder für Eclipse
zu entwickeln. Es gibt Referenzimplementationen für Swing und SWT GUI Builder. Mit
dem Framework können aber auch GUI Builder für andere Programmiersprachen entwi-
ckelt werden. Wir betrachten im Folgenden die Referenzimplementation für Java.
Der Visual Editor für Java ermöglicht das visuelle Entwerfen von GUIs, die AWT- und
Swing- sowie SWT-Komponenten beinhalten. Er wurde als Subprojekt des Eclipse-Tools-
Projekts entwickelt und steht als Download auf www.eclipse.org/vep mit seinem gesamten
Funktionsumfang als Open-Source-Produkt zur Verfügung. Eine Dokumentation ist eben-
falls kostenlos verfügbar. Sie wird beim Installieren in die Eclipse-Hilfe integriert.
Im Folgenden wird die Version 1.1.0.1 betrachtet, die unter Eclipse 3.1 lauffähig ist.

6.2.1 Installation und Aufruf


Zum Installieren des Visual Editors muss für die hier betrachtete Version 1.1.0.1 die Datei
VE-runtime-1.1.0.1.zip in das Eclipse-Verzeichnis entpackt werden. Hierbei ist zu beach-
ten, dass die in der zip-Datei gespeicherten Pfadangaben verwendet werden. War die Instal-
lation erfolgreich, so erscheint nach dem Starten von Eclipse unter HELP | ABOUT ECLIPSE
SDK | PLUG-IN DETAILS das Visual Editor-Plugin. Sollten das Eclipse Modeling Framework
(EMF) und das Graphical Modeling Framework (GEF) nicht installiert sein, müssen darü-
ber hinaus noch die Dateien GEF-runtime-3.1.1.zip und emf-sdo-runtime-2.1.1.zip in das

176
eclipse_v01.book Seite 177 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

Eclipse-Verzeichnis entpackt werden. Auch hier sind die in der zip-Datei gespeicherten
Pfadangaben zu verwenden.
Bevor wir eine oder mehrere Klassen eines GUI mit dem Visual Editor (VE) implementie-
ren können, müssen wir zunächst ein Java-Projekt erstellen. Um dann anschließend eine
neue Klasse mit dem Visual Editor programmieren zu können, rufen wir FILE | NEW | OTHER
auf. Im daraufhin erscheinenden Fenster wählen wir unter WIZARDS den Eintrag VISUAL
CLASS. Ist dieser nicht sichtbar, müssen wir zuvor auf das +-Zeichen vor JAVA klicken. Nach
dem Klicken auf NEXT benennen wir im nächsten Fenster unsere neue Klasse und wählen
unter STYLE diejenige Klasse, die unsere Klasse erweitern soll. Diese wird daraufhin unter
SUPERCLASS angezeigt. Alternativ können wir die Superklasse auch direkt unter SUPERCLASS
angeben.
Existiert in einem Projekt bereits eine Klasse, so lässt sie sich im Visual Editor öffnen, in-
dem sie im Package Explorer angewählt wird und anschließend im Kontextmenü über OPEN
WITH | VISUAL EDITOR geöffnet wird.

Abb. 6.1: Integration des Visual Editors in Eclipse

Der Visual Editor besitzt keine eigene Perspektive, sondern wird, wie die Abb. 6.1 zeigt, in
die Java-Perspektive integriert. Statt des Java Editor-Fensters wird das Visual Editor-Fens-
ter angezeigt. Dieses besteht aus einer Design View, einer Source View und der aufklapp-
baren Palette mit den Komponenten. Design View und Source View sind standardmäßig in

177
eclipse_v01.book Seite 178 Montag, 30. Januar 2006 12:02 12

Der Visual Editor für Java

der linken Fensterhälfte des Visual Editor-Fensters untereinander angeordnet. Die Palette
findet sich am rechten Fensterrand. Über WINDOW | PREFERENCES | JAVA | VISUAL EDITOR | AP-
PEARANCE | ON SEPARATE NOTEBOOK TABS können wir dafür sorgen, dass die Design View und
die Source View für eine Klasse auf separaten Registerkarten angeordnet werden, wobei die
Palette in diesem Fall nur bei der Design View angezeigt wird. Es sei noch einmal darauf
hingewiesen, dass die Änderung der Einstellung für bereits im Visual Editor-Fenster ange-
zeigte Klassen erst wirksam wird, wenn die Klassen im Visual Editor geschlossen und dann
wieder geöffnet werden.
Weiterhin wird die Java-Perspektive standardmäßig beim Öffnen des Visual Editors um
zwei weitere Sichten ergänzt, und zwar um die Java Beans View und die Properties View.
Die Java Beans View zeigt die Komponenten der gewählten Klasse als Baum. In der Pro-
perties View werden die Werte für die Eigenschaften einer in der Java Beans View oder De-
sign View angewählten Komponente angezeigt.

6.2.2 Hinzufügen von Komponenten


Die mithilfe des Visual Editors einfügbaren Komponenten sind in der Palette in verschie-
denen Kategorien zusammengefasst. Wird bei der Erstellung einer Klasse für ein GUI
unter FILE | NEW | OTHER | JAVA | VISUAL CLASS | STYLE ein AWT- oder Swing-Eintrag aus-
gewählt, werden nur die vier Kategorien Swing Components, Swing Containers, Swing
Menus und AWT Controls angezeigt. Bei Auswahl eines SWT-Eintrags dagegen werden
zusätzlich die drei Kategorien SWT Controls, SWT Containers und SWT Menus zur Ver-
fügung gestellt.
Bevor wir eine Komponente einem Container hinzufügen können, müssen wir zunächst die
gewünschte Komponente in der Palette anwählen. Hierzu gehen wir mit der Maus auf die
Palette. Nach einem kurzen Moment wird diese aufgeklappt und wir müssen dann, falls die
Komponenten einer Kategorie nicht angezeigt werden, zuvor auf die Kategorie klicken –
nochmaliges Klicken klappt die Kategorie wieder zu –, bevor wir die gewünschte Kompo-
nente anwählen können. Darüber hinaus lässt sich auch in der Palette der Befehl CHOOSE
BEAN anwählen und im daraufhin erscheinenden Fenster der gewünschte Klassenname ein-
geben. Der Stern * als Platzhalter ist hierbei möglich. Die Eingabe Button* liefert z.B. eine
Liste aller Klassen, deren Name mit Button beginnt. Hier können wir dann eine Klasse
auswählen. Durch Klicken auf den OK-Button müssen wir anschließend die Wahl bestä-
tigen.
Die angewählte Komponente kann jetzt entweder in der Design View oder in der Java
Beans View einer bestehenden Komponente hinzugefügt werden. Zum Hinzufügen in der
Design View bewegen wir die Maus, ohne die linke Maustaste gedrückt zu halten, an die
Position in der Design View, an der die neue Komponente platziert werden soll, und fügen
sie durch einen Klick ein.
Zum Hinzufügen in der Java Beans View wird die Maus zunächst in den Baum bewegt. Die
aktuelle Position wird hierbei durch eine dicke horizontale Linie angezeigt, bei der der An-
fang der Linie die Hierarchieebene wiedergibt. Ist die gewünschte Position erreicht, wird
die linke Maustaste losgelassen.

178
eclipse_v01.book Seite 179 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

Ist unter WINDOW | PREFERENCES | JAVA | VISUAL EDITOR | APPEARANCE die Option PROMPT FOR
BEAN NAME DURING CREATION aktiviert, erscheint sowohl in der Design View als auch in der
Java Beans View nach dem abschließenden Klick ein Fenster, das uns die Möglichkeit gibt,
den vom Visual Editor automatisch erzeugten Namensvorschlag zu ändern.
Wurde eine Komponente an der falschen Stelle eingefügt, kann sie entweder im Baum der
Java Beans View oder aber in der Design View verschoben werden. Beim Verschieben im
Baum der Java Beans View zeigt wiederum eine dicke horizontale Linie jeweils an, wo die
Komponente nach dem Loslassen der linken Maustaste eingefügt werden würde.

6.2.3 Entfernen von Komponenten


Alle Komponenten lassen sich in der Java Beans View entfernen, indem sie dort angeklickt
und mithilfe der ¢-Taste, über den DELETE-Button in der Werkzeugleiste oder den
DELETE-Befehl im Kontextmenü gelöscht werden. Die meisten Komponenten können wir
darüber hinaus auch direkt in der Design View entfernen. Hierzu wird die Komponente dort
angeklickt und mit der ¢-Taste gelöscht.

6.2.4 Ändern von Eigenschaften einer Komponente


Einige Eigenschaften von Komponenten lassen sich direkt in der Design View verändern.
So kann beispielsweise die Größe geändert werden, indem die Komponente angeklickt
wird und dann an den Markern gezogen wird. Die Position lässt sich dadurch ändern, dass
wir mit der Maus auf die Komponente gehen und dann bei gedrückt gehaltener linker Maus-
taste die Maus auf die gewünschte Position der Komponente bewegen. Für Beschriftungen
bei Buttons, Checkboxen usw. wird die Komponente, die eine Beschriftung erhalten soll
oder deren Beschriftung geändert werden soll, angeklickt und anschließend noch einmal
geklickt, um ein Texteingabefeld zu erhalten.
Alle Eigenschaften, auch die direkt in der Design View veränderbaren Eigenschaften, las-
sen sich in der Properties View einstellen. Hierfür gibt es drei verschiedene Möglichkeiten:
쐌 Für einige Eigenschaften können die Einstellungen direkt in der Spalte VALUE vorge-
nommen werden.
쐌 Bei einigen Eigenschaften können wir, wenn wir in die Spalte VALUE klicken, eine Liste
mit möglichen Einstellungen öffnen, von denen eine ausgewählt werden kann.
쐌 Für einige Eigenschaften lässt sich ein so genannter Java Property Editor öffnen. In die-
sem Fall muss für die gewählte Eigenschaft in der Spalte VALUE rechts das Symbol
erscheinen. Java Property-Editoren existieren für die Auswahl von Schriften, Farben,
Rahmen und Icons. In der Abb. 6.2 findet sich exemplarisch der Property Editor für
Schriften.

179
eclipse_v01.book Seite 180 Montag, 30. Januar 2006 12:02 12

Der Visual Editor für Java

Abb. 6.2: Der Property Editor für Schriften

6.2.5 Layouteinstellungen
Der Visual Editor stellt sowohl für AWT- und Swing-Container als auch für SWT-Container
verschiedene Layoutmanager zur Verfügung.
Die Auswahl eines Layoutmanagers erfolgt dadurch, dass wir den gewünschten Container
entweder in der Design View oder in der Java Beans View anklicken und anschließend in
der Properties View unter der Eigenschaft LAYOUT in der Spalte VALUE den Layoutmanager
wählen. Wenn wir anschließend auf das +-Zeichen vor der Eigenschaft LAYOUT klicken,
werden zusätzlich die allgemeinen Einstellungsmöglichkeiten für das gewählte Layout an-
gezeigt, wie z.B. die Anzahl der Spalten im Falle des GridLayouts, so dass jetzt direkt in der
Properties View Veränderungen der Einstellungen vorgenommen werden können. Für eine
Reihe von Layoutmanagern besteht darüber hinaus alternativ die Möglichkeit, voraus-
gesetzt der gewünschte Container ist angewählt, im Kontextmenü den Befehl CUSTOMIZE
LAYOUT oder in der Werkzeugleiste das Symbol anzuklicken und im daraufhin erschei-
nenden Customize Layout-Fenster im Register LAYOUT die Einstellungen vorzunehmen.
Die bei einigen Layoutmanagern möglichen individuellen Einstellungen für jede Kompo-
nente können wiederum entweder über die Properties View oder z.T. auch über das Cus-
tomize Layout-Fenster vorgenommen werden. Voraussetzung ist hier, dass die gewünschte
Komponente zuvor in der Design View oder in der Java Beans View angewählt worden ist.
Eine detailliertere Beschreibung hierzu findet sich in den Abschnitten 6.3.2 und 6.5.2.
Alternativ können wir den Layoutmanager aber auch auf null setzen, so dass Position und
Größe für jede Komponente explizit angegeben werden müssen. In diesem Fall stellt der
Visual Editor eine Reihe von Hilfen bereit. Wenn wir beispielsweise eine Komponente in
der Palette angewählt haben und anschließend den Mauscursor zum Hinzufügen in die De-
sign View bewegen, erscheint am Mauscursor eine gelbe Infobox, die die aktuelle Position
in Pixel angibt. Wenn wir, statt sofort zu klicken, bei gedrückt gehaltener linker Maustaste
einen Rahmen in der für die Komponente gewünschten Größe aufspannen, können wir hier-

180
eclipse_v01.book Seite 181 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

bei auch gleich die gewünschte Größe der Komponente einstellen. Weiterhin kann die Grö-
ße und Position einer bereits eingefügten Komponente, wie bereits in Abschnitt 6.2.4 be-
schrieben, mithilfe der Maus direkt in der Design View geändert werden. Wurden mehrere
Komponenten markiert, können diese mithilfe der Symbole im Register COMPONENT des
Customize Layout-Fensters zueinander ausgerichtet werden.

Abb. 6.3: Ausrichtungsmöglichkeiten im Customize Layout-Fenster

Haben wir zunächst einen Layoutmanager benutzt und entscheiden uns anschließend, den
Layoutmanager auf null zu setzen, so werden für die bisher eingefügten Komponenten als
Einstellungen für die Größe und Position die bisher durch den Layoutmanager definierten
Werte realisiert.
Hinsichtlich des Hinzufügens von Komponenten bei Wahl eines Layoutmanagers soll noch
darauf hingewiesen werden, dass in diesem Fall eine Komponente nicht immer an jeder be-
liebigen Stelle platziert werden kann. Der Visual Editor unterstützt den Benutzer insofern,
als dass am Mauscursor ein +-Zeichen erscheint, wenn eine Komponente an der aktuellen
Mausposition eingefügt werden kann.

6.2.6 Ereignisverarbeitung
Um für eine Komponente eine Ereignisverarbeitung zu implementieren, müssen wir die
Komponente zunächst in der Java Beans View oder der Design View anklicken. Anschlie-
ßend wählen wir im Kontextmenü den Befehl EVENTS. Es öffnet sich ein Menü, das Ereig-
nisse anzeigt, die besonders häufig für die gewählte Komponente vorkommen. Aufgeführt
wird hierbei der Name der für das Ereignis zuständigen Methode im entsprechenden Inter-
face bzw. der Adapterklasse. Über den Befehl ADD EVENTS, der sich ebenfalls im Menü be-
findet, kann aber auch ein Fenster geöffnet werden, in dem alle Ereignistypen mit ihren
möglichen Ereignissen aufgeführt sind. Wird ein Ereignis angewählt, wird automatisch
rechts im Fenster CREATE NEW LISTENER aktiviert, sofern nicht bereits ein Listener imple-
mentiert worden ist. Existiert für den Listener eine Adapterklasse, können wir außerdem
wählen, ob die Adapterklasse oder das Interface implementiert werden soll. Für den Fall,

181
eclipse_v01.book Seite 182 Montag, 30. Januar 2006 12:02 12

Besonderheiten bei der Erstellung eines GUI mit Swing-Komponenten

dass bereits ein Listener vorhanden ist, ist zu beachten, dass dieser vom Visual Editor nur
erkannt wird, wenn er als anonyme Klasse implementiert worden ist.
Nach dem Hinzufügen des Ereignisses wird dieses in der Java Beans View angezeigt, vor-
ausgesetzt, die Anzeige wurde aktiviert. Hierzu befindet sich in der Titelleiste der Java
Beans View das Symbol . Wird es angeklickt, erscheinen drei mögliche Optionen: NO
EVENTS, SHOW EVENTS, EXPERT EVENTS. Während bei Wahl von SHOW EVENTS nur die zu ei-
nem Ereignis zugehörige Methode angezeigt wird, erscheint bei Wahl von EXPERT EVENTS
auch der implementierte Listener. Wurde der Listener als anonyme Klasse implementiert,
verdeutlicht die Farbe des Symbols vor dem Listener die Art der Implementation: lila
– Interface, grün – Adapterklasse. Bei einer Implementation als benannte innere Klasse da-
gegen erscheint in beiden Fällen vor dem Listener das Symbol .

Abb. 6.4: Ereignisse in der Java Beans View bei Wahl der Option Expert Events

Das Entfernen eines implementierten Ereignisses ist sehr einfach dadurch möglich, dass
wir den entsprechenden Eintrag in der Java Beans View anklicken und anschließend das Er-
eignis mit der ¢-Taste löschen.

6.3 Besonderheiten bei der Erstellung eines GUI mit Swing-


Komponenten
Nachdem wir in den Abschnitten 6.2.1 bis 6.2.6 die allgemeine Funktionsweise des Visual
Editors erklärt haben, wollen wir im Folgenden diejenigen Punkte aufzeigen, die unter-
schiedlich sind, je nachdem ob ein GUI mit AWT- bzw. Swing-Komponenten oder ein GUI
mit SWT-Komponenten erstellt wird. Wir beginnen unsere Betrachtung mit den Besonder-
heiten für AWT/Swing.

182
eclipse_v01.book Seite 183 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

6.3.1 Unterstützte Komponenten


Die einem AWT- oder Swing-Container hinzufügbaren Komponenten sind in der Palette in
vier Kategorien zusammengefasst. Im Einzelnen beinhalten die vier Kategorien die folgen-
den Komponenten:
쐌 Swing Components
JButton, JCheckBox, JTextArea, JList, JTable etc.
쐌 Swing Containers
JFrame, JPanel, JScrollPane, JWindow, JApplet etc.
쐌 Swing Menus
JMenuBar, JToolBar, JMenu, JPopupMenu, JMenuItem etc.
쐌 AWT Controls
Frame, Panel, Button, Label, Checkbox, List, TextField etc.

6.3.2 Layoutmanager
Der Visual Editor stellt für AWT- und Swing-Container die sechs Layoutmanager Border-
Layout, BoxLayout, CardLayout, FlowLayout, GridBagLayout und GridLayout zur Verfü-
gung.
Neben der Möglichkeit, alle allgemeinen und individuellen Einstellungen für ein Layout in
der Properties View vornehmen zu können (allgemeine Einstellungen: Eigenschaft LAYOUT,
individuelle Einstellungen: Eigenschaft CONSTRAINT), können die allgemeinen Einstellun-
gen für das GridLayout auch über das Register LAYOUT im Customize Layout-Fenster (an-
wählbar über das Kontextmenü oder den Button in der Werkzeugleiste) realisiert werden.
Die bei Wahl des GridBagLayouts möglichen individuellen Einstellungen für jede dem
Container hinzugefügte Komponente können ebenfalls alternativ über die Properties View
oder das Customize Layout-Fenster vorgenommen werden. In diesem Fall ist das Register
COMPONENT zu wählen. Es sei noch einmal darauf hingewiesen, dass die Komponente zuvor
in der Design View oder der Java Beans View angewählt worden sein muss.

Abb. 6.5: Customize Layout-Fenster für GridLayout und GridBagLayout

183
eclipse_v01.book Seite 184 Montag, 30. Januar 2006 12:02 12

Besonderheiten bei der Erstellung eines GUI mit Swing-Komponenten

Hinsichtlich des Hinzufügens von Komponenten wird der Benutzer beim BoxLayout,
FlowLayout und GridLayout insofern zusätzlich unterstützt, als dass in der Design View an
der dem Mauscursor nächst gelegenen möglichen Einfügeposition eine dicke Linie er-
scheint. Beim BorderLayout erscheint nicht nur das +-Zeichen am Mauscursor, wenn eine
Komponente an einer gewählten Position eingefügt werden kann, sondern es wird zusätz-
lich der aktuelle Bereich markiert und benannt (North, West, Center, East, South). Beim
GridBagLayout werden noch die Zeile und die Spalte des Gitternetzes am Mauscursor ein-
geblendet, wobei die Zählung bei 0 beginnt. Des Weiteren kann eine Komponente beim
GridBagLayout sehr leicht mithilfe der Maus in einer neuen Zeile oder Spalte platziert wer-
den. Wir müssen hierzu nur mit der Maus in der Zeile auf die vertikale Gitternetzlinie ge-
hen, wo die neue Komponente links in einer neuen Spalte eingefügt werden soll, bzw. in der
Spalte auf die horizontale Gitternetzlinie, wo die neue Komponente oberhalb in einer neuen
Zeile eingefügt werden soll.

Abb. 6.6: Hinzufügen einer Komponente beim GridBagLayout

6.3.3 Codegenerierung
Erstellt der Benutzer eine neue Klasse mit dem VE New Class Wizard, so wird automatisch
ein Quelltext generiert. Dieser enthält die Deklaration einer neuen Klasse, die die im VE
New Class Wizard gewählte Superklasse erweitert. Des Weiteren wird die Superklasse
automatisch in der neuen Klasse importiert.

import javax.swing.JFrame;
public class Fenster extends JFrame {

Weiterhin enthält der generierte Quelltext den Standardkonstruktor, der den Konstruktor
der Superklasse und eine Methode initialize aufruft.

public Fenster {
super();
initialize();
}

184
eclipse_v01.book Seite 185 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

Erweitert die neue Klasse beispielsweise die Klasse JFrame oder JInternalFrame, wird
automatisch eine ContentPane dem Fenster hinzugefügt. Diese wird als Attribut der Klasse
deklariert und mit null initialisiert.

private JPanel jContentPane = null;

Des Weiteren wird eine Getter-Methode implementiert, die die ContentPane als Objekt der
Klasse JPanel erzeugt und mit dem BorderLayout versieht. Die Klasse JPanel und die
Klasse BorderLayout werden ebenfalls automatisch importiert.

private JPanel getJContentPane() {


if (jContentPane == null) {
jContentPane = new JPanel();
jContentPane.setLayout(new BorderLayout());
}
return jContentPane;
}

In der Methode initialize werden die ContentPane und die Eigenschaften der Klasse
gesetzt, wie z.B. die Größe und der Titel des Fensters bei einer Klasse, die die Klasse
JFrame erweitert. Anfänglich umfasst die Methode initialize nur den folgenden Quell-
text:

private void initialize() {


this.setSize(300, 200);
this.setContentPane(getJContentPane());
this.setTitle("JFrame");
}

Fügt der Benutzer dem GUI Komponenten hinzu, werden diese als Attribute der Klasse mit
dem Zugriffsrecht private deklariert und mit null initialisiert. Hierbei werden die Klas-
sen der GUI-Komponenten automatisch importiert, so dass sie nicht mit ihrem vollständi-
gen Namen angesprochen werden müssen.

private JButton jButton = null;

Mit Ausnahme der Komponenten Label und JLabel wird für jede Komponente, die einer
anderen Komponente hinzugefügt wird, automatisch eine Getter-Methode implementiert,
die nicht nur einen Wert zurückliefert, sondern, falls für eine Komponente noch kein Objekt
existiert, dieses erzeugt. Die Getter-Methoden besitzen das Zugriffsrecht private. Des
Weiteren finden sich Änderungen, die in der Properties View an Eigenschaften einer Kom-
ponente vorgenommen werden und dazu führen, dass Eigenschaften von der Voreinstellung
abweichende Werte erhalten, als Anweisungen in den Getter-Methoden wieder.

185
eclipse_v01.book Seite 186 Montag, 30. Januar 2006 12:02 12

Besonderheiten bei der Erstellung eines GUI mit Swing-Komponenten

private JButton getJButton() {


if (jButton == null) {
jButton = new JButton();
jButton.setText("OK");
}
return jButton;
}

Die Komponenten Label und JLabel dagegen werden direkt in derjenigen Komponente
erzeugt, der sie hinzugefügt werden. Weiterhin wird automatisch eine Beschriftung des
Labels implementiert.
Komponenten, die direkt auf der ContentPane platziert werden, werden in der Methode
getJContentPane dem GUI hinzugefügt.

private JPanel getJContentPane() {


if (jContentPane == null) {
jLabel = new JLabel();
jLabel.setText("JLabel");
jContentPane = new JPanel();
jContentPane.setLayout(new BorderLayout());
jContentPane.add(jLabel, java.awt.BorderLayout.NORTH);
jContentPane.add(getJButton(), java.awt.BorderLayout.CENTER);
}
return jContentPane;
}

Das Hinzufügen einer Komponente zu einer anderen Komponente erfolgt in der Getter-Me-
thode der anderen Komponente.

private JPanel getJPanel() {


if (jPanel == null) {
jLabel1 = new JLabel();
jLabel1.setText("JLabel");
jPanel = JPanel();
jPanel.add(jLabel1, null);
jPanel.add(getJSlider(), null);
}
return jPanel;
}

Wird im Visual Editor einer Komponente ein Ereignis hinzugefügt, wird die für das Ereig-
nis zuständige Methode sowie, falls der Listener für das Ereignis noch nicht vorhanden ist,
dieser ebenfalls im Quelltext implementiert und der Komponente hinzugefügt. Die Reali-
sierung des Listener erfolgt als anonyme Klasse. Die für das Ereignis relevante Methode
enthält eine println-Anweisung sowie einen TODO-Kommentar. In der Source View
wird automatisch der Ausschnitt mit der Listener-Implementation angezeigt und die Zeile,

186
eclipse_v01.book Seite 187 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

die die Methodendeklaration beinhaltet, wird farblich hervorgehoben. Wie bereits in Ab-
schnitt 6.2.6 erwähnt, können wir für die Listener-Implementation zwischen Interface und
Adapterklasse wählen. Wenn wir uns für das Interface entscheiden, werden neben der
Methode für das ausgewählte Ereignis noch die Methoden für die weiteren Ereignisse des
Ereignistyps implementiert, und zwar in diesem Fall mit leerem Methodenrumpf.

Abb. 6.7: Ereignisverarbeitung im Visual Editor

6.3.4 Beispiel
Im Folgenden soll zunächst anhand eines Swing-Beispiels der Einsatz des Visual Editors
demonstriert werden. In Abschnitt 6.5.4 findet sich darüber hinaus ein kleineres SWT-Bei-
spiel.

Beschreibung des Beispiels


Bei dem Beispiel handelt es sich um ein Swing-Fenster mit Menüleiste und mehreren In-
ternal Frames. Die Menüleiste besitzt drei Menüs – DATEI, ANSICHT, EINSTELLUNGEN. Im
Menü DATEI befindet sich der Befehl BEENDEN zum Beenden der Anwendung. Das Menü
ANSICHT ermöglicht es zum einen, ein Internal Frame zur Dateneingabe zu erzeugen, und
zum anderen ein Internal Frame, welches die bisher eingegebenen Daten in einer tabellari-
schen Übersicht ausgibt.

Abb. 6.8: Internal Frames zur Dateneingabe und für die tabellarische Übersicht

Im Menü EINSTELLUNGEN kann ein Internal Frame zum Verändern der Fenstergröße und der
Hintergrundfarbe des Hauptfensters geöffnet werden. Ein weiteres zu öffnendes Internal
Frame weist zwei Register auf: ein Register, um über einen Schieberegler die Schriftgröße
der Beschriftungen im Eingabefenster zu verändern, und ein zweites Register, um die Ta-
belle mit oder ohne Gitternetzlinien darzustellen.

187
eclipse_v01.book Seite 188 Montag, 30. Januar 2006 12:02 12

Besonderheiten bei der Erstellung eines GUI mit Swing-Komponenten

Abb. 6.9: Internal Frames für Einstellungen des Hauptfensters und der Internal Frames

Der Einfachheit halber wird bei der Implementation nicht berücksichtigt, dass das Internal
Frame für die Einstellungen zur Ansicht erst aufgerufen werden darf, wenn bereits die ent-
sprechenden Internal Frames aus dem Menü ANSICHT aufgerufen worden sind. Des Weite-
ren sind die Änderungen nur bei einem geöffneten Internal Frame bis zum nächsten Schlie-
ßen wirksam.

Einrichten des Projekts


Wir erstellen ein Projekt mit dem Namen com.entwickler.eclipsebuch.VEBeispiel. Da die
Quelltext- und die class-Dateien jeweils in einem eigenen Verzeichnis gespeichert werden
sollen, aktivieren wir unter PROJECT LAYOUT den Eintrag CREATE SEPARATE SOURCE AND OUT-
PUT FOLDERS. Nach dem Beenden des Wizards erstellen wir die beiden Packages gui und
daten.

Die Klasse Daten


Bevor wir mit der Implementation der Klassen für das GUI beginnen, erstellen wir im
Package daten eine Klasse Daten, die einen Datensatz repräsentiert. Im Java Editor legen
wir anschließend die Attribute der Klasse Daten fest, implementieren zwei Konstruktoren
und erzeugen über SOURCE | GENERATE GETTERS AND SETTERS die Zugriffsmethoden.

package daten;

public class Daten {


private String kundenNr, kundenName;
public Daten() {}
public Daten(String kundenNr, String kundenName) {
this.kundenNr = kundenNr;
this.kundenName = kundenName;
}
public String getKundenName() { return kundenName; }
public void setKundenName(String kundenName) {
this.kundenName = kundenName;
}

188
eclipse_v01.book Seite 189 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

public String getKundenNr() { return kundenNr; }


public void setKundenNr(String kundenNr) {
this.kundenNr = kundenNr;
}
}

Die Klasse HauptFenster


Die Implementation des GUI beginnen wir nun mit der Implementation der Klasse für das
Hauptfenster. Hierzu wählen wir im Package Explorer das Package gui an und starten über
FILE | NEW | OTHER | JAVA | VISUAL CLASS den New Visual Class Wizard. Als Klassennamen
geben wir im Wizard HauptFenster ein. Des Weiteren wählen wir unter STYLE | SWING
den Eintrag FRAME, so dass unsere Klasse aus der Klasse javax.swing.JFrame abgelei-
tet wird. Außerdem lassen wir uns eine main-Methode generieren.
Nach dem Beenden des Wizards wird automatisch der Visual Editor gestartet. In der Java
Beans View erscheint der Eintrag this für das aktuelle Fenster, in der Design View ein
Fenster mit Titelleiste und in der Properties View werden, wenn der Eintrag this in der
Java Beans View oder das Fenster in der Design View angeklickt worden ist, die Eigen-
schaften des Fensters angezeigt.
Da standardmäßig die Eigenschaft visible für das Fenster auf false gesetzt ist, müssen
wir sie auf true setzen, damit das Fenster auch angezeigt wird. Des Weiteren ändern wir
unter der Eigenschaft bounds die beiden letzten Einträge auf 600 und 400, um ein Fenster
der Größe 600x400 zu erhalten, und geben unter title Visual Editor Swing-Beispiel ein.
Anschließend fügen wir die Komponente JMenuBar aus der Kategorie SWING MENUS dem
Fenster hinzu, um unser Fenster mit einer Menüleiste zu versehen. Da wir die vom Visual
Editor automisch vergebenen Namen übernehmen wollen und bisher unter WINDOW | PRE-
FERENCES | JAVA | VISUAL EDITOR die Anzeige des Fensters zur Namensgebung nicht deakti-
viert haben, holen wir dies jetzt nach.
Die drei Menüs erhalten wir, indem wir jeweils aus der Kategorie SWING MENUS die Kom-
ponente JMenu wählen und dann in der Java Beans View die Komponente JMenuBar an-
klicken, um das Menü der Menüleiste hinzuzufügen. Anschließend können wir in der
Properties View über die Eigenschaft text oder direkt in der Design View über das Text-
eingabefeld den drei Menüs ihre Beschriftung – Datei, Ansicht, Einstellungen – geben.
Als Nächstes werden den Menüs die Befehle hinzugefügt und zwar dem Menü DATEI der
Befehl BEENDEN, dem Menü ANSICHT die Befehle EINGABE und TABELLE und dem Menü EIN-
STELLUNGEN die Befehle ALLGEMEIN und ANSICHT. Hierzu wählen wir in der Kategorie SWING
MENUS die Komponente JMenuItem und fügen sie jeweils in der Java Beans View durch
Klicken auf das gewünschte Menü diesem hinzu. Da die Menübefehle nur in der Java Beans
View angezeigt werden, klicken wir sie anschließend dort an, um in der Properties View bei
der Eigenschaft text die Beschriftung einzugeben.

189
eclipse_v01.book Seite 190 Montag, 30. Januar 2006 12:02 12

Besonderheiten bei der Erstellung eines GUI mit Swing-Komponenten

Abb. 6.10: Die Java Beans View für die Klasse HauptFenster

Da ausgehend vom Hauptfenster mehrere Internal Frames erzeugt werden sollen, müssen
wir die ContentPane in der Java Beans View löschen und stattdessen aus der Kategorie
SWING CONTAINERS die Komponente JDesktopPane dem Fenster hinzufügen. Um das Haupt-
fenster auch angezeigt zu bekommen, gehen wir weiterhin in die Source View und erzeugen
dort in der main-Methode ein Objekt der Klasse HauptFenster.

package gui;

import java.awt.BorderLayout;
import javax.swing.*;

public class HauptFenster extends JFrame {


private JMenuBar jJMenuBar = null;
private JMenu jMenu = null;
private JMenu jMenu1 = null;
private JMenu jMenu2 = null;
private JMenuItem jMenuItem = null;
private JMenuItem jMenuItem1 = null;
private JMenuItem jMenuItem2 = null;
private JMenuItem jMenuItem3 = null;
private JMenuItem jMenuItem4 = null;
private JDesktopPane jDesktopPane = null;

private JMenuBar getJJMenuBar() {


if (jJMenuBar == null) {
jJMenuBar = new JMenuBar();
jJMenuBar.add(getJMenu()); // Datei
jJMenuBar.add(getJMenu1()); // Ansicht
jJMenuBar.add(getJMenu2()); // Einstellungen
}
return jJMenuBar;
}

190
eclipse_v01.book Seite 191 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

private JMenu getJMenu() {


if (jMenu == null) {
jMenu = new JMenu();
jMenu.setText("Datei");
jMenu.add(getJMenuItem()); // Beenden
}
return jMenu;
}
private JMenu getJMenu1() {
if (jMenu1 == null) {
jMenu1 = new JMenu();
jMenu1.setText("Ansicht");
jMenu1.add(getJMenuItem1()); // Eingabe
jMenu1.add(getJMenuItem2()); // Tabelle
}
return jMenu1;
}
private JMenu getJMenu2() {
if (jMenu2 == null) {
jMenu2 = new JMenu();
jMenu2.setText("Einstellungen");
jMenu2.add(getJMenuItem3()); // Allgemein
jMenu2.add(getJMenuItem4()); // Ansicht
}
return jMenu2;
}
private JMenuItem getJMenuItem() {
if (jMenuItem == null) {
jMenuItem = new JMenuItem();
jMenuItem.setText("Beenden");
}
return jMenuItem;
}
private JMenuItem getJMenuItem1() {
if (jMenuItem1 == null) {
jMenuItem1 = new JMenuItem();
jMenuItem1.setText("Eingabe");
}
return jMenuItem1;
}
private JMenuItem getJMenuItem2() {
if (jMenuItem2 == null) {
jMenuItem2 = new JMenuItem();
jMenuItem2.setText("Tabelle");
}
return jMenuItem2;
}

191
eclipse_v01.book Seite 192 Montag, 30. Januar 2006 12:02 12

Besonderheiten bei der Erstellung eines GUI mit Swing-Komponenten

private JMenuItem getJMenuItem3() {


if (jMenuItem3 == null) {
jMenuItem3 = new JMenuItem();
jMenuItem3.setText("Allgemein");
}
return jMenuItem3;
}
private JMenuItem getJMenuItem4() {
if (jMenuItem4 == null) {
jMenuItem4 = new JMenuItem();
jMenuItem4.setText("Ansicht");
}
return jMenuItem4;
}
private JDesktopPane getJDesktopPane() {
if (jDesktopPane == null) {
jDesktopPane = new JDesktopPane();
}
return jDesktopPane;
}
public static void main(String[] args) {
new HauptFenster();
}
public HauptFenster() {
super();
initialize();
}
private void initialize() {
this.setJMenuBar(getJJMenuBar());
this.setTitle("Visual Editor Swing-Beispiel");
this.setContentPane(getJDesktopPane());
this.setBounds(new java.awt.Rectangle(0,0,600,400));
this.setVisible(true);
}
}

Bevor wir jetzt die Klassen für die Internal Frames implementieren, sorgen wir noch dafür,
dass die Anwendung beendet werden kann, und zwar zum einen über das Systemmenü und
zum anderen über den Menübefehl DATEI | BEENDEN.
Für das Beenden über das Systemmenü müssen wir das Fenster in der Java Beans View oder
Design View anklicken, den Befehl EVENTS aus dem Kontextmenü wählen und anschlie-
ßend den Eintrag WINDOWCLOSING. Im Quelltext wird daraufhin die Adapterklasse Window-
Adapter als anonyme Klasse implementiert und dem Fenster hinzugefügt. Wir müssen
jetzt lediglich in der Source View die automatisch generierte println-Anweisung löschen
und stattdessen die Anweisung System.exit(0) eingeben.

192
eclipse_v01.book Seite 193 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

this.addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent e) {
System.exit(0);
}
});

Für das Beenden über DATEI | BEENDEN muss für den Menübefehl im Kontextmenü unter
EVENTS der Eintrag ACTIONPERFORMED gewählt werden. Hierdurch wird ein ActionListener
als anonyme Klasse implementiert und dem Menübefehl hinzugefügt.

jMenuItem.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent e) {
System.exit(0);
}
});

Die Klasse IFEingabe


Um eine erste Klasse für ein Internal Frame zu erstellen, wählen wir im Package Explorer
wiederum das Package gui an und rufen über FILE | NEW | OTHER | JAVA | VISUAL CLASS den
New Visual Class Wizard auf. Nachdem wir unter NAME für die neue Klasse IFEingabe
eingegeben haben, müssen wir, um unsere Klasse von der Klasse javax.swing.JInter-
nalFrame abzuleiten, unter STYLE | SWING den Eintrag INTERNAL FRAME wählen.
Wir erhalten damit ein Internal Frame mit einer ContentPane. Der automatisch erzeugte
Quelltext lautet:

package gui;

import java.awt.BorderLayout;
import javax.swing.JPanel;
import javax.swing.JInternalFrame;

public class IFEingabe extends JInternalFrame {


private JPanel jContentPane = null;
public IFEingabe() {
super();
initialize();
}
private void initialize() {
this.setSize(300, 200);
this.setContentPane(getJContentPane());
}
private JPanel getJContentPane() {
if (jContentPane == null) {
jContentPane = new JPanel();

193
eclipse_v01.book Seite 194 Montag, 30. Januar 2006 12:02 12

Besonderheiten bei der Erstellung eines GUI mit Swing-Komponenten

jContentPane.setLayout(new BorderLayout());
}
return jContentPane;
}
}

In der Properties View nehmen wir anschließend für das Internal Frame eine Reihe von Ver-
änderungen vor. Das >-Zeichen vor einer Eigenschaft zeigt, dass diese Eigenschaft geän-
dert worden ist.

Abb. 6.11: Die Properties View für die Klasse IFEingabe

Im Quelltext werden daraufhin in der Methode initialize die entsprechenden Setter-


Methoden aufgerufen.

private void initialize() {


this.setBounds(new java.awt.Rectangle(0,0,290,350));
this.setTitle("Dateneingabe");
this.setClosable(true);
this.setVisible(true);
this.setContentPane(getJContentPane());
}

194
eclipse_v01.book Seite 195 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

Anschließend wählen wir die ContentPane an und wählen in der Properties View unter der
Eigenschaft layout das GridLayout aus. Nachdem wir dann auf das +-Zeichen vor lay-
out geklickt und damit die Eigenschaft layout erweitert haben, geben wir unter columns
2 und unter rows 3 an. Als horizontalen und vertikalen Abstand wählen wir jeweils 50.
Alternativ hätten wir auch über den Befehl CUSTOMIZE LAYOUT im Kontextmenü oder den
Button in der Werkzeugleiste das Customize Layout-Fenster öffnen und dort im Register
LAYOUT die Einstellungen vornehmen können.
Jetzt können der ContentPane die einzelnen Dialogelemente hinzugefügt werden. Wir be-
ginnen mit einem ersten Label. Hierzu wählen wir in der Palette in der Kategorie Swing
Components JLabel an und klicken entweder in der Java Beans View oder in der Design
View auf die ContentPane. Als Nächstes folgt ein Textfeld. Um die richtige Position des
Textfelds rechts neben dem ersten Label zu erhalten, erfolgt das Hinzufügen hier am besten,
indem wir das gewünschte Dialogelement in der Palette anklicken und anschließend in der
Java Beans View die Maus kurz unterhalb des Labels platzieren. Es erscheint dort dann ein
waagerechter Strich. Ein Klicken fügt das Textfeld ein. In der Design View erscheint das
Textfeld zunächst unterhalb des Labels. Erst wenn mehr Dialogelemente eingefügt worden
sind, als Zeilen zur Verfügung stehen, wird zur gewünschten zweispaltigen Darstellung ge-
wechselt.
Analog fügen wir nacheinander noch ein weiteres Label, ein weiteres Textfeld und zwei
Buttons ein. Für die Label stellen wir in der Properties View unter text als Beschriftung
KUNDENNUMMER und KUNDENNAME ein, für die Buttons ÜBERNEHMEN und SCHLIESSEN. Alter-
nativ hätten wir, nachdem wir die Komponente in der Design View angeklickt haben, noch
ein weiteres Mal klicken und dann den Text in dem daraufhin erscheinenden Eingabefeld
eingeben können.
Für jedes Dialogelement wird beim Hinzufügen automatisch die entsprechende Klasse im-
portiert und das Dialogelement selbst als Attribut deklariert und mit null initialisiert.

private JLabel jLabel = null;


private JTextField jTextField = null;
private JLabel jLabel1 = null;
private JTextField jTextField1 = null;
private JButton jButton = null;
private JButton jButton1 = null;

Des Weiteren erfolgt in der Getter-Methode für die ContentPane das Hinzufügen der Dia-
logelemente und für die Label auch das Erzeugen.

195
eclipse_v01.book Seite 196 Montag, 30. Januar 2006 12:02 12

Besonderheiten bei der Erstellung eines GUI mit Swing-Komponenten

private JPanel getJContentPane() {


if (jContentPane == null) {
jLabel1 = new JLabel();
jLabel1.setText("Kundenname");
jLabel = new JLabel();
jLabel.setText("Kundennummer");
GridLayout gridLayout = new GridLayout();
gridLayout.setRows(3);
gridLayout.setHgap(50);
gridLayout.setVgap(50);
gridLayout.setColumns(2);
jContentPane = new JPanel();
jContentPane.setLayout(gridLayout);
jContentPane.add(jLabel, null);
jContentPane.add(getJTextField(), null);
jContentPane.add(jLabel1, null);
jContentPane.add(getJTextField1(), null);
jContentPane.add(getJButton(), null);
jContentPane.add(getJButton1(), null);
}
return jContentPane;
}

Für die Textfelder und Buttons werden die Dialogelement-Objekte in Getter-Methoden er-
zeugt, welche dem Quelltext ebenfalls automatisch hinzugefügt werden. Der folgende
Quelltext enthält die Implementation für den ersten Button. Für die weiteren Dialogelemen-
te wird ein analoger Quelltext generiert, wobei für die beiden Textfelder der Aufruf der Me-
thode setText nicht erforderlich ist.

private JButton getJButton() {


if (jButton == null) {
jButton = new JButton();
jButton.setText("Übernehmen");
}
return jButton;
}

Jetzt fehlt in der Klasse IFEingabe noch die Ereignisverarbeitung. Wird auf den ÜBERNEH-
MEN-Button geklickt, sollen die in den beiden Textfeldern eingegebenen Werte gespeichert
werden und die beiden Textfelder geleert werden. Wir wählen zunächst den Button an, dann
im Kontextmenü EVENTS und anschließend ACTIONPERFORMED. Im Quelltext erzeugen wir
ein neues Datensatzobjekt, welches wir mit den Werten aus den Textfeldern initialisieren.
Da sich die Klasse Daten in einem anderen Package befindet, müssen wir den vollständi-
gen Klassennamen angeben oder alternativ die Klasse importieren. Das Datensatzobjekt fü-
gen wir anschließend einer Liste daten hinzu. In der Liste daten werden alle eingegebe-
nen Datensätze gespeichert.

196
eclipse_v01.book Seite 197 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

Damit auch das Internal Frame, welches die Daten in einer tabellarischen Übersicht aus-
gibt, auf die Daten zugreifen kann, wird die Liste daten als Attribut des Hauptfensters de-
finiert. Hierzu wird in der Klasse HauptFenster das Attribut daten als vom Typ ja-
va.util.LinkedList deklariert und mit null initialisiert, in der Methode initialize
das Objekt erzeugt und die Zugriffsmethoden über SOURCE | GENERATE GETTERS AND SETTERS
generiert.

private java.util.LinkedList daten = null;

private void initialize() {


// ...
daten = new java.util.LinkedList();
}
public java.util.LinkedList getDaten() { return daten; }
public void setDaten(java.util.LinkedList daten) {
this.daten = daten;
}

Die Definition der Liste als Attribut der Klasse HauptFenster macht es erforderlich, dass
in der Klasse IFEingabe auf das Hauptfenster zugegriffen werden kann. Hierzu muss das
Hauptfenster als Attribut der Klasse IFEingabe definiert werden.

private HauptFenster parent = null;

Weiterhin kopieren wir im Quelltext den Standardkonstruktor und ändern die Kopie, indem
wir als Parameter das Hauptfenster übergeben.

public IFEingabe(HauptFenster fenster) {


super();
parent = fenster;
initialize();
}

Als Letztes dürfen wir nicht vergessen, in der Methode actionPerformed die Textfelder
zu leeren. Die Implementation des Listeners sieht dann wie folgt aus:

jButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent e) {
daten.Daten datSatz = new daten.Daten(jTextField.getText(),
jTextField1.getText());
parent.getDaten().add(datSatz); // Hinzufügen zur Liste
jTextField.setText(""); // Textfeld leeren
jTextField1.setText(""); // Textfeld leeren
}
});

197
eclipse_v01.book Seite 198 Montag, 30. Januar 2006 12:02 12

Besonderheiten bei der Erstellung eines GUI mit Swing-Komponenten

Die Implementation des SCHLIESSEN-Buttons ist weniger aufwändig. Das Internal Frame soll
geschlossen werden, wenn auf den SCHLIESSEN-Button geklickt wird. Hierzu wird dem
SCHLIESSEN-Button das Ereignis hinzugefügt und innerhalb der Methode actionPerformed
die Methode dispose für das Internal Frame aufgerufen. Da innerhalb der anonymen Klasse
nicht direkt auf die Referenz des Internal Frame zugegriffen werden kann, wird vor der Imple-
mentation der anonymen Klasse eine Konstante definiert, die eine Referenz auf das Internal
Frame enthält.

final IFEingabe jIF = this;


jButton1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent e) {
jIF.dispose();
}
});

Nachdem die Klasse IFEingabe implementiert worden ist, kann für den Menübefehl AN-
SICHT | EINGABE im Hauptfenster die Ereignisverarbeitung realisiert werden. Da wir später
noch von einer anderen Klasse aus auf das Internal Frame zugreifen wollen, wird es als
Attribut des Hauptfensters deklariert.

private IFEingabe iFEingabe = null;

In der Methode actionPerformed wird dann das Objekt iFEingabe erzeugt und mithil-
fe der Methode add der DesktopPane des Hauptfensters hinzugefügt. Da beim Aufruf des
Konstruktors in der anonymen Klasse das Hauptfenster als Parameter übergeben wird, müs-
sen wir noch vor der Implementation des Listener eine Konstante definieren, die eine Re-
ferenz auf das Hauptfenster enthält.

final HauptFenster fenster = this;


jMenuItem1.addActionListener(new java.awt.event.ActionListener()
{
public void actionPerformed(java.awt.event.ActionEvent e) {
iFEingabe = new IFEingabe(fenster);
jDesktopPane.add(iFEingabe);
}
});

Die Klasse IFTabelle


Als Nächstes implementieren wir im Package gui die Klasse IFTabelle zur Darstellung
der eingegebenen Daten in einer Tabelle. Für das Internal Frame stellen wir die in der Abb.
6.12 dargestellten Eigenschaften ein.

198
eclipse_v01.book Seite 199 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

Abb. 6.12: Die Properties View für die Klasse IFTabelle

Anschließend wählen wir auch hier in der Properties View für die ContentPane das Grid-
Layout. Als Spaltenanzahl geben wir 1 und als Zeilenanzahl 2 ein. Der horizontale und ver-
tikale Abstand soll jeweils 50 betragen. Anschließend wird eine scrollbare Tabelle der Con-
tentPane hinzugefügt. Hierzu wird in der Palette in der Kategorie Swing Components die
Komponente JTable on JScrollPane gewählt.

private JPanel getJContentPane() {


if (jContentPane == null) {
GridLayout gridLayout = new GridLayout();
gridLayout.setRows(2);
gridLayout.setHgap(50);
gridLayout.setVgap(50);
gridLayout.setColumns(1);
jContentPane = new JPanel();
jContentPane.setLayout(gridLayout);
jContentPane.add(getJScrollPane(), null);
}
return jContentPane;
}
private JScrollPane getJScrollPane() {
if (jScrollPane == null) {
jScrollPane = new JScrollPane();
jScrollPane.setViewportView(getJTable());
}
return jScrollPane;
}

199
eclipse_v01.book Seite 200 Montag, 30. Januar 2006 12:02 12

Besonderheiten bei der Erstellung eines GUI mit Swing-Komponenten

private JTable getJTable() {


if (jTable == null) {
jTable = new JTable();
}
return jTable;
}

Um die bereits eingegebenen Daten in der Tabelle anzuzeigen, wird eine Methode
getTableModel implementiert, die ein Objekt der Klasse DefaultTableModel zurück-
liefert. Das Objekt wird in der Methode erzeugt und mit den Daten aus der Liste daten ini-
tialisiert. Da auch hierbei, genau wie bei der Klasse IFEingabe, auf das Hauptfenster zu-
gegriffen werden muss, müssen wir ebenfalls einen zweiten Konstruktor implementieren
und das Hauptfenster als Attribut der Klasse IFTabelle definieren.

private javax.swing.table.DefaultTableModel getTableModel() {


daten.Daten datSatz = new Daten();
String[] modUeberschrift = {"Kundennummer","Kundenname"};
String[][] modDaten = new String[parent.getDaten().size()][2];
for (int i=0; i<parent.getDaten().size(); i++) {
datSatz = (daten.Daten)parent.getDaten().get(i);
modDaten[i][0] = datSatz.getKundenNr();
modDaten[i][1] = datSatz.getKundenName();
}
return new
javax.swing.table.DefaultTableModel(modDaten,modUeberschrift);
}

In der Methode getJTable muss nun nur noch nach dem Erzeugen des Objekts jTable
das Modell gesetzt werden:

jTable.setModel(getTableModel());

Als zweites Dialogelement fügen wir der ContentPane jetzt noch einen Button hinzu. Der
Button soll unterhalb der Tabelle platziert werden und die Beschriftung AKTUALISIEREN auf-
weisen. Wird auf den Button geklickt, sollen alle bisher eingegebenen Daten angezeigt
werden. Hierzu wählen wir für den Button im Kontextmenü wiederum den Befehl EVENTS
und anschließend ACTIONPERFORMED. Im Quelltext muss dann in der actionPerformed-
Methode die Anweisung

jTable.setModel(getTableModel());

implementiert werden.
Die Ereignisverarbeitung für den Menübefehl ANSICHT | TABELLE kann jetzt analog zum
Menübefehl ANSICHT | EINGABE realisiert werden.

200
eclipse_v01.book Seite 201 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

Die Klasse IFAllgemein


Nachdem die beiden Internal Frames, die beim Aufruf der Befehle im Menü ANSICHT ange-
zeigt werden sollen, implementiert worden sind, erstellen wir nun die Klasse für das Inter-
nal Frame, welches die Wahl der Fenstergröße und der Hintergrundfarbe des Hauptfensters
ermöglicht. Die Klasse soll sich ebenfalls im Package gui befinden. Als Namen geben wir
im New Visual Class Wizard IFAllgemein ein. Darüber hinaus erstellen wir analog zu den
Klassen IFEingabe und IFTabelle einen zweiten Konstruktor und definieren das Haupt-
fenster als Attribut des Internal Frame. In der Properties View stellen wir für das Internal
Frame die in der Abb. 6.13 dargestellten Eigenschaften ein.

Abb. 6.13: Die Properties View für die Klasse IFAllgemein

Als Layout für die ContentPane wählen wir wiederum GridLayout. Die Anzahl der Zeilen
und Spalten ist jeweils 2, der horizontale und vertikale Abstand 50. Als Nächstes werden
die Komponenten der ContentPane hinzugefügt, und zwar zunächst ein Label, dann eine
Combobox – diese findet sich in der Palette in der Kategorie Swing Components –, danach
wieder ein Label und anschließend die Komponente JScrollPane aus der Kategorie Swing
Containers. In die ScrollPane wird als Letztes noch eine Liste eingefügt. Die richtige An-
ordnung lässt sich auch hier am einfachsten erreichen, indem die jeweilige Komponente in
der Palette angewählt wird und anschließend in der Java Beans View an die entsprechende
Stelle geklickt wird. Dem ersten Label geben wir entweder in der Properties View unter der
Eigenschaft text, oder indem wir in der Design View das Texteingabefeld aktivieren, die
Beschriftung FENSTERGRÖSSE, dem zweiten Label die Beschriftung HINTERGRUNDFARBE. Die
Einträge für die Combobox und die Liste müssen direkt im Quelltext implementiert wer-
den. Bei der Combobox werden die einzelnen Einträge mithilfe der Methode addItem dem

201
eclipse_v01.book Seite 202 Montag, 30. Januar 2006 12:02 12

Besonderheiten bei der Erstellung eines GUI mit Swing-Komponenten

JComboBox-Objekt hinzugefügt. Bei der Liste werden die einzelnen Einträge zunächst in
einem Feld gespeichert und dann wird das Feld mithilfe der Methode setListData dem
JList-Objekt bekannt gemacht.

// Combobox für Fenstergröße


private JComboBox getJComboBox() {
if (jComboBox == null) {
jComboBox = new JComboBox();
jComboBox.addItem("600x400");
jComboBox.addItem("750x500");
jComboBox.addItem("900x600");
}
return jComboBox;
}
// Liste für Hintergrundfarbe
private JList getJList() {
if (jList = null) {
jList = new JList();
Object[] items = {"grau","blau","weiß","rot","grün","gelb"};
jList.setListData(items);
}
return jList;
}

Um für die Combobox das Ereignis „Klick auf einen Eintrag“ zu implementieren, wählen
wir im Kontextmenü wiederum den Befehl EVENTS und anschließend ACTIONPERFORMED. Im
Quelltext müssen wir dann das Setzen der Fenstergröße wie folgt implementieren:

jComboBox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent e) {
String item = (String)jComboBox.getSelectedItem();
if (item.equals("600x400")) parent.setSize(600,400);
else if (item.equals("750x500")) parent.setSize(750,500);
else if (item.equals("900x600")) parent.setSize(900,600);
}
});

Während bei der Anwahl eines Ereignisses in der Combobox ein ActionEvent ausgelöst
wird, wird bei der Anwahl eines Ereignisses in der Liste ein ListSelectionEvent ausgelöst.
Auch hier können wir wieder das Ereignis über den Befehl EVENTS im Kontextmenü dem
Quelltext hinzufügen. Allerdings muss hier anschließend der Befehl VALUECHANGED ge-
wählt werden, um die Methode valueChanged zu erhalten, in der wir die folgenden An-
weisungen zur Änderung der Hintergrundfarbe implementieren.

202
eclipse_v01.book Seite 203 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

jList.addListSelectionListener(
new javax.swing.event.ListSelectionListener() {
public void valueChanged(
javax.swing.event.ListSelectionEvent e) {
int[] items = jList.getSelectedIndices();
switch (items[0]) {
case 0: parent.getJDesktopPane().
setBackground(java.awt.Color.gray);
break;
case 1: parent.getJDesktopPane().
setBackground(java.awt.Color.blue);
break;
case 2: parent.getJDesktopPane().
setBackground(java.awt.Color.white);
break;
case 3: parent.getJDesktopPane().
setBackground(java.awt.Color.red);
break;
case 4: parent.getJDesktopPane().
setBackground(java.awt.Color.green);
break;
case 5: parent.getJDesktopPane().
setBackground(java.awt.Color.yellow);
break;
}
}
});

Da wir hier auf die Methode getJDesktopPane zugreifen müssen, um die Hintergrund-
farbe für das Hauptfenster zu ändern, und diese bisher als private in der Klasse Haupt-
Fenster deklariert ist, müssen wir das Zugriffsrecht noch erweitern. Wir wählen hier
public, damit auch Klassen anderer Packages auf die DesktopPane zugreifen können.
Die Ereignisverarbeitung für den Menübefehl ANSICHT | ALLGEMEIN im Hauptfenster kann
jetzt wieder analog zu den bereits implementierten Befehlen realisiert werden.

Die Klasse IFAnsicht


Als letzte Klasse erstellen wir mithilfe des New Visual Class Wizard im Package gui die
Klasse IFAnsicht, welche wiederum die Klasse javax.swing.JInternalFrame er-
weitert. Auch hier definieren wir das Hauptfenster als Attribut des Internal Frame und er-
stellen einen zweiten Konstruktor.

203
eclipse_v01.book Seite 204 Montag, 30. Januar 2006 12:02 12

Besonderheiten bei der Erstellung eines GUI mit Swing-Komponenten

Abb. 6.14: Die Properties View für die Klasse IFAnsicht

Nachdem wir in der Properties View die in der Abb. 6.14 dargestellten Eigenschaften ein-
gestellt haben, fügen wir der ContentPane die Komponente JTabbedPane aus der Kategorie
Swing Containers hinzu. Die Register erhalten wir anschließend, indem wir der Tabbed-
Pane zwei Panels ebenfalls aus der Kategorie Swing Containers hinzufügen. Das erste
Panel erhält in der Properties View bei der Eigenschaft tab title als Beschriftung EIN-
GABE, das zweite Panel TABELLE. Das hierfür erforderliche Anwählen eines Panel erfolgt
hierbei in der Java Beans View. Jetzt wird für das erste Panel das GridLayout mit einer
Spalte und zwei Zeilen sowie einem horizontalen und vertikalen Abstand von 0 gewählt.
Danach werden dem Panel ein Label und die Komponente JSlider (Schieberegler) aus der
Kategorie Swing Components hinzugefügt. Für das Label geben wir als Beschriftung
SCHRIFTGRÖSSE ein. Für den Schieberegler wird unter den Eigenschaften majorTick-
Spacing und minorTickSpacing jeweils 2 eingestellt sowie unter der Eigenschaft
maximum 16 und minimum 8. Um eine Beschriftung zu erhalten, müssen wir des Weiteren
bei der Eigenschaft paintLabels true einstellen und für eine Unterteilung bei paint-
Ticks true. Unter value geben wir dann noch die aktuelle Schriftgröße für die Beschrif-
tungen in der Klasse IFEingabe an, nämlich 12.

private JSlider.getJSlider() {
if (jSlider == null) {
jSlider = new JSlider();
jSlider.setMajorTickSpacing(2);
jSlider.setMaximum(16);
jSlider.setMinimum(8);
jSlider.setPaintLabels(true);
jSlider.setPaintTicks(true);

204
eclipse_v01.book Seite 205 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

jSlider.setValue(12);
jSlider.setMinorTickSpacing(2);
}

Um bei einer Veränderung des Schiebereglers den neuen Wert zu erhalten, müssen wir ei-
nen ChangeListener mit dem Ereignis stateChanged über den Befehl EVENTS hinzufügen.
Mithilfe der folgenden Anweisungen, die wir anschließend im Quelltext implementieren,
können wir auf den Wert zugreifen und die Schriftgröße für die Label im Internal Frame zur
Dateneingabe ändern.

jSlider.addChangeListener(new javax.swing.event.ChangeListener()
{
public void stateChanged(javax.swing.event.ChangeEvent e) {
parent.getIFEingabe().getJLabel().setFont(
new java.awt.Font("Dialog",java.awt.Font.PLAIN,
jSlider.getValue()));
// analog für die restlichen Beschriftungen
}
});

Da die Methoden getJButton und getJButton1 vom Visual Editor in der Klasse
IFEingabe als private deklariert worden sind, müssen wir hier das Zugriffsrecht verän-
dern, z.B. auf public setzen, um der Klasse IFAnsicht den Zugriff zu gewährleisten.
Weiterhin ist über die Befehlsfolge SOURCE | GENERATE GETTERS AND SETTERS jeweils eine
Getter-Methode in der Klasse HauptFenster für das Attribut iFEingabe und in der
Klasse IFEingabe für die Attribute jLabel und jLabel1 zu erzeugen.
Für das zweite Panel wird zunächst in der Properties View unter layout GridLayout mit
einer Spalte, drei Zeilen und einem horizontalen und vertikalen Abstand von 0 eingetragen.
Dann werden dem Panel ein Label und zwei Radiobuttons aus der Kategorie Swing Com-
ponents hinzugefügt. Das Label erhält über die Eigenschaft text die Beschriftung TABELLE
und die Radiobuttons ebenfalls über die Eigenschaft text die Beschriftungen MIT GITTER-
NETZLINIEN und OHNE GITTERNETZLINIEN. Die Eigenschaft selected wird anschließend für
den ersten Radiobutton auf true gesetzt werden, damit dieser ausgewählt ist. Um bei einer
Änderung der Einstellung diese bei der Anzeige des Internal Frame auch zu verwirklichen,
wird beiden JRadioButton-Objekten jeweils ein ItemListener mit dem Ereignis item-
StateChanged hinzugefügt. Anschließend implementieren wir im Quelltext die erforder-
lichen Anweisungen. Zu beachten ist hier, dass für das Attribut iFTabelle der Klasse
HauptFenster noch die Getter-Methode implementiert werden muss sowie die Methode
getJTable z.B. das Zugriffsrecht public erhalten muss.

jRadioButton.addItemListener(new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent e) {
if (jRadioButton.isSelected()) {
jRadioButton1.setSelected(false);

205
eclipse_v01.book Seite 206 Montag, 30. Januar 2006 12:02 12

Das SWT

parent.getIFTabelle().getJTable().setShowGrid(true);
}
}
}

6.4 Das SWT


Das Standard Widget Toolkit (SWT) ist eine Grafikbibliothek, die mit dem Ziel entwickelt
worden ist, einerseits portable Anwendungen erstellen zu können, andererseits jedoch auch
eine große Betriebssystemnähe zu haben.
Die erste zur Verfügung stehende Grafikbibliothek, das Abstract Window Toolkit (AWT),
erlaubt zwar ebenfalls die Entwicklung portabler Anwendungen mit großer Betriebssys-
temnähe. Allerdings umfasst sie nur Klassen für diejenigen GUI-Elemente, die auf allen
Plattformen zur Verfügung stehen. Hierbei handelt es sich um einfache GUI-Elemente, wie
Buttons, Textfelder oder Listen. Komplexere GUI-Elemente wie Trees oder Tabellen feh-
len.
Das Swing Toolkit beinhaltet zwar Klassen für komplexere GUI-Elemente. Allerdings wer-
den die meisten GUI-Elemente emuliert, so dass beispielsweise ein Button in einer Swing-
Anwendung anders aussieht als unter dem jeweiligen Betriebssystem.
Im SWT sind nun die Vorteile beider Grafikbibliotheken vereint. Allerdings darf nicht ver-
schwiegen werden, dass auch einige Nachteile bestehen. So laufen SWT-Anwendungen nur
auf Plattformen, für die SWT zur Verfügung steht. Des Weiteren sind der Portabilität dort
Grenzen gesetzt, wo Dialogelemente auf verschiedenen Plattformen ein unterschiedliches
Verhalten zeigen.

6.4.1 Aufbau einer SWT-Applikation


Um eine Applikation mit SWT-Komponenten zu implementieren, müssen auf jeden Fall
Klassen aus den Packages org.eclipse.swt und org.eclipse.swt.widgets impor-
tiert werden. Der Einfachheit halber können wir alle Klassen dieser Packages importieren.

org.eclipse.swt.*;
org.eclipse.swt.widgets.*;

Für die Anzeige von SWT-Komponenten auf dem Bildschirm müssen ein GUI-Prozess, der
das Verbindungsglied zwischen der Anwendung und dem Betriebssystem bildet, und ein
Fenster erzeugt werden. Für den GUI-Prozess steht die Klasse Display zur Verfügung. Ein
Fenster wird repräsentiert durch die Klasse Shell. Dem Shell-Objekt wird hierbei beim
Erzeugen im Konstruktor das Display-Objekt übergeben. Dadurch wird die Shell als
Hauptfenster der Anwendung festgelegt.

Display display = Display.getDefault();


Shell shell = new Shell(display);

206
eclipse_v01.book Seite 207 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

Darüber hinaus kann optional ein Stilparameter angegeben werden, mit dem das Aussehen
und das Verhalten des Fensters gesteuert werden. Ein Stilparameter setzt sich aus einer oder
mehreren so genannten Stilkonstanten zusammen. Nähere Erläuterungen hierzu finden sich
im nächsten Abschnitt. Für eine Shell existieren u.a. die folgenden Stilkonstanten:

Stilkonstante Bedeutung
SWT.NONE Standardfenster
SWT.BORDER Fenster mit Rand
SWT.CLOSE Fenster mit Titelleiste und Schließen-Button
SWT.MIN Fenster mit Titelleiste und Minimalisieren-Button
SWT.MAX Fenster mit Titelleiste und Maximalisieren-Button
SWT.NO_TRIM Fenster ohne Titelleiste und Rand
SWT.RESIZE Fenstergröße kann durch Ziehen mit der Maus verändert werden
SWT.TITLE Fenster mit Titelleiste
SWT.SHELL_TRIM Kombination von Stilkonstanten für das Hauptfenster einer Applika-
tion (SWT.CLOSE, SWT.TITLE, SWT.MIN, SWT.MAX, SWT.RESIZE)
SWT.DIALOG_TRIM Kombination von Stilkonstanten für Dialogfenster (SWT.CLOSE,
SWT.TITLE, SWT.BORDER)

Wird kein Stilparameter angegeben, so wird eine Voreinstellung realisiert. Diese ist abhän-
gig vom Betriebssystem und von der Art der Shell (Hauptfenster oder Dialogfenster).
Innerhalb einer Anwendung lassen sich weitere Shells definieren. In diesem Fall ist beim
Konstruktoraufruf das übergeordnete Shell-Objekt anzugeben.
Nach dem Erzeugen der Shell können hierfür die Einstellungen vorgenommen werden, die
nicht über Stilkonstanten möglich sind, wie z.B. das Festlegen eines Titels für die Titelleiste
des Fensters. Des Weiteren können die SWT-Komponenten definiert und hierfür ebenfalls
die gewünschten Einstellungen erfolgen. Abschließend müssen wir die Shell mithilfe der
Methode open öffnen, eine Schleife für die Ereignisverarbeitung implementieren, die erst
beendet wird, wenn die Shell entfernt wird, und das Display-Objekt mit der Methode
dispose entfernen.

shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
display.dispose();

207
eclipse_v01.book Seite 208 Montag, 30. Januar 2006 12:02 12

Das SWT

6.4.2 SWT-Komponenten
Wird in einer Anwendung eine SWT-Komponente instanziiert, so erzeugt SWT die zuge-
hörige Komponente des Betriebssystems, sofern diese existiert. Da bei vielen Betriebssys-
temen beim Erzeugen eines GUI-Elements das übergeordnete GUI-Element angegeben
werden muss, ist dieses im SWT ebenfalls erforderlich. Das heißt, bei der Instanziierung ei-
ner SWT-Komponente muss im Konstruktor die übergeordnete Komponente angegeben
werden. Dadurch wird die Komponente der übergeordneten Komponente hinzugefügt. Eine
add-Methode wie im AWT oder in Swing muss deshalb nicht aufgerufen werden.

Button button = new Button(shell, SWT.PUSH);

SWT-Komponenten besitzen analog zu AWT- und Swing-Komponenten Eigenschaften. Ei-


nige dieser Eigenschaften können bzw. müssen beim Erzeugen der Komponente festgelegt
werden und können zum Teil nachträglich nicht mehr verändert werden. Für die verschie-
denen Ausprägungen der Eigenschaften existieren in diesem Fall so genannte Stilkonstan-
ten. Diese sind in der Klasse org.eclipse.swt.SWT definiert.
Bei der Festlegung mehrerer Eigenschaften für eine Komponente über Stilkonstanten wer-
den diese über ein bitweises Oder | zu einem Stilparameter zusammengefasst.

Shell shell = new Shell(SWT.CLOSE | SWT.RESIZE);

Soll für eine Eigenschaft die Voreinstellung realisiert werden, muss keine Stilkonstante an-
gegeben werden. Sollen für alle Stilkonstanten einer Komponente die Voreinstellungen gel-
ten, wird als Stilparameter lediglich die Stilkonstante SWT.NONE angegeben. Eine SWT-
Komponente verfügt über die in der zugehörigen Klasse definierten Stilkonstanten sowie
die aus Superklassen geerbten Stilkonstanten.

Die Klasse Widget


Die verschiedenen GUI-Elemente von SWT werden durch Klassen repräsentiert, die sich in
einer Vererbungshierarchie befinden. Die Superklasse aller Klassen ist hierbei die Klasse
Widget. Sie ist eine abstrakte Klasse, in der einige grundlegende Methoden implementiert
sind, wie z.B. die Methode dispose, die durch ein Objekt genutzte Ressourcen wieder
freigibt. Sie besitzt keine Stilkonstanten.

Die Klasse Control


Eine direkte Subklasse der Klasse Widget ist die ebenfalls abstrakte Klasse Control. Sie
repräsentiert GUI-Elemente, die beliebigen anderen GUI-Elementen hinzugefügt werden
können, und stellt eine Vielzahl von Methoden zur Verfügung, mit denen die verschiedenen
Eigenschaften der GUI-Elemente gesetzt und abgefragt werden, wie z.B. die Methoden
setVisible und setSize. Darüber hinaus verfügt sie über die Stilkonstante SWT.BOR-
DER, die ein GUI-Element mit einem Rahmen versieht.

208
eclipse_v01.book Seite 209 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

Widget
• Caret
• Control
• Button
• Label
• ProgressBar
• Sash
• Scale
• Scrollable
• Composite
• Canvas
• Decorations
• Shell
• Combo
• CoolBar
• Group
• Spinner
• TabFolder
• Table
• ToolBar
• Tree
• List
• Text
• Slider
• Item
• CoolItem
• MenuItem
• TabItem
• TableColumn
• TableItem
• ToolItem
• TreeItem
• Menu
• ScrollBar
• Tracker
Abb. 6.15: Die Klasse Widget und ausgewählte Subklassen

Die Klasse Composite


Widgets werden in der Regel nicht direkt auf einer Shell platziert, sondern auf einem so ge-
nannten Composite. Ein Composite ist ein Widget, das andere Widgets aufnehmen kann
und somit das Gruppieren von Widgets ermöglicht. Repräsentiert wird ein Composite
durch die Klasse Composite. Im Gegensatz zu Swing wird ein Widget einem Composite

209
eclipse_v01.book Seite 210 Montag, 30. Januar 2006 12:02 12

Das SWT

nicht mithilfe einer add-Methode hinzugefügt, sondern beim Erzeugen eines neuen Wid-
get-Objekts wird das Composite-Objekt als Parameter übergeben.

Composite composite = new Composite(shell, SWT.NONE);


Button button = new Button(composite, SWT.NONE);

Einem Composite können nicht nur GUI-Elemente, wie Buttons, Labels etc., sondern auch
ein oder mehrere weitere Composites hinzugefügt werden, so dass sich die Hierarchie der
Composites und der weiteren GUI-Elemente als Baum darstellen lässt mit der Shell als
Wurzel.
Für die Klasse Composite hält das SWT die Stilkonstante SWT.NO_RADIO_GROUP bereit,
die verhindert, dass bei mehreren auf einem Composite angeordneten Radiobuttons nur ein
Radiobutton ausgewählt werden kann, sowie die Stilkonstanten SWT.NO_BACKGROUND,
SWT.NO_FOCUS, SWT.NO_MERGE_PAINTS, SWT.NO_REDRAW_RESIZE. Letztere sind al-
lerdings mit Ausnahme der Klasse Canvas für die Subklassen der Klasse Composite un-
definiert.

Die Klasse Group


Die Klasse Composite besitzt als eine Subklasse die Klasse Group. Diese zeichnet um das
Composite zusätzlich einen Rahmen und kann optional einen Titel, der in den Rahmen
gesetzt wird, aufweisen. Das Aussehen der Linie kann über die Stilkonstanten SWT.
SHADOW_ETCHED_IN, SWT.SHADOW_ETCHED_OUT, SWT.SHADOW_IN, SWT.SHADOW_OUT
und SWT.SHADOW_NONE festgelegt werden. Diese werden jedoch nicht von allen Betriebs-
systemen unterstüzt. Für die Definition des Titels stellt die Klasse Group die Methode
setText zur Verfügung.

Die Klasse Canvas


Die Klasse Canvas dient als Zeichenfläche für Grafikoperationen und erlaubt es, eigene
GUI-Elemente zu implementieren. Über die aus der Klasse Composite geerbten Stilkon-
stanten lassen sich einige grundsätzliche Einstellungen vornehmen.

Die Klasse Label


Für Beschriftungen existiert die Klasse Label. Neben einer Beschriftung in Form von Text
ermöglicht es die Klasse Label auch, eine Grafik bzw. eine horizontale oder vertikale Linie
(Separator) abzubilden. Der Einsatzzweck und das Aussehen eines Labels werden über
Stilkonstanten gesteuert. In der folgenden Übersicht und auch den weiteren Übersichten
schließen sich die Stilkonstanten innerhalb einer Zelle gegenseitig aus, d.h., es kann nur je-
weils eine gewählt werden.

SWT.LEFT
SWT.RIGHT Ausrichtung für Text bzw. Grafik
SWT.CENTER

210
eclipse_v01.book Seite 211 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

SWT.WRAP Automatischer Umbruch bei Textlabels


SWT.SEPARATOR Label in Form einer horizontalen oder vertikalen Linie
(Separator)
SWT.HORIZONTAL
Orientierung der Linie im Fall eines Separators
SWT.VERTICAL

SWT.SHADOW_IN
SWT.SHADOW_OUT Schattendarstellung der Linie im Fall eines Separators
SWT.SHADOW_NONE

Das Festlegen des Texts bei einem Textlabel erfolgt mithilfe der Methode setText, das
Festlegen des Bilds bei einem Bildlabel mithilfe der Methode setImage.

Label textLabel = new Label(composite, SWT.LEFT);


textLabel.setText("Textlabel");
Label bildLabel = new Label(composite, SWT.CENTER);
bildLabel.setImage("bild.gif");
Label separator = new Label(composite, SWT.SEPARATOR |
SWT.VERTICAL);

Die Klasse Button


Während im AWT und in Swing für die unterschiedlichen Arten von Buttons auch ver-
schiedene Klassen existieren, besitzt das SWT nur eine Klasse Button, in der die verschie-
denen Arten zusammengefasst sind. Welche Art jeweils realisiert werden soll, wird beim
Erzeugen des Button-Objekts über eine Stilkonstante im Konstruktor gesteuert.

SWT.ARROW Button mit einem kleinen Pfeil

SWT.CHECK Checkbox

SWT.PUSH Button mit Beschriftung oder Grafik

SWT.RADIO Optionsfeld

SWT.TOGGLE Umschaltfläche

Weiterhin existieren noch Stilkonstanten, die die Ausrichtung und das Aussehen festlegen.

SWT.LEFT
SWT.RIGHT Ausrichtung für Text oder Grafik
SWT.CENTER

211
eclipse_v01.book Seite 212 Montag, 30. Januar 2006 12:02 12

Das SWT

SWT.LEFT
SWT.RIGHT
Pfeilrichtung
SWT.UP
SWT.DOWN

SWT.FLAT Button wird nur zweidimensional dargestellt.

Neben Text kann ein Button analog zu einem Label auch mit einer Grafik versehen werden.
Hierfür stehen die Methoden setText bzw. setImage zur Verfügung.

// Checkbox
Button checkButton = new Button(composite, SWT.CHECK);
checkButton.setText("Checkbox");
// Pfeiltaste nach unten
Button arrowButton = new Button(composite, SWT.ARROW | SWT.DOWN);

Mithilfe der Methode getSelection kann für alle Arten von Buttons ermittelt werden, ob
ein Button angewählt worden ist.

Die Klasse Text


Textfelder werden repräsentiert durch die Klasse Text. Über Stilkonstanten lassen sich das
Aussehen und das Verhalten eines Textfelds steuern.

SWT.CENTER Inhalt des Textfelds wird zentriert ausgerichtet.


SWT.LEFT Inhalt des Textfelds wird linksbündig ausgerichtet.
SWT.RIGHT
Inhalt des Textfelds wird rechtsbündig ausgerichtet.
SWT.SINGLE Einzeiliges Textfeld
SWT.MULTI Mehrzeiliges Textfeld
SWT.PASSWORD Passwortfeld
SWT.READ_ONLY Inhalt des Textfelds kann nicht vom Benutzer verändert werden.
SWT.WRAP Automatischer Zeilenumbruch

Im Falle eines mehrzeiligen Textfelds kann dieses über die aus der Klasse Scrollable ge-
erbten Stilkonstanten SWT.H_SCROLL und SWT.V_SCROLL auch mit einer horizontalen
und vertikalen Scrollleiste versehen werden.

Text textFeld = new Text(composite, SWT.H_SCROLL | SWT.MULTI |


SWT.V_SCROLL | SWT.WRAP);

212
eclipse_v01.book Seite 213 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

Die Klasse Text verfügt über eine Vielzahl von Methoden für die programmseitige Text-
eingabe und -bearbeitung sowie für das Abrufen von Informationen.

Append Fügt Text an den im Textfeld bereits vorhandenen Text an.


clearSelection Entfernt die Auswahl.
Copy Kopiert den im Textfeld ausgewählten Text in die Zwischen-
ablage.
cut Schneidet den im Textfeld ausgewählten Text aus. Er steht in
der Zwischenablage zur Verfügung.
getCaretLineNumber Liefert für die Cursorposition die Zeilennummer. Die Zählung
beginnt bei 0.
getCaretPosition Liefert für die Cursorposition die Nummer des Zeichens. Die
Zählung beginnt bei 0.
getCharCount Liefert die Anzahl eingegebener Zeichen.
getSelectionCount Liefert die Anzahl ausgewählter Zeichen.
getSelectionText Liefert den ausgewählten Text.
getText Liefert den im Textfeld eingegebenen Text.
insert Fügt Text an der Cursorposition ein.
paste Fügt Text aus der Zwischenablage an der Cursorposition
ein.
selectAll Wählt den gesamten im Textfeld eingegebenen Text aus.
setSelection Wählt im Textfeld eingegebenen Text in einem vorgegebe-
nen Bereich aus.
setText Gibt Text im Textfeld ein.
showSelection Markiert ausgewählten Text.

Die Klasse List


Für Listen existiert im SWT die Klasse List. Über die Stilkonstanten SWT.SINGLE und
SWT.MULTI kann gesteuert werden, ob ein oder mehrere Einträge aus einer Liste ausge-
wählt werden können. Analog zum Textfeld kann auch eine Liste über die Stilkonstanten
SWT.H_SCROLL und SWT.V_SCROLL mit Scrollleisten versehen werden. Für das Hinzufü-
gen von Elementen zu einer Liste stehen die Methoden add, setItem bzw. setItems zur
Verfügung. Die Methode add fügt hierbei ein Listenelement immer am Ende der Liste an,
die Methode setItems fügt die angegebenen Elemente am Anfang der Liste ein und mit
der Methode setItem kann festgelegt werden, in welcher Zeile ein Listenelement einge-
fügt werden soll. Zu beachten ist, dass die Zählung bei 0 beginnt.

213
eclipse_v01.book Seite 214 Montag, 30. Januar 2006 12:02 12

Das SWT

// Liste mit Rahmen


List list = new List(composite, SWT.BORDER);
list.setItems(new String[] {"item 1", "item 2", "item 3"});
list.add("item 4");
list.setItem(1,"item 5");

Für die Ermittlung der ausgewählten Listenelemente sowie die programmseitige Auswahl
von Listenelementen stehen die folgenden Methoden zur Verfügung:

getSelection Liefert ein String-Array zurück, welches die Beschriftung


der ausgewählten Listenelemente enthält.
getSelectionIndex Liefert den Index des ersten ausgewählten Listenelements
zurück.
getSelectionIndices Liefert die Indizes aller ausgewählten Listenelemente
zurück.
setSelection Wählt Listenelemente aus.

Die Klasse Combo


Comboboxen werden im SWT repräsentiert durch die Klasse Combo und erlauben sowohl
eine Listenauswahl als auch die Texteingabe. Es werden folgende Stilkonstanten unter-
stützt:

SWT.DROP_DOWN Die Auswahlliste wird erst beim Klick auf die Pfeiltaste ange-
zeigt.
SWT.SIMPLE Die Auswahlliste ist immer sichtbar.
SWT.READ_ONLY Nur eine Listenauswahl, aber keine Texteingabe ist möglich.

Für das Hinzufügen von Elementen zu einer Liste existieren analog zur Klasse List die
Methoden add, setItem und setItems. Für die Ermittlung der ausgewählten Listenele-
mente bzw. des eingegebenen Texts sowie die programmseitige Auswahl von Listenele-
menten bzw. die Bearbeitung des Texts existieren folgende Methoden:

getItems Liefert ein String-Array zurück, welches die Beschriftung der


ausgewählten Listenelemente enthält.
getSelectionIndex Liefert den Index des ausgewählten Listenelements zurück.
getText Liefert den im Textfeld eingegebenen Text.
select Wählt ein Listenelement aus.
setText Gibt Text im Textfeld ein.

214
eclipse_v01.book Seite 215 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

Die Klasse Spinner


Für die Eingabe von numerischen Werten existiert ab der Version 3.1 von Eclipse die Klasse
Spinner. Die Eingabe kann hierbei direkt in einem Eingabefeld erfolgen oder durch Be-
tätigen von Pfeil-nach-oben- bzw. Pfeil-nach-unten-Buttons, die sich rechts neben dem
Eingabefeld befinden. Als Stilkonstante steht SWT.READ_ONLY zur Verfügung, die steuert,
ob der Inhalt des Spinners vom Benutzer verändert werden kann.

Abb. 6.16: Spinner

Für die Ermittlung der eingegebenen Zahl und die Gestaltung des numerischen Eingabe-
felds existieren folgende Methoden:

getDigits Liefert die eingegebene Zahl.


setDigits Gibt eine Zahl ein.
setIncrement Setzt Schrittweite für die Pfeil-nach-oben- bzw. Pfeil-nach-
unten-Buttons.
setMaximum Setzt Maximalwert.
setMinimum Setzt Minimalwert.

Die Klassen Table, TableColumn und TableItem


Die Klasse Table dient zur Darstellung von Tabellen. Sie besitzen im SWT standardmäßig
eine Spalte. Soll eine Tabelle mehr als eine Spalte aufweisen, muss ihr die gewünschte An-
zahl Spalten hinzugefügt werden. Für Tabellenspalten existiert die Klasse TableColumn.
Hierbei erfolgt das Hinzufügen einer Spalte zu einer Tabelle, indem beim Erzeugen des
TableColumn-Objekts das Table-Objekt im Konstruktor angegeben wird.

Table table = new Table(composite, SWT.BORDER);


TableColumn tableColumn = new TableColumn(table, SWT.NONE);

Um Tabelleneinträge zu definieren, müssen einer Tabelle Tabellenzeilen hinzugefügt wer-


den. Tabellenzeilen werden repräsentiert durch die Klasse TableItem. Das Hinzufügen ei-
ner Tabellenzeile zur Tabelle geschieht analog zum Hinzufügen von Tabellenspalten.

TableItem tableItem = new TableItem(table, SWT.NONE);

Hierbei kann immer am Ende angefügt werden oder aber auch an beliebiger Stelle einge-
fügt werden. In diesem Fall muss im Konstruktor die gewünschte Position durch Angabe
der Zeilennummer angegeben werden, wobei die Zählung bei 0 beginnt. Im folgenden Bei-
spiel ist tableItem1 die erste Zeile und tableItem2 zunächst die zweite. TableItem3
wird dann als zweite Zeile eingefügt, so dass tableItem2 anschließend die dritte Zeile ist.

215
eclipse_v01.book Seite 216 Montag, 30. Januar 2006 12:02 12

Das SWT

TableItem tableItem1 = new TableItem(table, SWT.NONE);


TableItem tableItem2 = new TableItem(table, SWT.NONE);
TableItem tableItem3 = new TableItem(table, SWT.NONE,1);

Die Tabelleneinträge werden den TableItem-Objekten mithilfe der Methode setText


hinzugefügt. Hierbei existieren verschiedene Möglichkeiten.

setText(String string) Fügt in der gewählten Zeile in der ersten Spalte ein.
setText(int index, Fügt in der gewählten Zeile in der index+1-ten
String string) Spalte ein.
setText(String[] strings) Fügt in der gewählten Zeile bei den ersten n Spalten
ein, wobei n die Dimension von strings ist.

tableItem1.setText("S1Z1");
tableItem1.setText(1,"S2Z1");
tableItem2.setText(new String("S1Z2", "S2Z2");

Die Klasse Table besitzt folgende Stilkonstanten:

SWT.SINGLE Der Benutzer kann nur eine Tabellenzeile auswählen.


SWT.MULTI Der Benutzer kann mehrere Tabellenzeilen auswählen.
SWT.FULL_SELECTION Die gesamte Tabellenzeile ist selektierbar (plattform-
abhängig).
SWT.HIDE_SELECTION Die Tabellenzeilen sind nicht selektierbar (plattform-
abhängig).
SWT.CHECK Vor jeder Tabellenzeile wird eine Checkbox abgebildet.

Darüber hinaus lässt sich eine Tabelle über die aus der Klasse Scrollable geerbten Stil-
konstanten SWT.H_SCROLL und SWT.V_SCROLL mit Scrollleisten versehen.
Mithilfe der Methoden setHeaderVisible und setLinesVisible kann festgelegt
werden, ob Spaltenüberschriften und Trennlinien sichtbar sein sollen. Die Methoden set-
Background bzw. setForeground ermöglichen es, die Hintergrundfarbe der Tabellen-
zellen bzw. die Schriftfarbe der Tabelleneinträge festzulegen. Werden sie in Zusammen-
hang mit einem Table-Objekt verwendet, gelten die Einstellungen für alle Zellen der
Tabelle. Angewendet auf ein TableItem-Objekt können aber auch für einzelne Zellen
individuelle Einstellungen vorgenommen werden.
Die Klasse TableColumn stellt die Stilkonstanten SWT.LEFT, SWT.RIGHT und SWT.CEN-
TER zur Verfügung, um die Ausrichtung einer Tabellenspalte festzulegen.
In der Klasse TableItem sind keine Stilkonstanten definiert.

216
eclipse_v01.book Seite 217 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

Die Klassen Slider und Scale


Für die Eingabe eines numerischen Werts mittels eines Schiebereglers existieren die beiden
Klassen Slider und Scale. Im Gegensatz zur Klasse Slider verfügt die Klasse Scale
über eine Skaleneinteilung.

Abb. 6.17: Slider und Scale

Die Anordnung wird sowohl bei der Klasse Slider als auch bei der Klasse Scale über die
Stilkonstanten SWT.HORIZONTAL und SWT.VERTICAL beeinflusst.
Für die weiteren möglichen Einstellungen existieren für beide Klassen die folgenden Me-
thoden:

setIncrement Setzt Schrittweite.


setMaximum Setzt Maximalwert.
setMinimum Setzt Minimalwert.
setPageIncrement Setzt Schrittweite.
setSelection Setzt aktuelle Position.

Die Klasse Slider weist darüber hinaus noch folgende Methoden auf:

setThumb Setzt die Größe des Handgriffs.


setValues Setzt alle Werte in einer Methode.

Die aktuelle Position kann mit der Methode getSelection abgefragt werden.

Die Klasse ProgressBar


Fortschrittsbalken werden durch die Klasse ProgressBar repräsentiert. Sie weist die fol-
genden Stilkonstanten auf:

SWT.HORIZONTAL Horizontale Anordnung


SWT.VERTICAL Vertikale Anordnung
SWT.SMOOTH Bewirkt, dass der Fortschrittsbalken nicht durchbrochen
gezeichnet wird.
SWT.INDERMINATE Bewirkt, dass der Fortschrittsbalken endlos durchläuft.

217
eclipse_v01.book Seite 218 Montag, 30. Januar 2006 12:02 12

Das SWT

Des Weiteren existieren analog zu den Klassen Slider und Scale die Methoden set-
Maximum, setMinimum sowie setSelection und getSelection. Im folgenden Bei-
spiel ist ein Fortschrittsbalken implementiert, der unterbrochen jeweils von einer Pause von
1000 Millisekunden vom Wert 10 auf den Wert 20 wächst. Zu beachten ist, dass der Fort-
schritt nur sichtbar wird, wenn die Schleife zum Setzen der Werte nach dem Öffnen der
Shell ausgeführt wird.

ProgressBar progBar = new ProgressBar(shell, SWT.SMOOTH);


progBar.setMinimum(10);
progBar.setMaximum(20);
shell.open();
for (int i=progBar.getMinimum(); i<= progBar.getMaximum(); i++) {
progressBar.setSelection(i);
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {}
}

Die Klasse ScrollBar


Die für Scrollleisten im SWT vorhandene Klasse ScrollBar wird bei einer Reihe von
Widgets realisiert, die von vornherein mit Scrollleisten ausgestattet werden können, wie
z.B. ein Textfeld oder eine Tabelle. Die Klasse ähnelt stark der Klasse Slider, ist aber im
Gegensatz zur Klasse Slider keine Subklasse der Klasse Control, so dass Scrollleisten
nicht beliebigen anderen GUI-Elementen hinzugefügt werden können. Scrollleisten kön-
nen nur GUI-Elementen hinzugefügt werden, die über die Stilkonstanten SWT.H_SCROLL
und SWT.V_SCROLL verfügen. Das Hinzufügen erfolgt in diesem Fall über den Aufruf der
Stilkonstanten im Konstruktor.

Table table = new Table(composite, SWT.V_SCROLL);

Die Klassen Tree und TreeItem


Für die Darstellung von Bäumen und deren Baumknoten verfügt das SWT über die Klassen
Tree und TreeItem. Die Baumknoten der ersten Hierarchieebene werden einem Baum
hinzugefügt, indem beim Erzeugen des TreeItem-Objekts im Konstruktor das Tree-Ob-
jekt angegeben wird. Bei den Baumknoten tieferer Hierarchieebenen wird das übergeord-
nete TreeItem-Objekt angegeben.
Die Funktionsweise und das Aussehen werden durch die Stilkonstanten der Klasse Tree
festgelegt.

SWT.SINGLE Der Benutzer kann nur einen Baumknoten auswählen.


SWT.MULTI Der Benutzer kann mehrere Baumknoten auswählen.

218
eclipse_v01.book Seite 219 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

SWT.CHECK Vor jedem Baumknoten wird eine Checkbox abgebildet.


SWT.FULL_SELECTION Die gesamte Zeile eines Baumknotens wird markiert.

Die Beschriftungen für die Baumknoten werden mithilfe der Methode setText gesetzt.
Darüber hinaus kann jedem Baumknoten neben einer Beschriftung noch mithilfe der Me-
thode setImage eine Grafik zugeordnet werden.

Tree tree = new Tree(composite, SWT.BORDER | SWT.SINGLE);


TreeItem treeItem1 = new TreeItem(tree, SWT.NONE);
treeItem1.setText("Knoten 1");
TreeItem treeItem2 = new TreeItem(tree, SWT.NONE);
treeItem2.setText("Knoten 2");
TreeItem treeItem11 = new TreeItem(treeItem1, SWT.NONE);
treeItem11.setText("Knoten 1.1");

Mithilfe der Methode getSelection können für einen Baum die angewählten Baumkno-
ten ermittelt werden. Die Methode setSelection erlaubt eine programmseitige Auswahl
von Baumknoten.

Die Klassen Sash und SashForm


Im SWT kann man ein Composite über Trennleisten in mehrere Bereiche aufteilen, wobei
sich die Trennleisten verschieben lassen. Hierfür existiert die Klasse Sash. Da nach einem
Verschieben die neue Position der Trennleiste und der Nachbarelemente nicht automatisch
angezeigt wird, muss bei der Verwendung der Klasse Sash eine aufwändige Ereignisver-
arbeitung implementiert werden.
Daher empfiehlt es sich, stattdessen die Klasse SashForm zu verwenden. Diese repräsen-
tiert ein Composite, das in zwei Bereiche aufgeteilt ist. Die Anordnung der beiden Bereiche
– horizontal oder vertikal – wird hierbei über die Stilkonstanten SWT.HORIZONTAL und
SWT.VERTICAL gesteuert.

shell.setLayout(new FillLayout());
SashForm sash = new SashForm(shell, SWT.VERTICAL);
Composite compOben = new Composite(sash, SWT.NONE);
compOben.setLayout(new GridLayout());
Button buttonOben = new Button(compOben, SWT.NONE);
button1.setText("Oberer Button");
Composite compUnten = new Composite(sash, SWT.NONE);
compUnten.setLayout(new GridLayout());
Button buttonUnten = new Button(compUnten, SWT.NONE);
button2.setText("Unterer Button");

Im Gegensatz zur Klasse Sash befindet sich die Klasse SashForm nicht im Package
org.eclipse.swt.widgets, sondern im Package org.eclipse.swt.custom.

219
eclipse_v01.book Seite 220 Montag, 30. Januar 2006 12:02 12

Das SWT

Die Klassen TabFolder und TabItem


Die Klasse TabFolder implementiert zunächst einen Registerkartenbereich. Diesem müs-
sen dann die einzelnen Register hinzugefügt werden, indem Objekte der Klasse TabItem
erzeugt werden. Die Beschriftung der Registerkarten erfolgt mithilfe der Methode set-
Text. Um bei Wahl eines Registers spezifische GUI-Elemente anzeigen zu können, muss
für jedes Register ein Composite oder eine Group erstellt und über die Methode setCon-
trol dem Register zugeordnet werden. Neben der aus der Klasse Control geerbten Stil-
konstante SWT.BORDER verfügt die Klasse TabFolder über die beiden Stilkonstanten
SWT.TOP und SWT.BOTTOM, über die festgelegt werden kann, ob die Registerkarten oben
oder unten angeordnet werden sollen.

shell.setLayout(new FillLayout());
TabFolder tabFolder = new TabFolder(shell, SWT.NONE);
TabItem tabItem1 = new TabItem(tabFolder, SWT.NONE);
tabItem1.setText("Item 1");
Composite composite = new Composite(tabFolder,SWT.NONE);
composite.setLayout(new GridLayout());
tabItem1.setControl(composite);
Button button1 = new Button(composite, SWT.NONE);
button1.setText("Button 1");
TabItem tabItem2 = new TabItem(tabFolder, SWT.NONE);
tabItem2.setText("Item 2");
Group group = new Group(tabFolder,SWT.NONE);
group.setLayout(new GridLayout());
tabItem2.setControl(composite2);
Button button2 = new Button(group, SWT.NONE);
button2.setText("Button 2");

Während die Methode getSelection ein Array der ausgewählten Register zurückliefert,
lässt sich mit der Methode getSelectionIndex der Index des ersten angewählten Regis-
ters ermitteln. Die Methode setSelection ermöglicht die programmseitige Auswahl von
Registern.

Die Klassen ToolBar und ToolItem


Für die Implementation von Werkzeugleisten stehen die Klassen ToolBar und ToolItem
zur Verfügung. Jedes ToolBar-Objekt ist hierbei ein Composite, welches ein oder mehrere
Buttons beinhaltet. Die Buttons werden hierbei durch ToolItem-Objekte repräsentiert.
Das Erscheinungsbild von Werkzeugleisten lässt sich mithilfe der folgenden Stilkonstanten
festlegen:

SWT.FLAT Zweidimensionale statt dreidimensionale Darstellung (platt-


formabhängig)
SWT.WRAP Automatischer Umbruch

220
eclipse_v01.book Seite 221 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

SWT.RIGHT Rechtsbündige Anordnung


SWT.HORIZONTAL Horizontale Anordnung
SWT.VERTICAL Vertikale Anordnung

Das Aussehen der Buttons wird ebenfalls über Stilkonstanten festgelegt.

SWT.PUSH Button
SWT.CHECK Checkbox
SWT.RADIO
Optionsfeld
SWT.SEPARATOR
Separator
SWT.DROP_DOWN
Button mit Pfeiltaste

Die Beschriftung eines Buttons erfolgt auch hier über die Methode setText. Soll der But-
ton eine Grafik beinhalten, muss die Methode setImage verwendet werden. Für die Defi-
nition eines Tooltipps existiert die Methode setToolTipText. Mithilfe der Methode
getSelection kann ermittelt werden, ob ein Button angewählt wurde.
Wird als Stilkonstante SWT.DROP_DOWN gewählt, erscheint nicht automatisch beim Klick
auf die Pfeiltaste ein Drop-Down-Menü. Dieses muss explizit programmiert werden.

Die Klassen CoolBar und CoolItem


Die Klasse CoolBar ermöglicht die Implementation einer verschiebbaren Werkzeugleiste.
Hierzu wird ein ToolBar-Objekt mithilfe der Methode setControl in ein CoolItem-
Objekt eingebettet. Das CoolItem-Objekt wiederum wird einem CoolBar-Objekt hinzu-
gefügt, indem beim Erzeugen des CoolItem-Objekts im Konstruktor das CoolBar-Objekt
angegeben wird. Zu beachten ist, dass für das CoolItem-Objekt eine minimale Größe ge-
setzt werden muss.

Die Klassen Menu und MenuItem


Für Menüs existieren die Klassen Menu und MenuItem. Um eine Menüleiste mit Menüs
und Menübefehlen aufzubauen, muss zunächst die Menüleiste als Objekt der Klasse Menu
erzeugt werden. Hierbei ist als Typ SWT.BAR anzugeben und als übergeordnetes Composite
die Shell. Dann muss die Menüleiste mithilfe der Methode setMenuBar aktiviert werden.

Menu menuBar = new Menu(shell,SWT.BAR);


shell.setMenuBar(menuBar);

Anschließend werden für jedes Menü ein Menütitel sowie das Menü selbst erzeugt. Einen
Menütitel erhalten wir hierbei durch das Erzeugen eines Objekts der Klasse Menu, wobei
als übergeordnetes Composite die Shell und als Typ in diesem Fall SWT.DROP_DOWN anzu-
geben ist. Das Menü selbst wird repräsentiert durch ein Objekt der Klasse MenuItem. Als
Stilkonstante ist hier beim Erzeugen des Objekts im Konstruktor SWT.CASCADE zu wählen.

221
eclipse_v01.book Seite 222 Montag, 30. Januar 2006 12:02 12

Das SWT

Mithilfe der Methode setText kann dem Menü anschließend eine Beschriftung zugewie-
sen werden und über die Methode setMenu muss die Zuordnung Menü-Menütitel vorge-
nommen werden.

Menu menuDateiTit = new Menu(shell, SWT.DROP_DOWN);


MenuItem menuDatei = new MenuItem(menuBar, SWT.CASCADE);
menuDatei.setText("Datei");
menuDatei.setMenu(menuDateiTit);

Das Hinzufügen eines Menüeintrags zu einem Menü erfolgt nun durch Erzeugen eines Ob-
jekts der Klasse MenuItem. Hierbei werden der zugehörige Menütitel sowie die gewünsch-
te Art des Menüeintrags angegeben. Als Stilkonstanten für die verschiedenen Arten stehen
zur Verfügung:

SWT.CHECK Menüeintrag mit Check-Symbol


SWT.PUSH Normaler Menüeintrag
SWT.RADIO
Menüeintrag mit gegenseitiger Auslösung
SWT.SEPARATOR
Passive Trennlinie

Für die Definition der Beschriftung ist wiederum die Methode setText zuständig.

MenuItem menuDateiBeenden = new MenuItem(menuDateiTit, SWT.PUSH);


menuDateiBeenden.setText("Beenden");

6.4.3 Layoutmanager
Analog zum AWT und zu Swing besteht im SWT sowohl die Möglichkeit, die Position und
Größe von Komponenten explizit festzulegen als auch Layoutmanager zu verwenden, die
die Komponenten auf einem Composite automatisch anordnen und dabei Position und Grö-
ße berechnen. Wird das Composite in der Größe verändert, wird die Anordnung der Kom-
ponenten automatisch neu berechnet.
Das SWT stellt die fünf Layouts FillLayout, RowLayout, GridLayout, FormLayout und
StackLayout zur Verfügung. Darüber hinaus können aber auch eigene Layouts definiert
werden.
Layouts werden einem Composite mithilfe der Methode setLayout zugewiesen. Wäh-
rend beim FillLayout und beim StackLayout alle Komponenten gleich behandelt werden,
erlauben die anderen drei Layouts für jede Komponente spezifische Einstellungen. Diese
Layoutdaten werden einer Komponente mit der Methode setLayoutData zugewiesen.
Einzelheiten hierzu werden bei der folgenden detaillierten Betrachtung der verschiedenen
Layouts vorgestellt.

222
eclipse_v01.book Seite 223 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

Das FillLayout
Das FillLayout ist das einfachste der im SWT bereits definierten Layouts. Hierbei sind alle
GUI-Elemente gleich groß und werden in einer Zeile oder Spalte angeordnet, wobei kein
Zeilenumbruch möglich ist. Die GUI-Elemente füllen das gesamte Composite – abzüglich
eines definierbaren Randbereichs – aus. Für die einzelnen GUI-Elemente sind keine indi-
viduellen Einstellungen möglich. Für das Composite dagegen existieren folgende Einstel-
lungsmöglichkeiten:

Layoutoption Beschreibung Voreinstellung


marginHeight Größe des Randbereichs oben und unten in Pixel 0
marginWidth Größe des Randbereichs oben rechts und links in 0
Pixel
spacing Zwischenraum zwischen GUI-Elementen in Pixel 0
(für alle GUI-Elemente gleich)
type Anordnung horizontal (SWT.HORIZONTAL) / verti- SWT.HORIZON-
kal (SWT.VERTICAL) TAL

Der Zugriff auf die Attribute marginHeight, marginWidth, space und type erfolgt
hierbei direkt und nicht über eine Setter-Methode.

FillLayout fillLayout = new FillLayout();


fillLayout.type = SWT.VERTICAL;
composite.setLayout(fillLayout);

Anwendung findet das FillLayout z.B. für Werkzeugleisten, die Zusammenfassung von
Checkboxen in einer Group oder wenn ein GUI-Element ein Composite komplett ausfüllen
soll, wie beispielsweise ein Composite-Objekt die Shell.

Abb. 6.18: Fenster mit FillLayout in verschiedenen Größen jeweils ohne Randbereich und
Zwischenraum

223
eclipse_v01.book Seite 224 Montag, 30. Januar 2006 12:02 12

Das SWT

Das RowLayout
Ähnlich wie beim FillLayout ordnet das RowLayout die GUI-Elemente in einer Zeile oder
Spalte an. Allerdings sind Zeilenumbrüche möglich. Darüber hinaus existieren einige wei-
tere Einstellungsmöglichkeiten. Im Folgenden sind alle Layoutoptionen aufgelistet:

Layoutoption Beschreibung Voreinstellung


fill Wird die Einstellung auf true gesetzt, haben false
alle GUI-Elemente bei horizontaler Anordnung
die gleiche Höhe und bei vertikaler Anordnung
die gleiche Breite.
justify Wird die Einstellung auf true gesetzt, werden false
alle GUI-Elemente gleichmäßig über den ver-
fügbaren Platz verteilt.
marginBottom Größe des Randbereichs unten in Pixel 3
marginHeight Größe des Randbereichs oben und unten in 0
Pixel
marginLeft Größe des Randbereichs links in Pixel 3
marginRight Größe des Randbereichs rechts in Pixel 3
marginTop Größe des Randbereichs oben in Pixel 3
marginWidth Größe des Randbereichs links und rechts in 0
Pixel
pack Wird die Einstellung auf true gesetzt, werden true
alle GUI-Elemente in ihrer natürlichen Größe
und so weit links wie möglich dargestellt. Bei
false füllen sie das Composite aus.

spacing Zwischenraum zwischen GUI-Elementen in 3


Pixel
type Anordnung horizontal (SWT.HORIZONTAL) / SWT.HORIZONTAL
vertikal (SWT.VERTICAL)
wrap Wird die Einstellung auf true gesetzt, erfolgt true
bei Bedarf ein Zeilenumbruch.

Im folgenden Beispiel werden für einige Optionen von der Voreinstellung abweichende
Einstellungen vorgenommen. Hierbei ist das Setzen der Option type auch beim Erzeugen
des RowLayout-Objekts im Konstruktor möglich.

224
eclipse_v01.book Seite 225 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

RowLayout rowLayout = new RowLayout(SWT.VERTICAL);


rowLayout.wrap = false;
rowLayout.pack = false;
rowLayout.justify = true;
rowLayout.marginLeft = 15;
rowLayout.marginTop = 10;
rowLayout.marginRight = 15;
rowLayout.marginBottom = 10;
rowLayout.spacing = 7;
composite.setLayout(rowLayout);

Sollen für alle Optionen die Voreinstellungen realisiert werden, ist nur die folgende Anwei-
sung erforderlich:

composite.setLayout(new RowLayout());

Im Gegensatz zum FillLayout können für die GUI-Elemente eines Composite mit RowLay-
out die Breite und die Höhe jedes einzelnen GUI-Elements gesetzt werden. Hierzu müssen
wir ein Objekt der Klasse RowData erzeugen, die Breite und Höhe des GUI-Elements ent-
weder bei der Erzeugung des RowData-Objekts oder aber durch explizites Festlegen der
Werte setzen und das RowData-Objekt mithilfe der Methode setLayoutData dem GUI-
Element zuweisen.

composite.setLayout(new RowLayout());
Button button1 = new Button(composite, SWT.PUSH);
button1.setText("100x50");
button1.setLayoutData(new RowData(100,50));
Button button2 = new Button(composite, SWT.PUSH);
button2.setText("75x50");
RowData rowData = new RowData();
rowData.width = 75;
rowData.height = 50;
button2.setLayoutData(rowData);

Abb. 6.19: Fenster mit RowLayout in verschiedenen Größen

225
eclipse_v01.book Seite 226 Montag, 30. Januar 2006 12:02 12

Das SWT

Des Weiteren kann für ein GUI-Element ab der Version 3.1 von Eclipse mithilfe der Lay-
outoption exclude festgelegt werden, dass es seine Position und Größe nicht durch den
Layoutmanager erhält und auch nicht für die Bestimmung der Position und Größe der an-
deren GUI-Elemente herangezogen wird. Die Voreinstellung ist hier false, d.h. Position
und Größe werden durch den Layoutmanager festgelegt.

Das GridLayout
Beim GridLayout werden die GUI-Elemente auf einem Gitternetz angeordnet. Da eine
Vielzahl von Einstellungsmöglichkeiten existieren, ist das GridLayout relativ kompliziert
zu handhaben, es bietet allerdings auch sehr umfangreiche Einsatzmöglichkeiten.
Im Einzelnen stehen folgende allgemeine Einstellungsmöglichkeiten zur Verfügung:

Layoutoption Beschreibung Voreinstellung


horizontalSpacing Zwischenraum zwischen den Spalten in 5
Pixel
makeColumnsEqualWidth Wird die Einstellung auf true gesetzt, false
wird allen Spalten die gleiche Breite
zugeordnet.
marginBottom Größe des Randbereichs unten in Pixel 0
marginHeight Größe des Randbereichs oben und 5
unten in Pixel
marginLeft Größe des Randbereichs links in Pixel 0
marginRight Größe des Randbereichs rechts in 0
Pixel
marginTop Größe des Randbereichs oben in Pixel 0
marginWidth Größe des Randbereichs links und 5
rechts in Pixel
numColumns Anzahl der Spalten. Die Anzahl der Zei- 1
len ergibt sich automatisch aus der
Anzahl der GUI-Elemente und der
Spalten.
verticalSpacing Zwischenraum zwischen den Zeilen in 5
Pixel

Die allgemeinen Einstellungsmöglichkeiten werden analog zum RowLayout gesetzt, wobei


die Layoutoptionen numColumns und makeColumnsEqualWidth auch beim Erzeugen
des GridLayout-Objekts im Konstruktor angegeben werden können.

226
eclipse_v01.book Seite 227 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

GridLayout gridLayout = new GridLayout(3, true);


gridLayout.marginHeight = 10;
composite.setLayout(gridLayout);

Neben den allgemeinen Einstellungsmöglichkeiten für die GUI-Elemente existieren eine


Reihe von individuellen Layoutoptionen.

Layoutoption Beschreibung Voreinstellung


exclude Wird die Einstellung auf true false
gesetzt, erhält das GUI-Element
Position und Größe nicht durch
den Layoutmanager und wird
auch nicht für die Bestimmung
der Position und Größe der
anderen GUI-Elemente heran-
gezogen.
grapExcessHorizontalSpace Wird die Einstellung auf true false
gesetzt, passt sich das GUI-Ele-
ment horizontal der Zellgröße
an.
grapExcessVerticalSpace Wird die Einstellung auf true false
gesetzt, passt sich das GUI-Ele-
ment vertikal der Zellgröße an.
heightHint Höhe des GUI-Elements in Pixel SWT.DEFAULT

horizontalAlignment Horizontale Ausrichtung des GridData.


GUI-Elements: BEGINNING
GridData.BEGINNING
GridData.CENTER
GridData.END
GridData.FILL

horizontalIndent Linker Einzug eines GUI-Ele- 0


ments in Pixel
horizontalSpan Gibt an, wie viele Zellen das GUI- 1
Element in horizontaler Richtung
benötigt.
minimumHeight Minimale Höhe eines GUI-Ele- 0
ments in Pixel (nur wirksam,
wenn grapExcessVertical-
Space=true)

227
eclipse_v01.book Seite 228 Montag, 30. Januar 2006 12:02 12

Das SWT

Layoutoption Beschreibung Voreinstellung


minimumWidth Minimale Breite eines GUI-Ele- 0
ments in Pixel (nur wirksam,
wenn grapExcessHorizontal-
Space=true)

verticalAlignment Vertikale Ausrichtung des GUI- GridData.


Elements: CENTER
GridData.BEGINNING
GridData.CENTER
GridData.END
GridData.FILL

verticalIndent Oberer Einzug eines GUI-Ele- 0


ments in Pixel
VerticalSpan Gibt an, wie viele Zellen das 1
GUI-Element in vertikaler Rich-
tung benötigt.
WidthHint Breite des GUI-Elements in SWT.DEFAULT
Pixel

Die Layoutoptionen werden nach dem Erzeugen eines GridData-Objekts gesetzt:

Button button = new Button(composite, SWT.PUSH);


GridData gridData = new GridData();
gridData.widthHint = 100;
gridData.heightHint = 50;
button.setLayoutData(gridData);

Eine Reihe von Layoutoptionen für ein GUI-Element lassen sich aber auch bei der Erzeu-
gung des GridData-Objekts im Konstruktor vornehmen. Hierzu hält die Klasse GridData
folgende Konstruktoren bereit:
쐌 GridData(int style)
쐌 GridData(int width, int height)
쐌 GridData(int horizontalAlignment, int verticalAlignment, boolean
grapExcessHorizontalSpace, boolean grapExcessVerticalSpace)
쐌 GridData(int horizontalAlignment, int verticalAlignment, boolean
grapExcessHorizontalSpace, boolean grapExcessVerticalSpace, int
horizontalSpan, int verticalSpan)

228
eclipse_v01.book Seite 229 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

composite.setLayout(new GridLayout());
Button button = new Button(composite, SWT.PUSH);
GridData gridData = new GridData(GridData.CENTER,
GridData.BEGINNING,false,false);
gridData.widthHint = 100;
gridData.heightHint = 50;
gridData.horizontalSpan = 2;
button.setLayoutData(gridData);

Zu beachten ist, dass im Gegensatz zu Swing, wo beim GridBagLayout ein GridBagCon-


straints-Objekt für verschiedene GUI-Elemente verwendet werden kann, im SWT jedes
GUI-Element sein eigenes GridData-Objekt benötigt.

Abb. 6.20: Fenster mit GridLayout. Die Textfelder erstrecken sich über zwei Spalten.

Das FormLayout
Die Klasse FormLayout wurde mit der Version 2.0 von Eclipse eingeführt. Sie ermöglicht
ebenfalls eine sehr flexible Gestaltung eines GUI. Im Gegensatz zum GridLayout werden
die GUI-Elemente jedoch nicht in einem Gitter angeordnet, sondern es lässt sich für jede
Seite eines GUI der Abstand zu der Seite eines übergeordneten Composite oder eines an-
deren GUI-Elements festlegen.
Für das FormLayout selbst stehen analog zum RowLayout bzw. GridLayout die Einstel-
lungsmöglichkeiten marginBottom, marginHeight, marginLeft, marginRight,
marginTop, marginWidth und spacing zur Verfügung, die die Größe des Randbereichs
bzw. den Zwischenraum zwischen zwei GUI-Elementen definieren. Die Voreinstellung ist
hier überall 0.
Für jedes GUI-Element lassen sich über die Layoutoptionen heigth und width die Höhe
und Breite des GUI-Elements in Pixel angeben. Zum Festlegen der Abstände existieren die
Layoutoptionen top, bottom, left und right.
Individuelle Layouteinstellungen für einzelne GUI-Elemente werden in einer Instanz der
Klasse FormData gespeichert und an die Methode setLayoutData übergeben.

229
eclipse_v01.book Seite 230 Montag, 30. Januar 2006 12:02 12

Das SWT

shell.setLayout(new FormLayout());
Button button = new Button(shell, SWT.PUSH);
button.setText("OK");
FormData formData = new FormData();
button.setLayoutData(formData);

Das FormData-Objekt erlaubt jetzt das Festlegen der Größe des GUI-Elements sowie der
Abstände. Für die Definition der Abstände existieren grundsätzlich zwei Möglichkeiten.
Zum einen können Abstände nur für zwei Seiten definiert werden, und zwar für die Kom-
binationen links-oben, links-unten, rechts-oben, rechts-unten. In diesem Fall ist es erforder-
lich, die Größe für ein GUI-Element explizit zu setzen, und zwar entweder beim Erzeugen
des FormData-Objekts im Konstruktor oder direkt durch Wertzuweisung an die Attribute
heigth und width der Klasse FormData. Zum anderen können wir die Abstände auch für
alle vier Seiten festlegen, so dass hierüber automatisch auch die Größe eines GUI-Elements
definiert wird. Werden für ein GUI-Element keine Abstände definiert, wird es automatisch
links oben in der Ecke des übergeordneten Composite platziert. Werden für mehrere GUI-
Elemente keine Abstände festgelegt, werden alle übereinander ebenfalls in der linken obe-
ren Ecke des übergeordneten Composite angeordnet.
Für das Festlegen der Abstände ist die Klasse FormAttachment zuständig, wobei, wie
oben bereits erläutert, zwei Möglichkeiten bestehen. Es lassen sich für ein GUI-Element
sowohl die Abstände zu dem übergeordneten Composite als auch zu einem anderen GUI-
Element angeben. Sollen die Abstände zu einem übergeordneten Composite angegeben
werden, existieren wiederum zwei Möglichkeiten. Die Anweisung

formData.top = new FormAttachment(50,-5);

führt beispielsweise dazu, dass das GUI-Element vertikal an der Position (50/100)*Höhe-
5 platziert wird, wobei Höhe die Höhe des übergeordneten Composite in Pixel ist. Die Zahl
50 ist hier eine Prozentangabe, daher die Division durch 100. Die Anweisung

formData.top = new FormAttachment(100,200,5);

bewirkt, dass das GUI-Element vertikal an der Position (100/200)*Höhe+5 angeordnet


wird.

Abb. 6.21: Fenster mit FormLayout in verschiedenen Größen (Abstand zur Shell nach oben
und links jeweils 50%, Buttongröße vorgegeben)

230
eclipse_v01.book Seite 231 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

Abb. 6.22: Fenster mit FormLayout in verschiedenen Größen (Abstand zur Shell nach oben
und links jeweils 50%, unten und rechts jeweils 75%)

Für die Angabe der Abstände zu einem anderen GUI-Element hält die Klasse Form-
Attachment drei Konstruktoren bereit:
쐌 FormAttachment(control)
쐌 FormAttachment(control, abstand)
쐌 FormAttachment(control, abstand, ausrichtung)
Der Parameter control gibt hierbei das GUI-Element an, zu dem der Abstand festgelegt
werden soll, der Parameter abstand den Abstand in Pixel und der Parameter ausrich-
tung, zu welcher Seite des GUI-Elements der Abstand gelten soll. Wird der Parameter ab-
stand nicht angegeben, wird dieser auf Null gesetzt. Wird der Parameter ausrichtung
weggelassen, wird die nächstliegende Seite benutzt. Für die vertikale Ausrichtung existie-
ren die Stilkonstanten SWT.TOP, SWT.BOTTOM und SWT.CENTER. Für die horizontale Aus-
richtung die Stilkonstanten SWT.LEFT, SWT.RIGHT und SWT.CENTER.

Das StackLayout
Das StackLayout nimmt eine Sonderstellung unter den Layouts ein. Erstens befindet sich
die Klasse StackLayout nicht wie die Klassen der anderen Layouts im Package org.
eclipse.swt.layout, sondern im Package org.eclipse.swt.custom, und zweitens
wird innerhalb eines Composite immer nur ein GUI-Element angezeigt. Dieses liegt darin
begründet, dass alle GUI-Elemente gleich groß gemacht werden und an der gleichen Stelle
übereinander positioniert werden. Dadurch ist immer nur das oberste Element sichtbar. Als
Layoutoptionen stehen zur Verfügung:

Layoutoption Beschreibung Voreinstellung


marginHeight Größe des oberen und unteren Randbereichs 0
in Pixel
marginWidth Größe des linken und rechten Randbereichs in 0
Pixel
topControl Das sichtbare GUI-Element 0

Eine sinnvolle Anwendung für das StackLayout ist ein GUI, bei dem zwischen den ver-
schiedenen GUI-Elementen hin- und hergeschaltet werden soll.

231
eclipse_v01.book Seite 232 Montag, 30. Januar 2006 12:02 12

Das SWT

6.4.4 Ereignisverarbeitung
Ereignisse sind im SWT analog zum AWT zu Ereignistypen zusammengefasst. Es existie-
ren verschiedene Ereignistypen, die jeweils durch eine Klasse repräsentiert werden. Die
Klassen weisen die Namen XyzEvent auf, wobei Xyz für den Ereignistyp steht. Um ein
Ereignis für ein GUI-Element abzufangen, muss diesem der für das Ereignis relevante Lis-
tener mithilfe der Methode addXyzListener hinzugefügt werden. Xyz steht hierbei wie-
derum für den Ereignistyp. Des Weiteren muss eine Implementation des Listener-Interface
erfolgen. Analog zum AWT existiert in einem Listener-Interface für jedes Ereignis eine
Methode. Für Interfaces, die mehr als eine Methode aufweisen, existieren im SWT Adap-
terklassen.
Die Implementation des Ereignisses „Klick auf einen Button“ sieht beispielsweise wie folgt
aus:

Button button = new Button(composite, SWT.PUSH);


button.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
System.out.println("Button wurde angeklickt!");
}
public void widgetDefaultSelected(SelectionEvent e) {}
});

In diesem Fall wird das Interface als anonyme Klasse realisiert. Da für die widgetDe-
faultSelected-Methode kein Quelltext implementiert ist, bietet sich die Implementation
der entsprechenden Adapterklasse an.

Button button = new Button(composite, SWT.PUSH);


button.addSelectionListener(new SelectionAdapter () {
public void widgetSelected(SelectionEvent e) {
System.out.println("Button wurde angeklickt!");
}
});

Im Folgenden findet sich zunächst eine Übersicht über die Listener-Interfaces und die Er-
eignistyp-Klassen und anschließend eine Übersicht über die Methoden für die Ereignisse
eines Ereignistyps sowie die GUI-Elemente, für die die Ereignisse eintreten können. Es
werden allerdings nur GUI-Elemente aufgeführt, die hier zuvor vorgestellt worden sind.

232
eclipse_v01.book Seite 233 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

Listener-Interface / Beschreibung
Ereignistyp-Klasse
ArmListener / Tritt auf, wenn ein GUI-Element für die Selektion vorbe-
ArmEvent reitet wird.
ControlListener / Tritt auf, wenn ein GUI-Element bewegt oder in der
ControlEvent Größe verändert wird.
DisposeListener / Tritt auf, wenn ein GUI-Element entsorgt wird.
DisposeEvent

FocusListener / Tritt auf, wenn ein GUI-Element den Fokus erhält oder
FocusEvent verliert.
HelpListener / Tritt auf, wenn für ein GUI-Element Hilfe angefordert
HelpEvent wird, z.B. durch Drücken der É-Taste.
KeyListener / Tritt auf, wenn Tasten der Tastatur betätigt werden.
KeyEvent

MenuListener / Tritt auf, wenn Menüs ein- oder ausgeblendet werden.


MenuEvent

ModifyListener / Tritt auf, wenn Text modifiziert wurde.


ModifyEvent

MouseListener / Tritt auf, wenn die Maustasten betätigt werden.


MouseEvent

MouseMoveListener / Tritt auf, wenn die Maus bewegt wird.


MouseEvent

MouseTrackListener / Tritt auf, wenn die Maus sich über ein GUI-Element
MouseEvent bewegt.
PaintListener / Tritt auf, wenn ein GUI-Element neu gezeichnet werden
PaintEvent muss.
SelectionListener / Tritt auf, wenn ein GUI-Element selektiert wird.
SelectionEvent

ShellListener / Tritt auf, wenn der Zustand einer Shell sich ändert.
ShellEvent

TraverseListener / Tritt auf, wenn der Benutzer mit der TAB-Taste zum
TraverseEvent nächsten Feld springt.
TreeListener / Tritt auf, wenn Baumknoten ein- oder ausgeblendet wer-
TreeEvent den.
VerifyListener / Tritt auf, bevor Text modifiziert wird.
VerifyEvent

233
eclipse_v01.book Seite 234 Montag, 30. Januar 2006 12:02 12

Das SWT

Ereignistyp Methoden für die Ereignisse GUI-Elemente, für die die


Ereignisse eintreten können

Arm widgetArmed MenuItem

Control controlMoved, Control, TableColumn


controlResized

Dispose widgetDisposed Widget

Focus focusGained, focusLost Control

Help helpRequested Control, Menu, MenuItem

Key keyPressed, keyReleased Control

Menu menuHidden, Menu


menuShown

Modify modifyText Combo, Spinner, Text

Mouse mouseDoubleClick, Control


mouseDown, mouseUp

MouseMove mouseMove Control

MouseTrack mouseEnter, Control


mouseExit, mouseHover

Paint paintControl Control

Selection widgetDefaultSelected, Button, Combo, CoolItem, List,


widgetSelected MenuItem, Sash, Scale, Slider,
ScrollBar, Spinner, TabFolder,
Table, TableColumn, Text, Tool-
Item, Tree

Shell shellActivated, Shell


shellClosed,
shellDeactivated,
shellDeiconified,
shellIconified

Traverse keyTraversed Control

Tree treeCollapsed, Tree


treeExpanded

Verify verifyText Text

Die Implementation der Reaktion auf ein Ereignis macht es häufig erforderlich, dass auf be-
stimmte Informationen zugegriffen wird, beispielsweise, welches Listenelement bei einer
Liste ausgewählt ist. Eine Reihe von Methoden steht hierfür jeweils in den Klassen für die
GUI-Elemente zur Verfügung. Darüber hinaus liefert die Methode getSource aus der

234
eclipse_v01.book Seite 235 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

Klasse java.util.EventObject das das Ereignis auslösende GUI-Element. Des Weite-


ren verfügen einige Ereignistyp-Klassen über Attribute, auf die direkt zugegriffen werden
kann, wie beispielsweise das Attribut character der Klasse KeyEvent, welches das ein-
gegebene Zeichen liefert, oder die Attribute x und y der Klasse MouseEvent, die die Posi-
tion liefern, an der die Maus gedrückt worden ist.

6.5 Besonderheiten bei der Erstellung eines GUI mit


SWT-Komponenten
Nachdem in Abschnitt 6.3 die Besonderheiten bei der Entwicklung eines GUI mit Swing-
Komponenten betrachtet worden sind, werden nach der Beschreibung der Grafikbibliothek
SWT in diesem Abschnitt die Besonderheiten des Visual Editors für SWT vorgestellt.

6.5.1 Unterstützte Komponenten


Wird bei der Erstellung einer Klasse für ein GUI unter FILE | NEW | OTHER | VISUAL CLASS |
STYLE ein SWT-Eintrag gewählt, stehen in der Palette neben den vier Kategorien für AWT-
und Swing-Komponenten auch drei Kategorien mit den folgenden SWT-Komponenten zur
Verfügung:
쐌 SWT Controls
Button, CheckBox, RadioButton, Label, CLabel, Link, Text, TextArea,
StyledText, List, Combo, CCombo, Progress Bar, Spinner, Slider, Scale,
Table, TableColumn, Tree, ToolBar, ToolItem.Push, ToolItem.Check, Tool-
Item.Radio, ToolItem.Dropdown, ToolItem.Separator, Browser
쐌 SWT Containers
Shell, Composite, Group, TabFolder, CTabFolder, CoolBar, CBanner, Sash-
Form, ViewForm, Canvas, ScrolledComposite
쐌 SWT Menus
MenuBar, MenuItem.Push, MenuItem.Submenu, MenuItem.Separator, Menu-
Item.Check, MenuItem.Radio

6.5.2 Layoutmanager
Wenn wir ein GUI mit SWT-Komponenten erstellen, lassen sich die Layoutmanager Fill-
Layout, FormLayout, GridLayout und RowLayout über die Properties View anwählen. De-
ren allgemeine und individuelle Einstellungsmöglichkeiten lassen sich wiederum ebenfalls
in der Properties View realisieren, und zwar über die Eigenschaft layout (allgemein) bzw.
layoutdata (individuell). Darüber hinaus können die allgemeinen Einstellungen zum
FillLayout, GridLayout und RowLayout alternativ auch über das Register LAYOUT im Cus-
tomize Layout-Fenster vorgenommen werden. Beim GridLayout sind die individuellen
Einstellungen ebenfalls über das Customize Layout-Fenster möglich. In diesem Fall ist das
Register COMPONENT zu wählen.

235
eclipse_v01.book Seite 236 Montag, 30. Januar 2006 12:02 12

Besonderheiten bei der Erstellung eines GUI mit SWT-Komponenten

Abb. 6.23: Customize Layout-Fenster für allgemeine Einstellungen FillLayout und


RowLayout

Abb. 6.24: Customize Layout-Fenster für allgemeine und individuelle Einstellungen


GridLayout

Beim Hinzufügen einer Komponente zu einem Container mit GridLayout werden zwar
nicht wie beim GridBagLayout die Zeile und Spalte des Gitternetzes am Mauscursor ein-
geblendet. Jedoch kann auch hier eine Komponente sehr leicht mithilfe der Maus in einer
neuen Zeile oder Spalte positioniert werden, indem wir analog zum GridBagLayout mit der
Maus in der Zeile auf die vertikale Gitternetzlinie gehen, wo die neue Komponente links in
einer neuen Spalte eingefügt werden soll, bzw. in der Spalte auf die horizontale Gitternetz-
linie, wo die neue Komponente oberhalb in einer neuen Zeile eingefügt werden soll.
Abschließend sei noch darauf hingewiesen, dass Layoutoptionen, die erst seit der Version
3.1 von Eclipse existieren (allgemeine Einstellungen GridLayout und FormLayout:
marginBottom, marginLeft, marginRight, marginTop, individuelle Einstellungen
RowLayout bzw. GridLayout: exclude, minimumWidth, minimumHeight, vertical-
Indent), in der hier betrachteten Version 1.1.0.1 vom Visual Editor direkt im Quelltext
realisiert werden müssen. Des Weiteren ist das Festlegen der Abstände beim FormLayout
ebenfalls bisher nicht möglich.

236
eclipse_v01.book Seite 237 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

6.5.3 Codegenerierung
Wählt der Benutzer bei der Erstellung einer neuen Klasse im VE New Class Wizard unter
STYLE | SWT den Eintrag SHELL, wird eine neue Klasse implementiert, die das Attribut
sShell vom Typ org.eclipse.swt.widgets.Shell sowie die Methode create-
SShell aufweist. In dieser Methode wird das Attribut sShell instanziiert, der Titel in der
Titelzeile des Fensters gesetzt und die Größe des Fensters festgelegt. Weiterhin werden die
bis zu diesem Zeitpunkt erforderlichen Klassen importiert.

import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Shell;

public class Fenster {


private Shell sShell = null;

private void createSShell() {


sShell = new Shell();
sShell.setText("Shell");
sShell.setSize(new Point(300, 200));
}
}

Hat der Benutzer darüber hinaus im VE New Class Wizard unter WHICH METHOD STUBS
WOULD YOU LIKE TO CREATE? den Eintrag PUBLIC STATIC VOID MAIN(STRING[] ARGS) aktiviert,
wird der Quelltext noch um die main-Methode mit folgendem Quelltext erweitert:

public static void main(String[] args) {


Display display = Display.getDefault();
SWTFenster thisClass = new SWTFenster();
thisClass.createSShell();
thisClass.sShell.open();
while (!thisClass.sShell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
display.dispose();
}

Fügen wir jetzt der Shell ein Composite hinzu, wird der Quelltext um die Methode
createComposite erweitert. Diese enthält zunächst nur die Anweisung zur Instanzi-
ierung und Zuordnung des Composite-Objekts und die Anweisung für die Festlegung der
Position und Größe des Composite. Werden anschließend in der Properties View Änderun-
gen an den Eigenschaften des Composite vorgenommen, wie beispielsweise als Layout
GridLayout gewählt und die Spaltenanzahl auf 2 gesetzt, wird der Quelltext hierfür eben-
falls in der createComposite-Methode implementiert.

237
eclipse_v01.book Seite 238 Montag, 30. Januar 2006 12:02 12

Besonderheiten bei der Erstellung eines GUI mit SWT-Komponenten

private void createComposite() {


gridLayout=new GridLayout();
gridLayout.numColumns = 2;
composite = new Composite(sShell, SWT.NONE);
composite.setLayout(gridLayout);
composite.setBounds(
new org.eclipse.swt.graphics.Rectangle(15,9,262,142));
}

Der Aufruf der createComposite-Methode erfolgt in der Methode createSShell.


Das Hinzufügen einer Komponente zum Composite in der Design View oder der Java
Beans View bewirkt, dass die Komponente als Attribut der Klasse mit dem Zugriffsrecht
private deklariert und in der Methode createComposite instanziiert und dem Compo-
site hinzugefügt wird. Änderungen von Eigenschaften eines GUI-Elements in der Proper-
ties View werden als Anweisungen ebenfalls in der createComposite-Methode reali-
siert.

private Button button = null;


// …
private void createComposite() {
// …
button = new Button(composite, SWT.NONE);
button.setText("OK");

Wird einem GUI-Element ein Ereignis im Visual-Editor hinzugefügt, werden analog zu Er-
eignissen bei AWT- und Swing-Komponenten der für das Ereignis zuständige Listener und
die zuständige Methode im Quelltext implementiert und der Komponente hinzugefügt. Die
Implementation erfolgt als anonyme Klasse und die Methode enthält eine println-
Anweisung und einen TODO-Kommentar. Die entsprechenden Anweisungen finden sich
in der createComposite-Methode. Entscheiden wir uns bezüglich des Listener für die
Realisierung des Interface und nicht der Adapterklasse, werden auch hier die Methoden für
die weiteren Ereignisse des Ereignistyps mit leerem Methodenrumpf implementiert.

button.addSelectionListener(new
org.eclipse.swt.events.SelectionAdapter() {
public void widgetSelected(
org.eclipse.swt.events.SelectionEvent e) {
System.out.println("widgetSelected()");
// TODO Auto-generated Event stub widgetSelected()
}
});

238
eclipse_v01.book Seite 239 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

6.5.4 Beispiel
Da wir bereits für ein GUI mit Swing-Komponenten ein umfangreiches Beispiel betrachtet
haben, soll im Folgenden nur ein kleines Beispiel für ein GUI mit SWT-Komponenten vor-
gestellt werden.

Beschreibung des Beispiels


Es soll ein SWT-Fenster mit zwei Textfeldern und einem Label unter Verwendung des
GridLayouts erstellt werden. Hierbei soll sich das Label horizontal über zwei Zellen erstre-
cken und diese ausfüllen. Weiterhin sollen folgende Ereignisse implementiert werden:
쐌 Als Erstes soll beim Anzeigen des Fensters, also beim shellActivated-Ereignis, der
Text „Hello“ in das erste und „World“ in das zweite Textfeld eingetragen werden.
쐌 Beim focusGained-Ereignis der Textfelder soll der Inhalt des entsprechenden Text-
felds markiert werden und beim focusLost-Ereignis der Text aus dem ersten mit dem
aus dem zweiten Textfeld zusammengesetzt und in das Label ausgegeben werden.
쐌 Beim mouseDown-Ereignis des Labels soll die Schriftgröße um jeweils zwei Punkt ver-
größert werden.

Implementation des Beispiels


Wir erstellen zunächst ein neues Projekt mit dem Namen com.entwickler.eclipsebuch.VE-
BeispielSWT. Da die Quelltext- und die class-Dateien jeweils in einem eigenen Verzeichnis
gespeichert werden sollen, aktivieren wir, nachdem wir im New Java Project Wizard den
Namen für das Projekt eingegeben haben, unter PROJECT LAYOUT den Eintrag CREATE SEPA-
RATE SOURCE AND OUTPUT FOLDERS. Anschließend klicken wir rechts daneben auf CONFIGURE
DEFAULT und aktivieren im daraufhin erscheinenden Fenster unter SOURCE AND OUTPUT FOL-
DER den Eintrag FOLDERS. Die dort vorgeschlagenen Verzeichnisnamen src und bin überneh-
men wir.
Nach dem Beenden des New Java Project Wizard klicken wir als Nächstes im Package Ex-
plorer auf das Quelltextverzeichnis und wählen FILE | NEW | OTHER | JAVA | VISUAL CLASS.
Nach dem Klicken auf NEXT geben wir im Fenster des VE New Class Wizard der Klasse
den Namen SWTFenster, wählen unter STYLE | SWT den Eintrag SHELL und aktivieren unter
WHICH METHOD STUBS WOULD YOU LIKE TO CREATE? den Eintrag PUBLIC STATIC VOID
MAIN(STRING[] ARGS). Es wird daraufhin der in Abschnitt 6.5.3 beschriebene Quelltext er-
zeugt. Damit das als Nächstes der Shell hinzuzufügende Composite diese komplett ausfüllt,
wählen wir als Layout für die Shell in der Properties View unter der Eigenschaft layout
das FillLayout.
Zum Hinzufügen des Composite klicken wir in der Palette in der Kategorie SWT Contai-
ners auf den Eintrag COMPOSITE und anschließend einfach in der Design View in die Shell.
Für das Composite stellen wir anschließend das GridLayout ein und geben, nachdem wir
auf das +-Zeichen vor layout geklickt haben, unter numcolumns als Spaltenanzahl 2 ein.
Die beiden Textfelder und das Label werden nun dem Composite hinzugefügt, indem in der
Palette unter der Kategorie SWT Controls der Eintrag TEXT bzw. LABEL gewählt und an-
schließend in das Composite geklickt wird. Wenn unter WINDOW | PREFERENCES | JAVA |

239
eclipse_v01.book Seite 240 Montag, 30. Januar 2006 12:02 12

Besonderheiten bei der Erstellung eines GUI mit SWT-Komponenten

VISUAL EDITOR | APPEARANCE die Option PROMPT FOR BEAN NAME DURING CREATION aktiviert ist,
erscheint nach dem Klick in das Composite jeweils das Fenster zur Namensgebung und wir
benennen dort das erste Textfeld mit txtEingabe1, das zweite mit txtEingabe2 und das
Label mit lblAusgabe. Falls das Fenster zur Namensgebung nicht erscheint, verändern
wir den Namen in der Properties View unter der Eigenschaft field name. Anschließend
löschen wir noch den standardmäßig eingetragenen Text aus dem Label, und zwar entweder
direkt in der Design View oder in der Properties View unter text.
Da wir den Layoutmanager GridLayout eingestellt haben, können wir für jede SWT-Kom-
ponente weitere Angaben machen. In unserem Fall ist dies für das Label erforderlich. Wir
wählen dieses in der Design View oder der Java Beans View an und geben entweder im
Customize Layout-Fenster oder in der Properties View unter layoutdata an, dass das
Label sich horizontal über zwei Zellen erstrecken (horizontalSpan=2) und diese ausfül-
len (horizontalAlignment=FILL) soll.
Im Quelltext wird in der Methode createComposite ein GridData-Objekt erzeugt, bei
dem die gewählten Einstellungen realisiert werden, bevor es in der Methode setLayout-
Data dem Label-Objekt übergeben wird:

GridData gridData = new org.eclipse.swt.layout.GridData();


gridData.horizontalSpan = 2;
gridData.horizontalAlignment =
org.eclipse.swt.layout.GridData.FILL;
lblAusgabe.setLayoutData(gridData);

Um unserer Beispielanwendung Leben einzuhauchen, implementieren wir jetzt noch die


oben beschriebenen Ereignisse, und zwar als Erstes das shellActivated-Ereignis.
Dazu klicken wir in der Java Beans View auf die Shell und wählen im Kontextmenü den Be-
fehl EVENTS | ADD EVENTS. Im daraufhin erscheinenden Dialogfenster klicken wir auf den
Eintrag SHELLACTIVATED. Dadurch wird in der Methode createSShell der Listener mit-
hilfe der Methode addShellListener der Shell hinzugefügt. Die Ereignismethode
shellActivated wird in eine anonyme Klasse geschrieben. In dieser implementieren
wir, dass im ersten Textfeld der Text „Hello“ und im zweiten der Text „World“ erscheinen
soll.

sShell.addShellListener(new
org.eclipse.swt.events.ShellAdapter(){
public void shellActivated(org.eclipse.swt.events.ShellEvent e){
txtEingabe1.setText("Hello");
txtEingabe2.setText("World");
}
});

Als Nächstes programmieren wir, dass der Inhalt der Textfelder beim focusGained-Er-
eignis markiert wird und beim focusLost-Ereignis der Text aus dem ersten mit dem aus
dem zweiten Textfeld zusammengesetzt und in das Label ausgegeben wird. Da für beide

240
eclipse_v01.book Seite 241 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

Ereignisse des Focus Events eine Reaktion erfolgen soll, verwenden wir in diesem Fall
nicht die Adapterklasse, sondern wählen im Dialogfenster zur Auswahl der Ereignisse ei-
nes der beiden Ereignisse focusGained oder focusLost und geben anschließend an,
dass der FocusListener implementiert werden soll. Für das Markieren des Inhalts der Text-
felder nutzen wir die Methode selectAll aus der Klasse Text. Für das focusLost-Er-
eignis programmieren wir die Methode lblAusgabeTextSetzen in der äußeren Klasse
(SWTFenster). Auf diese Weise können wir dieselbe Funktionalität für mehrere Ereignisse
verwenden und müssen sie nicht mehrfach implementieren.

txtEingabe1.addFocusListener(
new org.eclipse.swt.events.FocusListener(){
public void focusGained(org.eclipse.swt.events.FocusEvent e) {
txtEingabe1.selectAll();
}
public void focusLost(org.eclipse.swt.events.FocusEvent e) {
lblAusgabeTextSetzen();
}
});
txtEingabe2.addFocusListener(
new org.eclipse.swt.events.FocusListener(){
public void focusGained(org.eclipse.swt.events.FocusEvent e) {
txtEingabe2.selectAll();
}
public void focusLost(org.eclipse.swt.events.FocusEvent e) {
lblAusgabeTextSetzen();
}
});

Die Methode lblAusgabeTextSetzen enthält lediglich eine Befehlszeile:

private void lblAusgabeTextSetzen() {


lblAusgabe.setText(txtEingabe1.getText()+" "+
txtEingabe2.getText());
}

Als letztes Ereignis implementieren wir noch, dass sich bei Klick auf das Label
lblAusgabe die Schriftgröße um zwei Punkt vergrößert. Hierbei handelt es sich um ein
mouseDown-Ereignis, das wir im Dialogfenster zur Auswahl der Ereignisse unter MOUSE
auswählen. Da noch zwei Ereignisse für den Mouse Event existieren, auf die jedoch nicht
reagiert werden soll, implementieren wir hier wieder die entsprechende Adapterklasse. In
ihr können wir bei SWT die Schriftgröße eines Label-Objekts nicht direkt verändern, son-
dern müssen uns eine Referenz auf das Font-Objekt des Labels erzeugen. Die zugrunde
liegende Font-Klasse stammt aus dem Package org.eclipse.swt.graphics. Aller-
dings kann die Schriftgröße auch nicht im Font-Objekt direkt angepasst werden, sondern
es muss eine Referenz auf das zugehörige FontData-Objekt-Array erzeugt werden. Das
erste Element des Arrays enthält die Daten zum Font des Labels. Mit der setHeight-Me-

241
eclipse_v01.book Seite 242 Montag, 30. Januar 2006 12:02 12

Der WindowBuilder

thode setzen wir die Schriftgröße auf den um zwei vergrößerten Wert. Danach instanziieren
wir ein neues Font-Objekt mithilfe des veränderten FontData-Objekt-Arrays und ordnen
es dem Label zu.

lblAusgabe.addMouseListener(
new org.eclipse.swt.events.MouseAdapter() {
public void mouseDown(org.eclipse.swt.events.MouseEvent e) {
org.eclipse.swt.graphics.FontData[] fd =
lblAusgabe.getFont().getFontData();
fd[0].setHeight(fd[0].getHeight()+2);
org.eclipse.swt.graphics.Font font =
org.eclipse.swt.graphics.Font(composite.getDisplay(),fd);
lblAusgabe.setFont(font);
}
});

Damit ist die Programmierung unserer Beispielanwendung abgeschlossen.

6.6 Der WindowBuilder


Der WindowBuilder ermöglicht das visuelle Entwerfen von GUIs, die Swing- und SWT-
Komponenten beinhalten. Er wurde von der Firma Instantiations entwickelt und besteht aus
den beiden Komponenten SWT Designer und Swing Designer. Eine Download-Möglich-
keit besteht unter www.windowbuilderpro.com, wobei eine Free Edition und eine Profes-
sional Edition existieren. Eine Dokumentation kann in jedem Fall kostenlos herunterge-
laden werden. Die Free Edition besitzt einen eingeschränkten Funktionsumfang. Eine
Übersicht der Unterschiede zwischen Free und Professional Edition findet sich ebenfalls
unter www.windowbuilderpro.com.
Die Grundlage für die folgende Beschreibung ist die Version 4.2.0 Professional Edition, die
14 Tage kostenlos getestet werden kann. Obwohl ab der Version 2.0.0 auch die Entwick-
lung von GUIs mit Swing-Komponenten (Swing Designer) möglich ist, beschränken wir
uns auf die Verwendung von SWT-Komponenten (SWT Designer), da mit dem Visual Edi-
tor bereits ein Designer für die Entwicklung von GUIs mit Swing-Komponenten vorgestellt
worden ist.

6.6.1 Installation und Aufruf


Zum Installieren der hier betrachteten Version des WindowBuilder muss zunächst die Datei
Designer_v4.2.0_for_Eclipse3.1.zip in das Eclipse-Verzeichnis entpackt werden. Es ist
darauf zu achten, dass die in der zip-Datei gespeicherten Pfadangaben verwendet werden.
War die Installation erfolgreich, so erscheint nach dem Starten von Eclipse unter HELP |
ABOUT ECLIPSE SDK | PLUG-IN DETAILS das WindowBuilder-Plugin. Dieses ist leicht am Fir-
mennamen Instantiations in der Spalte PROVIDER zu erkennen. Soll auch die Hilfe zur Ver-
fügung stehen, muss des Weiteren noch die Datei Designer_v4.2.0_docs.zip ebenfalls in
das Eclipse-Verzeichnis entpackt werden. Sie wird – wie auch beim Visual Editor – in die

242
eclipse_v01.book Seite 243 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

Eclipse-Hilfe integriert. Bevor wir jetzt den WindowBuilder nutzen können, müssen wir
ihn noch aktivieren. Hierzu starten wir unter WINDOW | PREFERENCES | DESIGNER | LICENSE
durch Klick auf den Button REGISTRATION AND ACTIVATION den Online Activation Wizard
und führen die daraufhin erscheinenden Anweisungen aus.
Der WindowBuilder verfügt über einen eigenen Projekt-Wizard. Analog zum Projekt-
Wizard von Eclipse müssen wir zunächst FILE | NEW | PROJECT aufrufen. In dem daraufhin
erscheinenden Fenster klicken wir auf das +-Zeichen vor dem Eintrag DESIGNER, so dass die
Option SWT/JFACE JAVA PROJECT angezeigt wird, die wir dann anwählen. Nach dem Kli-
cken auf NEXT können wir nun im nächsten Fenster für das neue Projekt den Projektnamen
eingeben.
Um eine Klasse mit dem WindowBuilder zu erstellen, wählen wir FILE | NEW | OTHER. In
dem dann erscheinenden Fenster klicken wir unter DESIGNER auf das +-Zeichen vor dem
Eintrag SWT und wählen die Option APPLICATION WINDOW. Im nächsten Fenster geben wir
das Quelltextverzeichnis, das Package und den neuen Klassennamen an. Haben wir zuvor
im Package Explorer das gewünschte Package angeklickt, ist es im Fenster bereits einge-
stellt.

Abb. 6.25: Erstellen einer neuen SWT-Applikation im WindowBuilder

Sind die erforderlichen Informationen eingegeben und FINISH ist angeklickt worden, wird
statt des Java Editor-Fensters von Eclipse das Designer-Fenster angezeigt. Wenn wir hier
dann das Register SOURCE wählen, erscheint der Quelltext der SWT-Applikation. Bei der

243
eclipse_v01.book Seite 244 Montag, 30. Januar 2006 12:02 12

Der WindowBuilder

Wahl des Registers DESIGN erscheint das in Abb. 6.26 dargestellte Fenster. Dieses weist
mehrere Bestandteile auf:
쐌 eine Root List, die für ein GUI die Container der obersten Ebene in einer Liste anzeigt,
쐌 ein Control Tree, der alle Komponenten eines GUI in einer baumartigen Struktur dar-
stellt,
쐌 eine Control-Palette, die der Auswahl der Komponenten dient,
쐌 die so genannte Content Pane, die das GUI so zeigt, wie es nach dem Start der Anwen-
dung erscheinen wird, und
쐌 ein Inspector, der die Eigenschaften bzw. Ereignisse einer angewählten Komponente
zeigt.

Abb. 6.26: Eclipse mit dem Designer-Fenster des WindowBuilder

Unter WINDOW | PREFERENCES | DESIGNER | EDITOR LAYOUT lassen sich alternative Anordnun-
gen der einzelnen Bestandteile festlegen.

6.6.2 Hinzufügen von Komponenten


Die mithilfe des WindowBuilder einfügbaren SWT-Komponenten sind in der Control-
Palette in drei Kategorien zusammengefasst:
쐌 SWT Composites
Composite, Group, SashForm, TabFolder, TabItem etc.

244
eclipse_v01.book Seite 245 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

쐌 SWT Controls
Button, Label, Text, Combo, List, Tree etc.
쐌 Menu Controls
MenuBar, PopupMenu, SubMenu, MenuItem etc.
Um eine SWT-Komponente einem Composite hinzuzufügen, wählen wir die gewünschte
SWT-Komponente zunächst in der Control-Palette an und klicken anschließend an die ge-
wünschte Stelle in der Content Pane oder auch im Control Tree. Je nachdem, welchen Lay-
outmanager wir verwenden, werden wir in unterschiedlicher Weise unterstützt. In Ab-
schnitt 6.6.5 erfolgt hierzu eine detaillierte Beschreibung.
Haben wir eine Komponente an der falschen Stelle eingefügt, können wir sie in der Content
Pane oder im Control Tree verschieben.

6.6.3 Entfernen von Komponenten


Eine Komponente lässt sich entfernen, indem sie im Control Tree oder der Content Pane an-
geklickt wird und anschließend mit der ¢-Taste oder dem DELETE-Befehl aus dem Kon-
textmenü gelöscht wird.

6.6.4 Ändern von Eigenschaften einer Komponente


Um die Eigenschaften einer Komponente zu ändern, müssen wir sie in der Content Pane
oder dem Control Tree anklicken. Im Inspector werden daraufhin die Eigenschaften ange-
zeigt. Wenn wir im Inspector entweder in der linken Spalte den Namen einer Eigenschaft
anwählen oder in der rechten Spalte den Wert einer Eigenschaft, können wir den Wert än-
dern. Um die Änderung zu bestätigen, muss bei vielen Eigenschaften anschließend Æ ge-
drückt werden.
Bei einigen Eigenschaften handelt es sich um komplexe Eigenschaften. Diese sind erkenn-
bar an dem Zeichen vor dem Namen der Eigenschaft in der linken Spalte. Erscheint ein
+-Zeichen, muss auf dieses geklickt werden, um weitere Eigenschaften zu der gewählten
Eigenschaft anzuzeigen. Bei einem Minuszeichen werden die weiteren Eigenschaften be-
reits angezeigt.

6.6.5 Layouteinstellungen
Der WindowBuilder unterstützt die vorgestellten Layoutmanager FillLayout, FormLayout,
GridLayout, RowLayout und StackLayout. Sofern unter WINDOW | PREFERENCES | DESIGNER |
SWT die Option ALLOW ABSOLUTE/NULL LAYOUT (SETBOUNDS()) aktiviert ist, kann alternativ
auch der Layoutmanager auf null gesetzt werden, so dass wir für jede Komponente ihre
Position und Größe explizit angeben müssen.
Haben wir zunächst einen Layoutmanager benutzt und setzen ihn dann auf null, werden –
wie auch beim Visual Editor – für die bisher eingefügten Komponenten als Einstellungen
für die Größe und die Position die bisher durch den Layoutmanager definierten Werte rea-
lisiert.

245
eclipse_v01.book Seite 246 Montag, 30. Januar 2006 12:02 12

Der WindowBuilder

Die Wahl des Layouts ist entweder im Inspector unter LAYOUT möglich oder aber indem in
der Palette das gewünschte Layout in der Kategorie SWT Layout angewählt wird und an-
schließend im Control Tree oder der Content Pane der Container, der mit dem Layout ver-
sehen werden soll, angeklickt wird. Nachdem unter LAYOUT der gewünschte Layout-
manager gewählt worden ist, können anschließend für alle Layoutmanager die allgemeinen
Layouteinstellungen ebenfalls über den Inspector vorgenommen werden. Hierzu muss le-
diglich der Eintrag LAYOUT durch Klicken auf das +-Zeichen erweitert werden. Alternativ
lässt sich beim FillLayout die Anordnung – horizontal oder vertikal – über das Ausrich-
tungssymbol , das sich in der Content Pane rechts oben am angewählten Container be-
findet, einstellen.
Die bei Wahl des FormLayout, GridLayout und RowLayout für jede Komponente mögli-
chen individuellen Einstellungen können ebenfalls über den Inspector vorgenommen wer-
den. Hierzu wird die gewünschte Komponente in der Content Pane oder im Control Tree
angewählt und anschließend können wir die Einstellungen unter LAYOUTDATA im Inspector
realisieren.
Beim FormLayout wird zusätzlich zu den Markern an jeder Seite einer angewählten Kom-
ponente in der Content Pane ein Symbol in der folgenden Form gezeigt, über das alter-
nativ eine Reihe von individuellen Einstellungen vorgenommen werden können. Beim
GridLayout besitzt eine angeklickte Komponente drei Arten von Kennzeichnungen. Mithil-
fe der beiden Symbole am oberen rechten Rand kann die Ausrichtung der Kompo-
nente in der Zelle festgelegt werden. Die schwarzen Marker ermöglichen das Festlegen der
Größe und über die grünen Marker lässt sich einstellen, über wie viele Zeilen bzw. Spalten
sich die Komponente erstrecken soll. Darüber hinaus findet sich im Kontextmenü noch ein
Befehl, mit dem wir angeben können, ob sich die Komponente horizontal bzw. vertikal der
Zellgröße anpassen soll. Beim RowLayout können wir durch Ziehen an den Markern die
Größe verändern.
Als dritte Alternative lassen sich beim GridLayout alle individuellen Einstellungen und
beim FormLayout ein Teil der individuellen Einstellungen über einen Layoutassistenten
vornehmen. Dieser wird, nachdem die gewünschte Komponente angewählt worden ist, über
das Symbol in der Symbolleiste des WindowBuilder gestartet.

Abb. 6.27: Layoutassistent für GridLayout und FormLayout

Wie bereits in Abschnitt 6.6.2 erwähnt, stellt der WindowBuilder auch eine Reihe von Hil-
fen beim Hinzufügen einer Komponente zu einem Container zur Verfügung. Verwenden

246
eclipse_v01.book Seite 247 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

wir keinen Layoutmanager, erscheint, nachdem wir eine Komponente in der Palette ange-
wählt haben und anschließend den Mauscursor zum Hinzufügen in die Content Pane be-
wegt haben, am Mauscursor eine gelbe Infobox, die die aktuelle Position in Pixel angibt.
Wenn wir, statt sofort zu klicken, bei gedrückt gehaltener linker Maustaste einen Rahmen
in der für die Komponente gewünschten Größe aufspannen, können wir hierbei auch gleich
die gewünschte Größe der Komponente einstellen. In diesem Fall erscheint am Mauscursor
eine weitere gelbe Infobox, die die aktuelle Größe angibt. Weiterhin kann die Größe und
Position einer bereits eingefügten Komponente nachträglich mithilfe der Maus direkt in der
Content Pane geändert werden. Hierbei erscheinen sowohl beim Verschieben als auch bei
der Größenänderung wiederum die gelben Infoboxen. Haben wir mehrere Komponenten
markiert, können wir diese mithilfe der Ausrichtungssymbole in der Symbolleiste des De-
signers ausrichten.
Beim FillLayout und beim RowLayout erscheint an den Stellen, an denen eine Komponente
hinzugefügt werden kann, in der Content Pane jeweils eine rote und im Control Tree eine
waagerechte dicke schwarze Linie. Beim FormLayout erscheinen in der Nähe des Maus-
cursors zwei gelbe Infoboxen: eine, die die Entfernung in Pixel zum oberen Rand des ge-
wählten Containers angibt, und eine, die die Entfernung in Pixel zum linken Rand ausweist.
Beim GridLayout werden, wie beim Visual Editor auch, falls eine mögliche Einfügeposi-
tion angewählt worden ist, in der Content Pane die Zeile und Spalte am Mauscursor einge-
blendet. Beim StackLayout ist keine weitere Unterstützung erforderlich, da im gewählten
Container auch bei Vorhandensein mehrerer Komponenten immer nur eine Komponente
gleichzeitig angezeigt wird, die den Container vollständig ausfüllt.

6.6.6 Ereignisverarbeitung
Um für eine Komponente eine Ereignisverarbeitung zu implementieren, muss diese zu-
nächst in der Content Pane oder im Control Tree angeklickt werden. Anschließend müssen
wir gegebenenfalls im Inspector noch auf das +-Zeichen vor der Eigenschaft events kli-
cken, um alle möglichen Ereignistypen für die angewählte Komponente anzuzeigen. Wenn
wir dann auf das +-Zeichen eines Ereignistyps klicken, erscheinen die möglichen Ereignis-
se dieses Typs. Jetzt können wir entweder in der rechten Spalte doppelt beim gewünschten
Ereignis klicken oder in die linke Spalte klicken und Æ drücken. Der WindowBuilder
fügt den Quelltext für das Ereignis ein und wechselt dann zur Anzeige des Quelltextes. Im
Inspector erscheint in der rechten Spalte für das Ereignis die Angabe der Zeile in der Quell-
textdatei, in der der eingefügte Quelltext beginnt.
Für einen Button (Button, Check Button, Radio Button) und einen Menüeintrag ist ein
schnellerer Weg möglich. Hier reicht für die Implementation eines Ereignisses ein Dop-
pelklick auf die Komponente.
Um ein bereits implementiertes Ereignis für eine Komponente zu löschen, müssen wir die
Komponente zunächst anwählen, dann im Inspector unter der Eigenschaft events das zu
entfernende Ereignis sichtbar machen und anwählen und anschließend mit der ¢-Taste
löschen. Dadurch verschwindet die Angabe, in welcher Zeile der Quelltextdatei sich der zu-
gehörige Quelltext befindet, und dieser wird ebenfalls gelöscht.

247
eclipse_v01.book Seite 248 Montag, 30. Januar 2006 12:02 12

Der WindowBuilder

Abb. 6.28: Die Anzeige eines Ereignisses im Inspector

Über WINDOW | PREFERENCES | DESIGNER | CODE GENERATION | EVENT HANDLERS können wir
steuern, in welcher Form die Ereignisverarbeitung implementiert werden soll: anonyme
Klasse, innere Klasse oder Implementation des für das Ereignis zuständigen Interface in der
Klasse für das GUI.

6.6.7 Codegenerierung
Wird mithilfe des WindowBuilder eine SWT-Applikation erstellt, wird automatisch ein
Quelltext generiert. Unter WINDOW | PREFERENCES | DESIGNER | CODE GENERATION lassen sich,
wie der Abb. 6.29 zu entnehmen ist, eine Reihe von Einstellungen vornehmen, die die
Codegenerierung beeinflussen.

Abb. 6.29: Einstellungsmöglichkeiten für Codegenerierung

248
eclipse_v01.book Seite 249 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

Der Quelltext enthält in jedem Fall die import-Anweisungen für die verwendeten Klassen
Display und Shell, die Deklaration der neuen Klasse, eine main-Methode und eine
open-Methode, die in der main-Methode aufgerufen wird. Ist bei der Erstellung der SWT-
Applikation die Option PROTECTED CREATECONTENTS() METHOD aktiviert worden, verfügt der
Quelltext weiterhin noch über eine Methode createContents, in der in diesem Fall das
Shell-Objekt erzeugt wird. Im Folgenden wird diese Implementationsvariante beschrie-
ben. Hierbei wird in der main-Methode ein Objekt der implementierten Klasse erzeugt und
die open-Methode aufgerufen. In der open-Methode erhalten wir dann das Display-Ob-
jekt, die Methode createContents wird aufgerufen, die Shell geöffnet und die Schleife
für die Ereignisverarbeitung implementiert. Die dispose-Anweisung für das Entfernen
des Display-Objekts fehlt hier.

import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class Fenster {


protected Shell shell;
public static void main(String[] args) {
try {
Dummy window = new Dummy();
window.open();
} catch (Exception e) {
e.printStackTrace();
}
}
public void open() {
final Display display = Display.getDefault();
createContents();
shell.open();
shell.layout();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
}
protected void createContents() {
shell = new Shell();
shell.setSize(500, 375);
shell.setText("SWT Application");
}
}

Werden dem GUI Komponenten hinzugefügt, wird der größte Teil der Komponenten in der
main-, open- oder createContents-Methode deklariert und instanziiert. Für einige
Komponenten, wie z.B. Textfelder, Comboboxen und Listen wird allerdings auch ein Attri-
but der Klasse mit dem Zugriffsrecht private deklariert und in der main-, open- bzw.
createContents-Methode erfolgt dann die Instanziierung des Objekts.

249
eclipse_v01.book Seite 250 Montag, 30. Januar 2006 12:02 12

Der WindowBuilder

public class Fenster {


private Text text;
// ...
{
final Button button = new Button(shell, SWT.NONE);
button.setText("button");
}
{
text = new Text(shell, SWT.BORDER);
}

Ist die Blockbildung aktiviert worden (siehe WINDOW | PREFERENCES | DESIGNER | CODE GENE-
RATION), gibt sie die Hierarchie der Komponenten wieder. Im folgenden Beispiel wird zu-
nächst der Shell ein Button und ein Composite hinzugefügt. Anschließend wird dem Com-
posite noch ein Button hinzugefügt. Der Block hierfür befindet sich nicht auf der gleichen
Ebene wie die Blöcke für den ersten Button und das Composite, sondern innerhalb des
Blocks des Composite.

{
final Button button = new Button(shell, SWT.NONE);
button.setText("button");
}
{
final Composite composite = new Composite(shell, SWT.NONE);
composite.setLayout(new FillLayout());
{
final Button button = new Button(composite, SWT.NONE);
button.setText("button");
}
}

Wird einer Komponente ein Ereignis über den Inspector hinzugefügt, erfolgt je nach gewähl-
ter Option unter WINDOW | PREFERENCES | DESIGNER | CODE GENERATION | EVENT HANDLERS |
EVENT CODE GENERATION eine unterschiedliche Implementation.
Bei Wahl von CREATE ANONYMOUS CLASS wird eine anonyme Klasse implementiert, die das
für den gewählten Ereignistyp zuständige Listener-Interface implementiert bzw., falls eine
Adapterklasse existiert, diese erweitert. In beiden Fällen weist die für das gewählte Ereignis
zuständige Methode einen leeren Methodenrumpf auf.

protected void createContents() {


shell = new Shell();
shell.addMouseMoveListener(new MouseMoveListener() {
public void mouseMove(MouseEvent e) {}
});
// ...

250
eclipse_v01.book Seite 251 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

final Button button = new Button(composite, SWT.NONE);


button.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {}
});

Wird die Option CREATE INNER CLASS aktiviert, erfolgt die Implementation statt als anonyme
Klasse als innere Klasse.

public class Fenster


// ...
private class ShellMouseMoveListener implements
MouseMoveListener {
public void mouseMove(MouseEvent e) {}
}
private class ButtonSelectionListener extends SelectionAdapter {
public void widgetSelected(SelectionEvent e) {}
}

Wird die dritte Möglichkeit IMPLEMENT LISTENER INTERFACE IN PARENT CLASS aktiviert, wird in
jedem Fall, also auch bei Vorhandensein einer Adapterklasse, das Interface implementiert,
und zwar direkt in der Klasse für das GUI.

public class Fenster implements


MouseMoveListener, SelectionListener {
// ...
public void mouseMove(MouseEvent e) {}
public void widgetSelected(SelectionEvent e) {}
public void widgetDefaultSelected(SelectionEvent e) {}

6.6.8 Beispiel
Im Folgenden soll anhand eines Beispiels der Einsatz des WindowBuilders veranschaulicht
werden. Das Beispiel ähnelt dem Swing-Beispiel, verwendet jedoch der Einfachheit halber
keine Menüleiste und umfasst nur eine Klasse für die Implementation des GUI.

Beschreibung des Beispiels


Bei dem Beispiel handelt es sich um ein SWT-Fenster, welches in vier Bereiche aufgeteilt
ist. Der obere linke Bereich dient zur Dateneingabe. Im unteren linken Bereich werden die
bisher eingegebenen Daten in einer tabellarischen Übersicht ausgegeben. Der obere rechte
Bereich ermöglicht es, die Fenstergröße und die Hintergrundfarbe der beiden oberen Berei-
che zu verändern. Der untere rechte Bereich weist zwei Register auf. In einem ersten Re-
gister kann über einen Schieberegler die Schriftgröße verändert werden. Das zweite Regis-
ter ermöglicht es, die Tabelle mit oder ohne Gitternetzlinien darzustellen.

251
eclipse_v01.book Seite 252 Montag, 30. Januar 2006 12:02 12

Der WindowBuilder

Abb. 6.30: Das Beispielfenster

Einrichten des Projekts


Wir erstellen zunächst ein neues Projekt mit dem Namen com.entwickler.eclipse-
buch.SWTDesignerBeispiel. Hierzu rufen wir FILE | NEW | OTHER | DESIGNER | SWT/JFACE
JAVA PROJECT auf. Im nächsten Fenster können wir dann für das neue Projekt den Namen
eingeben. Da die Quelltext- und die class-Dateien jeweils in einem eigenen Verzeichnis ge-
speichert werden sollen, aktivieren wir, nachdem wir im New SWT/JFace Java Project
Wizard den Namen für das Projekt eingegeben haben, unter PROJECT LAYOUT den Eintrag
CREATE SEPARATE SOURCE AND OUTPUT FOLDERS. Anschließend klicken wir rechts daneben auf
CONFIGURE DEFAULT und aktivieren im daraufhin erscheinenden Fenster unter SOURCE AND
OUTPUT FOLDER den Eintrag FOLDERS. Die dort vorgeschlagenen Verzeichnisnamen src und
bin übernehmen wir. Nach dem Beenden des New SWT/JFace Java Project Wizard klicken
wir als Nächstes im Package Explorer auf das Quelltextverzeichnis und erstellen ein
Package gui und ein Package daten.

Die Klasse Daten


Bevor wir jetzt mit der Implementation der Klasse für das GUI beginnen, erstellen wir zu-
nächst eine Klasse, die einen Datensatz repräsentiert. Hierzu generieren wir im Package
daten eine neue Klasse mit dem Namen Daten. Als Attribute definieren wir kundenNr
und kundenName vom Typ String und erzeugen die zugehörigen Getter- und Setter-Metho-
den. Weiterhin implementieren wir den Standardkonstruktor mit leerem Methodenrumpf
und einen Konstruktor für die Initialisierung der beiden Attribute.

package daten;

public class Daten {


private String kundenNr, kundenName;

// Konstruktoren ...

// Getter- und Setter-Methoden ...


}

252
eclipse_v01.book Seite 253 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

Die Klasse Fenster


Bevor wir mit der Implementation der Klasse Fenster beginnen, deaktivieren wir unter
WINDOW | PREFERENCES | DESIGNER | CODE GENERATION den Eintrag CREATE EVERY COMPONENT
IN ITS OWN BLOCK.

Für die Implementation des GUI wählen wir im Package Explorer das Package gui an und
rufen FILE | NEW | OTHER | DESIGNER | SWT | APPLICATION WINDOW, um den New SWT Appli-
cation Wizard zu starten. Als Klassennamen geben wir dann im Wizard Fenster ein. Des
Weiteren lassen wir unter CREATE CONTENTS IN die Option PROTECTED CREATECONTENTS() ME-
THOD aktiviert.

Nach dem Beenden des Wizard erhalten wir im Register SOURCE den generierten Quelltext.
Wir wechseln jetzt zum Register DESIGN und nehmen, wie in Abb. 6.31 gezeigt, im Inspec-
tor Veränderungen bei den Eigenschaften layout und text vor.

Abb. 6.31: Einstellungen für das Beispielfenster im Inspector

Diese werden sofort im Quelltext berücksichtigt.

package gui;

import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class Fenster {


// ...
protected void createContents() {
shell = new Shell();
shell.setLayout(new FillLayout());
shell.setSize(600, 400);
shell.setText("SWT Designer Beispiel");
}
// ...

Wir fügen jetzt als Erstes der Shell ein SashForm hinzu, indem wir in der Control-Palette
unter SWT Composites den Eintrag SASHFORM anklicken und anschließend entweder im

253
eclipse_v01.book Seite 254 Montag, 30. Januar 2006 12:02 12

Der WindowBuilder

Control Tree auf die Shell oder in der Content Pane auf das Fenster klicken. Als Nächstes
werden dem SashForm ebenfalls durch Anwahl in der Control-Palette und Klicken auf das
SashForm im Control Tree oder in der Content Pane zwei Composites hinzugefügt. Für bei-
de Composites wird als Layout das FillLayout eingestellt. Weiterhin erhält das erste Com-
posite unter VARIABLE als Bezeichner compLi, das zweite Composite erhält den Bezeichner
compRe.
Um sowohl die linke als auch die rechte Hälfte des SashForm jeweils wiederum in zwei Be-
reiche aufzuteilen, fügen wir beiden Composites noch ein SashForm hinzu. Das linke Sash-
Form erhält im Inspector unter VARIABLE den Bezeichner sashFoLi und das rechte den Be-
zeichner sashFoRe. Des Weiteren stellen wir für beide SashForms im Inspector unter
STYLE ORIENTATION als Anordnung vertical ein, so dass wir im Gegensatz zum ersten
SashForm eine vertikale Aufteilung erhalten.
Um jetzt in den vier durch die beiden SashForms entstandenen Bereichen GUI-Elemente
platzieren zu können, versehen wir das SashForm sashFoLi mit zwei Composites und das
SashForm sashFoRe mit einer Group und einem Composite. Als Bezeichner vergeben wir
für das obere und das untere Composite der linken Seite compLo und compLu. Die Group
bzw. das Composite der rechten Seite erhalten die Bezeichner compRo und compRu.

protected void createContents() {


// ...
shell.setText("SWT Designer Beispiel");
final SashForm sashForm = new SashForm(shell, SWT.NONE);
final Composite compLi = new Composite(sashForm, SWT.NONE);
compLi.setLayout(new FillLayout());
final SashForm sashFoLi = new SashForm(compLi, SWT.VERTICAL);
final Composite compLo = new Composite(sashFoLi, SWT.NONE);
final Composite compLu = new Composite(sashFoLi, SWT.NONE);
sashFoLi.setWeights(new int[] { 1, 1 });
final Composite compRe = new Composite(sashForm, SWT.NONE);
compRe.setLayout(new FillLayout());
final SashForm sashFoRe = new SashForm(compRe, SWT.VERTICAL);
final Group compRo = new Group(sashFoRe, SWT.NONE);
final Composite compRu = new Composite(sashFoRe, SWT.NONE);
sashFoRe.setWeights(new int[] { 1, 1 });
sashForm.setWeights(new int[] { 1, 1 });
}// ...

Als Erstes wird nun der linke obere Bereich fertig gestellt. Als Layout wählen wir, nachdem
wir das Composite compLo im Control Tree angeklickt haben, im Inspector unter LAYOUT
das GridLayout. Unter NUMCOLUMNS geben wir 2 ein. Anschließend wählen wir in der Con-
trol-Palette in der Kategorie SWT Controls den Eintrag LABEL aus und klicken im Control
Tree auf das Composite compLo. Unter LAYOUTDATA stellen wir im Inspector für hGrap und
vGrap true ein, für hIdent 5 und als Beschriftung unter TEXT Kundennummer.

254
eclipse_v01.book Seite 255 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

final Label kundennummerLabel = new Label(compLo, SWT.NONE);


final GridData gridData = new GridData(GridData.BEGINNING,
GridData.CENTER, true, true);
gridData.horizontalIndent = 5;
kundennummerLabel.setLayoutData(gridData);
kundennummerLabel.setText("Kundennummer");

Als Nächstes fügen wir ein Textfeld ein. Hierzu wählen wir in der Control-Palette in der
Kategorie SWT Controls den Eintrag TEXT und klicken anschließend im Control Tree wie-
derum auf das Composite compLo. Die Einstellungen unter LAYOUTDATA werden danach
analog zum Label vorgenommen.

text = new Text(compLo, SWT.BORDER);


final GridData gridData_1 = new GridData(GridData.FILL,
GridData.CENTER, true, true);
gridData_1.horizontalIndent = 5;
text.setLayoutData(gridData_1);

Nachdem ein Label und ein Textfeld in dem Composite compLo platziert worden sind, wer-
den auf die soeben beschriebene Art ein weiteres Label und ein weiteres Textfeld eingefügt.
Dem Label geben wir die Beschriftung Kundenname.
Um nach der Eingabe eines Datensatzes diesen zu speichern, fügen wir jetzt noch dem
Composite einen Button hinzu. Für den Button nehmen wir unter LAYOUTDATA die gleichen
Einstellungen vor, wie für die Label und Textfelder. Als Beschriftung geben wir im Inspec-
tor unter TEXT Übernehmen ein. Damit ist die Implementation des linken oberen Bereichs
abgeschlossen.

final Button button = new Button(compLo, SWT.NONE);


final GridData gridData_4 = new GridData(GridData.BEGINNING,
GridData.CENTER, true, true);
gridData_4.horizontalIndent = 5;
button.setLayoutData(gridData_4);
button.setText("Übernehmen");

Für das Composite des linken unteren Bereichs wird als Layout FillLayout gewählt. Da
eine Tabelle und ein Button untereinander angeordnet sein sollen, verändern wir die Ein-
stellung layout style auf vertical. Dann fügen wir zunächst dem Composite compLu
eine Tabelle hinzu und anschließend der Tabelle zwei Spalten. Hierzu wählen wir in der
Control-Palette aus der Kategorie SWT Controls die Einträge Table und Table column.
Die erste Tabellenspalte erhält als Spaltenüberschrift unter TEXT Kundennummer, die zwei-
te Kundenname.

255
eclipse_v01.book Seite 256 Montag, 30. Januar 2006 12:02 12

Der WindowBuilder

table = new Table(compLu, SWT.BORDER);


table.setLinesVisible(true);
table.setHeaderVisible(true);

final TableColumn newColumnTableColumn = new TableColumn(table,


SWT.NONE);
newColumnTableColumn.setWidth(100);
newColumnTableColumn.setText("Kundennummer");

final TableColumn newColumnTableColumn_1 = new TableColumn(table,


SWT.NONE);
newColumnTableColumn_1.setWidth(100);
newColumnTableColumn_1.setText("Kundenname");

Als Letztes fügen wir im linken unteren Bereich noch einen Button ein. Dieser erhält als
Beschriftung Aktualisieren.
Die Bearbeitung des rechten oberen Bereichs beginnen wir mit den Einstellungen zur
Group. Im Inspector wählen wir als Layout GridLayout, unter NUMCOLUMNS 2 und unter
TEXT geben wir Allgemeine Einstellungen ein. Anschließend fügen wir der Group ein
Label hinzu, welches die Beschriftung Fenstergröße erhält. Als Nächstes wählen wir in
der Control-Palette aus der Kategorie SWT Controls den Eintrag COMBO und klicken im
Control Tree oder der Content Pane die gewünschte Position an. Um die Einträge für die
Combobox zu definieren, klicken wir im Inspector unter ITEMS auf das Symbol und ge-
ben im daraufhin erscheinenden Fenster die Einträge 600x400, 750x500, 900x600 unterei-
nander ein. Jetzt folgt noch ein Label mit der Beschriftung Hintergrundfarbe und als
Letztes fügen wir aus der Kategorie SWT Controls eine Liste ein. Die Einträge für die Liste
(grau, blau, weiß, rot, grün, gelb) werden wieder eingegeben, indem im Inspector unter
ITEMS auf das Symbol geklickt wird.

final Label label = new Label(compRo, SWT.NONE);


label.setText("Fenstergröße");
combo = new Combo(compRo, SWT.NONE);
combo.setItems(new String[] {"600x400", "750x500", "900x600"});
final Label hintergrundfarbeLabel = new Label(compRo, SWT.NONE);
hintergrundfarbeLabel.setText("Hintergrundfarbe");
list = new List(compRo, SWT.BORDER);
list.setItems(new String[] {"grau", "blau", "weiß",
"rot", "grün", "gelb"});

Zum Abschluss erfolgt die Implementation des rechten unteren Bereichs. Als Layout stel-
len wir hier für das Composite compRu das FillLayout ein und unter LAYOUT STYLE verti-
cal. Dem Composite wird zunächst ein TabFolder aus der Kategorie SWT Composites im
Inspector hinzugefügt. Anschließend werden dem TabFolder zwei TabItems ebenfalls aus
der Kategorie SWT Composites hinzufügt. Unter der Eigenschaft TEXT geben wir dann für
das erste TabItem Eingabe und für das zweite TabItem Tabelle ein.

256
eclipse_v01.book Seite 257 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

final TabFolder tabFolder = new TabFolder(compRu, SWT.NONE);


final TabItem eingabeTabItem = new TabItem(tabFolder, SWT.NONE);
eingabeTabItem.setText("Eingabe");
final TabItem tabelleTabItem = new TabItem(tabFolder, SWT.NONE);
tabelleTabItem.setText("Tabelle");

Um in den einzelnen Registern GUI-Elemente platzieren zu können, müssen wir beiden


TabItems noch jeweils ein Composite bzw. eine Group hinzufügen. Hierzu müssen wir zu-
nächst in der Control-Palette den Eintrag COMPOSITE anwählen. Anschließend wählen wir
dann entweder in der Content Pane oder in dem Inspector das gewünschte TabItem an. Im
Quelltext wird daraufhin ein Composite-Objekt erzeugt und mithilfe der Methode set-
Control dem angewählten TabItem zugewiesen.

final Composite composite = new Composite(tabFolder, SWT.NONE);


eingabeTabItem.setControl(composite);
final Group tabelleGroup = new Group(tabFolder, SWT.NONE);
tabelleTabItem.setControl(tabelleGroup);

Als Layout für das erste Composite wählen wir FillLayout und unter LAYOUT STYLE die Op-
tion vertical. Anschließend fügen wir dem Composite ein Label mit der Beschriftung
Schriftgröße und einen Schieberegler hinzu. Für den Schieberegler muss in der Control-
Palette in der Kategorie SWT Controls der Eintrag SCALE gewählt werden. Im Inspector ge-
ben wir für den Schieberegler unter MAXIMUM 16, unter MINIMUM 8, unter PAGEINCREMENT 2
und unter SELECTION 8 ein.

final Scale scale = new Scale(composite, SWT.NONE);


scale.setSelection(8);
scale.setPageIncrement(2);
scale.setMinimum(8);
scale.setMaximum(16);

Die Group erhält zunächst als Layout das GridLayout mit einer Spalte. Anschließend defi-
nieren wir als Beschriftung für die Group Tabelle und fügen der Group zwei Radio-
buttons hinzu. Die Radiobuttons finden sich in der Control-Palette in der Kategorie SWT
Controls. Unter der Eigenschaft TEXT erhalten die Radiobuttons ihre Beschriftung, und zwar
das erste mit Gitternetzlinien und das zweite ohne Gitternetzlinien. Da beim
Starten der Anwendung die Einstellung mit Gitternetzlinien realisiert sein soll, wird die Ei-
genschaft SELECTION für den ersten Radiobutton auf true gesetzt.

257
eclipse_v01.book Seite 258 Montag, 30. Januar 2006 12:02 12

Der WindowBuilder

tabelleGroup.setText("Tabelle");
final Button mitGitternetzlinienButton =
new Button(tabelleGroup, SWT.RADIO);
mitGitternetzlinienButton.setSelection(true);
mitGitternetzlinienButton.setText("mit Gitternetzlinien");
final Button ohneGitternetzlinienButton =
new Button(tabelleGroup, SWT.RADIO);
ohneGitternetzlinienButton.setText("ohne Gitternetzlinien");

Nachdem jetzt alle Komponenten dem Fenster hinzugefügt worden sind, implementieren
wir noch die Ereignisverarbeitung. Im linken oberen Bereich soll beim Klicken auf den
ÜBERNEHMEN-Button der eingegebene Datensatz in einer Liste gespeichert werden und die
Textfelder sollen geleert werden. Hierzu fügen wir ein Attribut daten, welches vom Da-
tentyp java.util.LinkedList ist, der Klasse Fenster hinzu. Damit sind die Daten
global innerhalb der Klasse und allen für die Ereignisverarbeitung benötigten anonymen
Klassen bekannt. Anschließend erzeugen wir in der Methode createContents ein Objekt
der Klasse LinkedList zum Speichern der Daten in einer Liste. Da in der Liste Objekte
vom Typ Daten gespeichert werden sollen, die Klasse Daten sich aber im Package daten
befindet, muss außerdem die Klasse Daten importiert werden.

import daten.Daten;

public class Fenster {


private java.util.LinkedList daten = null;
// ...
protected void createContents() {
daten = new java.util.LinkedList();

Danach wählen wir den ÜBERNEHMEN-Button im Control Tree oder in der Content Pane an
und wählen im Inspector unter EVENTS zunächst SELECTION. Anschließend klicken wir dop-
pelt in die Zelle rechts neben dem Eintrag WIDGETSELECTED. Wir gelangen dadurch automa-
tisch in die Quelltextansicht. Hier wurde bereits die Adapterklasse SelectionAdapter
mit einem leeren Methodenrumpf für die Methode widgetSelected implementiert. Wir
programmieren jetzt den Methodenrumpf wie folgt:

button.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
Daten datSatz = new Daten(text.getText(),text_1.getText());
daten.add(datSatz);
text.setText("");
text_1.setText("");
}});

258
eclipse_v01.book Seite 259 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

Auch für die Ereignisverarbeitung der weiteren GUI-Elemente müssen wir jeweils das Er-
eignis widgetSelected im Inspector auswählen und anschließend im Quelltext in der
Methode widgetSelected die erforderlichen Anweisungen eingeben. Im Folgenden sind
für die weiteren GUI-Elemente die gewünschten Reaktionen sowie die Implementationen
der widgetSelected-Methode dargestellt.
Wird im linken unteren Bereich auf den AKTUALISIEREN-Button geklickt, sollen die bisher
eingegebenen Daten in der Tabelle angezeigt werden.

aktualisierenButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
String[][] datFeld = new String[daten.size()][2];
for (int i=0; i<daten.size(); i++) {
Daten datSatz = new Daten();
datSatz = (Daten)daten.get(i);
datFeld[i][0] = datSatz.getKundenNr();
datFeld[i][1] = datSatz.getKundenName();
}
final TableItem[] tableItem = new TableItem[daten.size()];
for (int i=0; i<daten.size(); i++) {
tableItem[i] = new TableItem(table,0);
tableItem[i].setText(datFeld[i]);
}
daten = new java.util.LinkedList();
}});

Da nur für die seit dem letzten Klick auf den AKTUALISIEREN-Button eingegebenen Daten
Tabellenzeilen erzeugt werden müssen, wird jeweils die Liste daten durch die letzte An-
weisung geleert.
Die Auswahl einer Fenstergröße im rechten oberen Bereich soll die Größe entsprechend
verändern.

combo.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
String item = combo.getText();
if (item.equals("600x400")) shell.setSize(600,400);
else if (item.equals("750x500")) shell.setSize(750,500);
else if (item.equals("900x600")) shell.setSize(900,600);
}
});

Die Wahl einer Hintergrundfarbe sorgt dafür, dass der Hintergrund der beiden oberen Be-
reiche in der ausgewählten Farbe erscheint.

259
eclipse_v01.book Seite 260 Montag, 30. Januar 2006 12:02 12

Der WindowBuilder

list.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
String[] farbe = list.getSelection();
Display display = Display.getDefault();
if (farbe[0].equals("grau")) {
compLo.setBackground(
display.getSystemColor(SWT.COLOR_GRAY));
compRo.setBackground(
display.getSystemColor(SWT.COLOR_GRAY));
}
else if (farbe[0].equals("blau")) {
compLo.setBackground(
display.getSystemColor(SWT.COLOR_BLUE));
compRo.setBackground(
display.getSystemColor(SWT.COLOR_BLUE));
}
// analog für weiß, rot, grün, gelb

Wird im rechten unteren Bereich im Register EINGABE der Schieberegler betätigt, so ändert
sich entsprechend die Schriftgröße der Label- und Button-Beschriftungen im linken oberen
Bereich.

scale.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
Display display = Display.getDefault();
kundennummerLabel.setFont(new org.eclipse.swt.graphics.Font(
display,"Arial",scale.getSelection(),SWT.NORMAL));
kundennummerLabel.pack();
kundennameLabel.setFont(new org.eclipse.swt.graphics.Font(
display,"Arial",scale.getSelection(),SWT.NORMAL));
kundennameLabel.pack();
button.setFont(new org.eclipse.swt.graphics.Font(
display,"Arial",scale.getSelection(),SWT.NORMAL));
button.pack();
}});

Die Wahl der Einstellung mit Gitternetzlinien im Register TABELLE sorgt dafür, dass die Ta-
belle mit Gitternetzlinien dargestellt wird.

mitGitternetzlinienButton.addSelectionListener(
new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
table.setLinesVisible(true);
}});

260
eclipse_v01.book Seite 261 Montag, 30. Januar 2006 12:02 12

6 – GUI-Design mit Eclipse

Die Wahl der Einstellung ohne Gitternetzlinien zeigt bei der Tabelle keine Gitternetzlinien
an.

mitGitternetzlinienButton.addSelectionListener(
new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
table.setLinesVisible(false);
}});

Damit ist die Implementation des SWT-Beispiels mithilfe des WindowBuilder abgeschlos-
sen.

261
eclipse_v01.book Seite 262 Montag, 30. Januar 2006 12:02 12
eclipse_v01.book Seite 263 Montag, 30. Januar 2006 12:02 12

7 Eclipse und J2EE

von Dr. Kai Brüssau

Die Java 2 Platform, Enterprise Edition (J2EE) definiert einen Standard für die Entwick-
lung von mehrschichtigen Anwendungen. Wie bei der Java 2 Platform, Standard Edition
(J2SE) gilt der Grundsatz der Plattformunabhängigkeit. J2EE kombiniert die bereits in
J2SE existierenden Technologien, wie JDBC API für Datenbankzugriffe oder RMI für den
Aufruf von Objekten in entfernten virtuellen Maschinen. Daneben unterstützt J2EE als ser-
verseitige Komponenten die so genannten Enterprise Java Beans (EJBs) sowie die Java
Servlet API und Java Server Pages (JSP). J2EE verwendet einen einheitlichen Standard für
diese Technologien, so dass Anwendungen mit den unterschiedlichsten Anforderungen ab-
gebildet werden können. Ein Beispiel für die Architektur einer J2EE-Anwendung ist in
Abb. 7.1 dargestellt.

Abb. 7.1: Architektur einer J2EE-Anwendung

J2EE-Anwendungen können auf unterschiedlichen Systemen ablaufen. Es gibt eine Viel-


zahl von Applikations- und Webservern, die den J2EE-Standard unterstützen. In dem vor-
liegenden Buch werden wir den Tomcat Webserver als Webserver und den JBoss Server als
Server für EJB-Komponenten verwenden. Die beiden Server stehen kostenlos zur Verfü-
gung.1

1. Der Tomcat Server wird im Rahmen des Apache Jakarta Project entwickelt und kann unter
http://jakarta.apache.org/tomcat herunter geladen werden. Der JBoss Server wird von der JBoss Group
entwickelt und steht unter http://www.jboss.org zur Verfügung.

263
eclipse_v01.book Seite 264 Montag, 30. Januar 2006 12:02 12

XML

Die Entscheidung für einen Server hat aber kaum Auswirkungen auf die Entwicklung der
Webkomponenten oder der EJBs. Es existieren vor allem Unterschiede bei der Verteilung
der Komponenten auf die Systeme (Deployment). Der Programmcode der Beispiele in die-
sem Buch und die Beschreibung der technischen Aspekte können auch auf andere J2EE-
fähige Systeme übertragen werden.
Im Folgenden beschäftigen wir uns zunächst mit den Technologien XML und JSP. Es wird
die Fragestellung behandelt, wie diese Technologien mit Eclipse gehandhabt werden. Für
das komfortable Erstellen und Editieren von solchen Dokumenten sind das Hervorheben
von Tags und Befehlen (Highlighting) sowie eine Syntaxprüfung erforderlich. Rudimentäre
Funktionalitäten sind in Eclipse vorhanden. Für weitere Anforderungen an einen XML-
bzw. JSP-Editor stehen diverse Plug-Ins zur Verfügung, von denen wir die gängigen und
leistungsfähigsten betrachten. Die Erstellung von statischen HTML-Seiten kann als eine
Umsetzung von XML-Dokumenten oder als eine Spezialform eines JSP-Dokuments be-
trachtet werden. Aus diesem Grund werden wir darauf nicht explizit eingehen.
Im Anschluss werden wir uns damit beschäftigen, wie wir Webanwendungen erstellen und
auf dem Webserver installieren. Die Architektur einer solchen Anwendung ist noch ver-
gleichsweise einfach, da ausschließlich Browser und Webserver miteinander kommunizie-
ren. Die erste Erweiterung, die wir betrachten, ist die Integration von Datenbankservern.
Wir verwenden den MySQL Datenbankserver1. Dabei ist es grundsätzlich irrelevant, wel-
ches System benutzt wird.
Den Schwerpunkt dieses Kapitels bildet aber die Integration von Enterprise Java Beans.
Wir betrachten die unterschiedlichen Arten von EJBs (Session, Entity und Message Driven
Beans). Neben der Entwicklung der Serverkomponenten der Anwendung werden wir aber
auch verschiedene Clients untersuchen. Abhängig von der Systemkonfiguration sind einige
Aspekte zu unterscheiden.

7.1 XML

7.1.1 Einführung
XML (Extensible Markup Language) ist eine Metasprache zur Definition von Markup-
Sprachen. XML-Dokumente speichern Daten, die als Entitäten bezeichnet werden. Sie be-
stehen im Wesentlichen aus Tags und Daten. Die logische Bedeutung der Tags kann frei
festgelegt werden. XML-Dokumente eignen sich für den Datenaustausch zwischen unter-
schiedlichen Systemen und zur Speicherung von Informationen.
Bei der Entwicklung von XML selbst wurde und wird auf Plattformunabhängigkeit und
eine einfache Verarbeitung durch Programme Wert gelegt. Dies wird dadurch erreicht, dass
XML-Dokumente in der Regel in einfachen Textdateien gespeichert und strenge Kriterien
an die Gültigkeit von XML-Dokumenten gestellt werden.

1. Der MySQL-Datenbankserver steht unter http://www.mysql.com/ kostenlos zur Verfügung.

264
eclipse_v01.book Seite 265 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

In diesem Kapitel verwenden wir XML-Dokumente vorwiegend als Deployment-Deskrip-


toren, die einem Server die Informationen zu einer Komponente oder einer Anwendung be-
reitstellen. Daneben werden die Informationen zur Konfiguration von Servern zumeist auch
als XML-Dokument gespeichert.

7.1.2 Bestandteile eines XML-Dokuments

Tags
In XML-Dokumenten treten Tags in der Regel paarweise als Start- und als End-Tag auf. Sie
geben an, welche Bedeutung der dazwischen liegende Text besitzt. Tags können auch ver-
schachtelt werden, wobei jeweils das innere Start-Tag vor dem äußeren Start-Tag durch ein
End-Tag beendet werden muss. Tags können außerdem Attribute enthalten, deren Werte
immer in Quotes-Zeichen (entweder " oder ’) eingeschlossen sind.

Prolog
XML-Dokumente beginnen mit einem Prolog. Hierbei handelt es sich um eine Deklaration,
die die XML-Version angibt, z.B.:

<?xml version="1.0" encoding="UTF-8"?>

Außerdem wird für die Kodierung des Dokuments UTF-8 angegeben. Dabei handelt es sich
um das 8-bit Unicode Transformation Format, mit dem sich alle Unicode-Zeichen abbilden
lassen.

Document Type Declaration/Document Type Definition


Die XML Document Type Declaration steht immer zu Beginn eines XML-Dokuments und
gibt im Wesentlichen die Document Type Definition (DTD) an. Die DTD beschreibt die
Struktur einer Klasse von XML-Dokumenten mit Hilfe einer Textdatei in einem bestimm-
ten Format. Ein Beispiel für eine Document Type Declaration kann wie folgt aussehen.

<!DOCTYPE Kundenliste SYSTEM ’kundenliste.dtd’>

Das Wort DOCTYPE leitet die Deklaration ein. Danach folgt der Name des Root-Elements,
welches alle anderen Elemente (Entitäten) einschließt. Die DTD-Datei wird im Anschluss
angegeben, wobei sie wie hier auf demselben oder aber auch auf einem anderen System lie-
gen kann. In diesem Fall wird sie über eine URL angesprochen.
Die DTD definiert die Struktur und den Aufbau des Dokuments. In ihr werden die Elemente
definiert. Beispielsweise soll eine Kundenliste Kundenelemente beinhalten, die wiederum

265
eclipse_v01.book Seite 266 Montag, 30. Januar 2006 12:02 12

XML

aus Elementen wie Name, Adresse und Telefon bestehen. Dann könnte eine DTD-Datei
wie folgt aussehen1:

<?xml version="1.0" encoding="UTF-8"?>


<!ELEMENT Kundenliste (Kunde)*>
<!ELEMENT Kunde (Name, Adresse?, Telefon*)>
<!ELEMENT Name (#PCDATA)>
<!ELEMENT Adresse (#PCDATA)>
<!ELEMENT Telefon (#PCDATA)>
<!ATTLIST Telefon
Typ (mobil | privat | geschäftlich) #REQUIRED
>

Die erste Zeile definiert das Root-Element Kundenliste, welches Elemente vom Typ
Kunde beinhaltet. Durch den Stern (*) wird angegeben, dass beliebig viele Kunden in der
Kundeliste stehen dürfen. Das Element Kunde besteht wiederum aus den Elementen Name,
Adresse und Telefon. Das Fragezeichen hinter Adresse gibt an, dass dieses Element
optional ist. Die Reihenfolge der Angaben ist verbindlich. Im Anschluss folgt die Defini-
tion der Elemente, denen ausschließlich Texte (Parsed Character Data: PCDATA) und
keine weiteren Elemente zugeordnet werden dürfen. Als Letztes wird schließlich eine Attri-
butsliste (ATTLIST) angegeben, die bei Verwendung des Elements Telefon zwingend not-
wendig (REQUIRED) ist.
Mithilfe der DTD wird die Struktur von XML-Dokumenten verbindlich und zentral festge-
legt. Gültige XML-Dokumente, die einen Verweis auf eine DTD enthalten, müssen dieser
DTD entsprechen. Eine solche Gültigkeitsprüfung wird von vielen XML verarbeitenden
Tools angeboten. Mir der beschriebenen DTD kann ein XML-Dokument wie folgt aufge-
baut sein2:

<?xml version="1.0" encoding="UTF-8"?>


<!DOCTYPE Kundenliste SYSTEM ’kundenliste.dtd’>
<Kundenliste>
<Kunde>
<Name>Hans Muster</Name>
<Telefon Typ="mobil">0171 987</Telefon>
<Telefon Typ="privat">040 555</Telefon>
<Telefon Typ="geschäftlich">040 428</Telefon> </Kunde>
<Kunde>
<Name>Heidi Muster</Name>
<Adresse>Goetheallee 12, 20123 Hamburg</Adresse>
<Telefon Typ="mobil">0171 123</Telefon>
</Kunde>
</Kundenliste>

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel07/Kap70101XML-DTD


2. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel07/Kap70101XML-DTD

266
eclipse_v01.book Seite 267 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

XML Schema
Ein XML Schema ist eine XML-basierte Alternative zum DTD-Konzept. Es beschreibt die
Struktur eines XML-Dokuments. Dazu zählen:
쐌 Definition der Elemente
쐌 Definition der Attribute
쐌 Definition der Elemente, die innerhalb anderer Elementen vorkommen und deren Be-
ziehungen
쐌 Definition, ob ein Element Text enthält oder nicht
쐌 Definition der Datentypen für Elemente und Attribute
쐌 Definition von Standard- oder festgelegten Werten
Die Technik der XML Schemas ist leistungsfähiger als die der DTDs. Insbesondere die
Existenz von Datentypen und der Aspekt, dass sie in XML geschrieben werden und damit
leicht erweiterbar sind, ist ein Vorteil. Außerdem können Namensräume definiert werden,
in denen verschiedene Datentypen zusammengefasst werden. Wir verwenden beispiels-
weise den Namensraum http://www.w3.org/2001/XMLSchema, in dem einfache Daten-
typen wie string, long oder double definiert sind.
Als Beispiel diene wiederum die Kundenliste aus dem vorherigen Abschnitt zu den DTDs.
Wir entwickeln hierfür ein XML Schema. Dieses enthält das Root-Element <schema>,
welches einige Attribute besitzt. Die Deklaration des XML Schema sieht damit wie folgt
aus1:

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://eclipsebuch.entwickler.com"
xmlns:tns="http://eclipsebuch.entwickler.com"
elementFormDefault="qualified">

Üblicherweise wird der Namensraum http://www.w3.org/2001/XMLSchema im XML-


Schema mit xsd bezeichnet. Die Definition erfolgt in dem Bereich:

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"

Die Zeilen

targetNamespace="http://eclipsebuch.entwickler.com"
xmlns:tns="http://eclipsebuch.entwickler.com"

legen den Targetnamespace fest und geben an, dass die komplexen Datentypen, die in
diesem Schema definiert werden, dem eigenen Namensraum (this namespace: tns) "http://
eclipsebuch.entwickler.com" zugeordnet werden. Wir schreiben bei der Verwendung der
Datentypen jeweils den Namensraum davor.

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel07/Kap70102XML-Schema

267
eclipse_v01.book Seite 268 Montag, 30. Januar 2006 12:02 12

XML

Der Teil

elementFormDefault="qualified"

beschreibt, dass alle Elemente des Schemas einem Namensraum zugeordnet sein müssen.
Anschließend werden die einzelnen Elemente festgelegt:

<xsd:element name="Kundenliste" type="tns:myKundenliste"/>

Der Name des Tags wird hier mit Kundenliste bestimmt und ist vom Typ tns:myKun-
denliste, den wir anschließend definieren:

<xsd:complexType name="myKundenliste">
<xsd:sequence>
<xsd:element name="Kunde" type="myKunde"
minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>

Elemente vom Typ tns:myKundenliste bestehen aus 0 (minOccurs="0") oder beliebig


vielen (maxOccurs="unbounded") Elementen mit dem Namen Kunde, die wiederum
vom Typ tns:myKunde sind.
Demnach ist noch der Typ tns:myKunde zu definieren. Dieser besteht aus den drei Ele-
menten Name, Adresse und Telefon, die alle vom Typ xsd:string sind. Das Element
Name muss immer innerhalb des Kunde-Elements vorhanden sein, wohingegen die Adresse
und die Telefonnummer weggelassen werden können. Bei der Telefonnummer soll die
Möglichkeit bestehen, dass sie mehrfach vorkommt, wenn z.B. unterschiedliche Telefon-
nummern eines Kunden gespeichert werden. Die Telefonnummer soll zusätzlich ein Attri-
but erhalten, das mit Typ bezeichnet wird. Attribute können aber nicht für Elemente, die
einen einfachen Datentyp wie z.B. xsd:string besitzen, definiert werden. Aus diesem
Grund erzeugen wir einen <xsd:complexType> und legen dort das Attribut fest.
Das Attribut gibt in unserem Beispiel an, ob es sich um die private, mobile oder geschäft-
liche Telefonnummer handelt. Aus diesem Grund definieren wir eine Enumeration, die wir
mit myTelefontypen bezeichnen. Von diesem Typ ist das Attribut mit dem Namen Typ,
was bedeutet, dass das Attribut nur die Werte privat, mobil oder geschäftlich anneh-
men darf:

<xsd:attribute name="Typ" type="tns:myTelefontypen"/>

268
eclipse_v01.book Seite 269 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

Die gesamte XML Schema-Datei sieht wie folgt aus:

<?xml version="1.0" encoding="Cp1252"?>


<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://eclipsebuch.entwickler.com"
xmlns:tns="http://eclipsebuch.entwickler.com"
elementFormDefault="qualified">

<xsd:element name="Kundenliste" type="tns:myKundenliste"/>


<xsd:complexType name="myKundenliste">
<xsd:sequence>
<xsd:element name="Kunde" type="tns:myKunde"
minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>

<xsd:complexType name="myKunde">
<xsd:sequence>
<xsd:element name="Name" type="xsd:string"/>
<xsd:element name="Adresse" type="xsd:string"
minOccurs="0" />
<xsd:element name="Telefon"
minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="Typ" type="tns:myTelefontypen"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>

<xsd:simpleType name="myTelefontypen">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="mobil"/>
<xsd:enumeration value="privat"/>
<xsd:enumeration value="geschäftlich"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:schema>

269
eclipse_v01.book Seite 270 Montag, 30. Januar 2006 12:02 12

XML

In einem XML-Dokument, das auf diesem Schema basiert, verändert sich gegenüber dem
Beispiel mit der DTD das Root-Element, welches jetzt einen Verweis auf die XML Sche-
ma-Datei kundenliste.xsd besitzt1:

<?xml version="1.0" encoding="UTF-8"?>


<Kundenliste
xmlns="http://eclipsebuch.entwickler.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://eclipsebuch.entwickler.com
kundenliste.xsd">

<Kunde>...</Kunde>
</Kundenliste>

Die Attribute des Root-Elements spezifizieren als Default Namespace (xmlns) http://
eclipsebuch.entwickler.com. Das bedeutet, dass alle in diesem XML-Dokument verwende-
ten Elemente in dem entsprechenden Namensraum deklariert worden sind.
Nach der Definition des Namensraums xsi (XML Schema Instance) kann das Attribut
schemaLocation verwendet werden. Dieses gibt den Ort des XML Schema für das vor-
liegende XML-Dokument an.

7.1.3 Plug-Ins für das Bearbeiten von XML-Dokumenten


Für die Erstellung oder Bearbeitung von XML-Dokumenten sind XML-Editoren hilfreich.
Sie bieten eine übersichtliche Darstellung, prüfen die Gültigkeit in Bezug auf eine DTD
oder ein XML Schema und zeigen Fehler an.

Sample XML Editor


Eclipse beinhaltet den Sample XML Editor, der mit Hilfe eines Assistenten als Eclipse-
Plug-In erzeugt und in die Entwicklungsumgebung integriert werden kann. Dazu erzeugen
wir ein neues Projekt als Plug-In Project (Menü: FILE | NEW | PROJECT  PLUG-IN DEVELOP-
MENT | PLUG-IN PROJECT).

Nach Vergabe des Projektnamens belassen wir die Standardeinstellungen auf den beiden
folgenden Eingabemasken (PLUG-IN PROJECT STRUCTURE und PLUG-IN CONTENT).
Danach wählen wir als Projekt-Vorlage das Template PLUG-IN WITH AN EDITOR (siehe Abb.
7.2).

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel07/Kap70102XML-Schema

270
eclipse_v01.book Seite 271 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

Abb. 7.2: Plug-In-Erstellung des Sample XML Editors

Bei Klick auf FINISH wird ein neues Projekt erzeugt, welches die Grundfunktionalitäten ei-
nes Editors zur Bearbeitung von XML-Dokumenten beinhaltet. Im Rahmen der Plug-In-
Entwicklung können weitere Funktionalitäten hinzugefügt werden, was aber an dieser Stel-
le nicht Gegenstand der Betrachtung sein soll.
Um das Plug-In in Eclipse zu integrieren, müssen wir noch ein Java-Archiv mit den neu ge-
nerierten Klassen erstellen. Dazu klicken wir mit der rechten Maustaste auf das Projekt und
wählen EXPORT  DEPLOYABLE PLUG-INS AND FRAGMENTS aus. Auf der folgenden Eingabe-
maske markieren wir unser Projekt sowie den Punkt DIRECTORY im Bereich EXPORT DESTI-
NATION. Als Zielverzeichnis geben wir den Namen des Eclipse-Verzeichnisses an und kli-
cken auf FINISH (siehe Abb. 7.3)1.
Das Plug-In wird in das plugins-Verzeichnis der Eclipse-Installation kopiert und nach dem
Neustart von Eclipse mit ausgeführt. Daraufhin können wir Dateien mit der Endung .xml
mit dem Sample XML Editor anzeigen lassen.

1. Siehe auch beiliegende CD ../Kapitel07/Kap70103SampleXMLEditor

271
eclipse_v01.book Seite 272 Montag, 30. Januar 2006 12:02 12

XML

Abb. 7.3: Projektverzeichnis des Sample XML Editors

Der Sample XML Editor ist vor allem in der Lage, XML-Dokumente anzuzeigen (siehe
Abb. 7.4). Allerdings fehlt eine Darstellung in Form einer Outline View oder die Syntax-
prüfung eines XML-Dokuments in Bezug auf eine DTD oder XML-Schema-Datei. Solche
Funktionalitäten können wir durch Programmieren dem Plug-In hinzufügen, wir wollen
das aber an dieser Stelle nicht vertiefen, da wir auf andere frei verfügbaren XML-Editoren
zurückgreifen.

Abb. 7.4: Darstellung eines XML-Dokuments mit dem Sample XML Editor

272
eclipse_v01.book Seite 273 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

Ant Editor
Der Ant Editor ist ein Editor für die so genannten Ant-Skripte, die als XML-Dokumente
abgespeichert werden. In Ant-Skripten können beispielsweise die Schritte, die für das
Deployment einer Anwendung auf einen J2EE Server notwendig sind, zusammengefasst
werden. Beim Deployment der Anwendung muss dann nur noch das Ant-Skript und nicht
jeder Schritt einzeln ausgeführt werden. Ant-Skripte besitzen eine bestimmte Struktur und
beinhalten wie HTML-Dokumente spezielle Tags. Der Ant Editor überprüft die Struktur
und hilft bei der Erstellung von Ant-Skripten durch Angabe von möglichen Tags und Attri-
buten.
Über das Menü WINDOWS | PREFERENCES können wir Einstellungen für den Ant Editor vor-
nehmen. Dazu zählen beispielsweise die Farbgebung der einzelnen Teile des Ant-Skripts
und Angaben zur Formatierung.
Der Ant Editor verfügt weiterhin über eine Outline View, in der die Elemente des Ant-
Skripts übersichtlich angezeigt werden.

XMLBuddy
Ein weiteres Plug-In zur Bearbeitung von XML-Dokumenten in Eclipse ist XMLBuddy,
welches von der Firma Bocaloco Software als Eclipse Plug-In entwickelt worden ist.1
XMLBuddy bietet eine benutzerdefinierbare Farbgebung, Unterstützung bei Verwendung
und Generierung von DTDs, Gültigkeitsprüfungen in Bezug auf DTDs und XML Schemas
sowie eine synchronisierte Outline View. Vor der Nutzung müssen wir XMLBuddy in das
plugins-Verzeichnis von Eclipse kopieren und Eclipse neu starten.
Darstellung
Nach dem Öffnen eines XML-Dokuments mit XMLBuddy erscheint unter anderem die
Outline View. Hier sehen wir die einzelnen Elemente strukturiert dargestellt. Weiterhin
wird der Menüleiste der Eintrag XML hinzugefügt. Im Editorfenster selbst werden die
Tags, Texte, Kommentare etc. farblich unterschiedlich hervorgehoben. Die verwendeten
Farben können vom Benutzer verändert werden. Außerdem ist es möglich, den XML-Code
automatisch zu formatieren. Dazu muss der (gesamte oder Teile vom) Code markiert und
der Menüpunkt XML | FORMAT ausgewählt werden, wodurch die Zeilen, die zu einem Ele-
ment gehören, jeweils eingerückt werden.
Gültigkeitsprüfung
Über den Menüpunkt XML | VALIDATE können wir eine Gültigkeitsprüfung starten. Diese
vergleicht das XML-Dokument mit der DTD oder mit dem XML Schema und meldet even-
tuell auftretende Fehler. Mögliche Fehler sind, dass die Elemente nicht in der vorgegebenen
Reihenfolge stehen, dass Attributswerte nicht definiert sind oder dass nichtoptionale Felder
ausgelassen worden sind. In Abb. 7.5 wird dem Attribut Typ des Elements Telefon der
Wert moil statt mobil zugeordnet. Daher wird die Zeile als fehlerhaft markiert, weil der
Wert moil in der entsprechenden Enumeration im XML Schema nicht vorkommt.

1. Das Plug-In XMLBuddy steht unter http://xmlbuddy.com/ zur Verfügung.

273
eclipse_v01.book Seite 274 Montag, 30. Januar 2006 12:02 12

XML

Abb. 7.5: Darstellung einer XML-Datei mit dem Editor XMLBuddy

Erzeugen einer DTD-Datei


Existiert noch keine DTD-Datei, so können wir über den Menüpunkt XML | GENERATE
DTD eine neue erzeugen. Diese ist für das XML-Dokument gültig, welches wir gerade be-
arbeiten. Weil diese DTD aber in der Regel nicht unseren Ansprüchen genügt, sollte sie nur
als Grundlage dienen und weiter bearbeitet werden.
Code-Assistent
XMLBuddy unterstützt uns auch beim Schreiben eines XML-Dokuments. Wenn eine gül-
tige DTD oder ein XML Schema angegeben ist, so werden jeweils die Elemente angezeigt,
die innerhalb eines Elements verwendet werden dürfen. Außerdem erscheinen die Attribute
zu einem Tag, wenn es gerade bearbeitet wird. Dieser Code-Assistent erleichtert das
Schreiben und reduziert mögliche Tippfehler.
Bewertung
Der XML Editor XMLBuddy genügt den Ansprüchen, die wir im Folgenden benötigen, um
beispielsweise Deployment-Deskriptoren und andere Konfigurationsdateien zu erzeugen.
Vor allem die Gültigkeitsüberprüfung und der Code-Assistent sind nützliche Features, die
in dem integrierten Editor von Eclipse nicht vorhanden sind. Auf diesem Gebiet gibt es wei-
tere Plug-Ins von Drittanbietern, die ähnliche Funktionalitäten besitzen. Wir wollen diese
aber an dieser Stelle nicht im Einzelnen betrachten.

JSP-Editoren
JSP-Editoren benötigen wir zum Erstellen von JSP-Dokumenten. Hierbei werden HTML-
Tags und Java-Code in einem Dokument vermischt. Daraus ergibt sich, dass ein JSP-Editor
einerseits Gültigkeitsüberprüfungen und Code-Assistenz für den HTML-Teil als auch eine
Syntaxüberprüfung des Java-Codes bereitstellen sollte.

274
eclipse_v01.book Seite 275 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

JSP-Editoren werden bei der Erstellung von Webanwendungen verwendet. Da wir uns noch
nicht näher mit JSP-Dokumenten beschäftigt haben, werden wir uns den Funktionalitäten
von JSP-Editoren im Abschnitt 7.3.2 widmen, nachdem wir die Erstellung von JSP-Doku-
menten untersucht haben.

7.2 Webanwendungen

7.2.1 Grundlagen
Webanwendungen sind mehrschichtige Anwendungen. Die Präsentationsschicht liegt da-
bei auf der einen Seite beim Webclient bzw. beim Browser, der z.B. HTML-Dokumente
darstellt. Auf der anderen Seite wird die Präsentation vom Webserver aufbereitet, der bei-
spielsweise über JSP-Dokumente oder Servlets HTML-Dokumente erzeugt und versendet.
JSPs und Servlets können dabei auf Datenbankserver oder andere Systeme zugreifen. In
diesem Kapitel wird zunächst die Programmierung für den Webserver im Vordergrund ste-
hen.
Grundsätzlich lässt sich die Kommunikation zwischen Browser und Webserver wie folgt
darstellen:
1. Der Browser sendet eine Anfrage (HTTP Request) an einen Webserver.
2. Der Webserver verarbeitet diesen Request. Dies erfolgt beispielsweise durch Aufruf
von bestimmten Methoden innerhalb eines Servlets. Dabei wird aus dem HTTP-Re-
quest ein Request-Objekt erzeugt, so dass die Informationen der Anfrage innerhalb des
Servlets genutzt werden können.
3. Zusätzlich wird ein Response-Objekt erzeugt, welches unter anderem das neu erzeugte
HTML-Dokument erhält und nach Bearbeitung des Servlets an den Client bzw. Brow-
ser zurückgesendet wird.
Der Ablauf ist in Abb. 7.6 schematisch dargestellt.

Abb. 7.6: Kommunikation zwischen Browser und Webserver

275
eclipse_v01.book Seite 276 Montag, 30. Januar 2006 12:02 12

Webanwendungen

7.2.2 Beispiel einer Webanwendung mit Servlets


Wir wollen uns zunächst einem sehr einfachen Beispiel widmen, um die einzelnen Schritte
im Ablauf von Webanwendungen nachzuvollziehen. In diesem Beispiel kann ein Benutzer
auf einem HTML-Dokument zwei Zahlen eingeben und auf einen Button klicken. Damit
wird eine Anfrage an einen Webserver gesendet, der diese Anfrage bearbeitet, die beiden
Zahlen addiert und ein neues HTML-Dokument mit der Summe der beiden Zahlen zurück-
sendet.
Um eine Webanwendung ausführen zu können benötigen wir einen Webserver. In diesem
Buch verwenden wir den Tomcat Webserver1 in der Version 5.5. Wir werden die Anwen-
dung zunächst in einem beliebigen Verzeichnis entwickeln und anschließend das Deploy-
ment auf den Webserver vornehmen.

Erstellung eines Projekts für eine Webanwendung mit Eclipse


Im ersten Schritt erzeugen wir ein neues Java-Projekt in Eclipse mit dem Projektnamen My-
Calculator2.
Ein Servlet ist eine Java-Klasse. Die kompilierte Version dieser Klasse (.class-Datei) muss
in einem bestimmen Unterverzeichnis des Webservers stehen, damit dieser darauf zugreifen
kann. Um nicht später die Dateien in ein anderes Verzeichnis kopieren zu müssen, geben wir
für dieses Projekt als DEFAULT OUTPUT FOLDER das Verzeichnis WEB-INF/classes vor.
Weiterhin wird die externe Bibliothek servlet-api.jar (bzw. servlet.jar bei älteren Versionen
des Tomcat Servers) benötigt, um Servlets entwickeln zu können. Diese liegt im Verzeich-
nis common/lib der Tomcat-Installation und im Klassenpfad unseres Projekts eingebunden.

Erstellung des Servlets


Nachdem wir nun ein neues Java-Projekt generiert haben, fügen wir diesem ein neues Pack-
age mit dem Namen com.entwickler.eclipsebuch.web hinzu. Als Servlet erzeugen
wir eine neue Java-Klasse, die von der abstrakten Klasse javax.servlet.http.Http-
Servlet abgeleitet ist. Wir bezeichnen sie mit Rechenmaschine und ordnen sie dem
Package com.entwickler.eclipsebuch.web zu. Das Servlet soll zwei Zahlen, die in
einem Browser eingegeben worden sind, addieren und das Ergebnis an den Browser zu-
rückschicken.
Die Servlet-Klasse erbt unter anderem die Methoden doGet bzw. doPost von HttpServ-
let. Je nach Art des Request (Get Request oder Post Request) wird eine der beiden Me-
thoden ausgeführt. Aus diesem Grund sollte zumindest eine überschrieben werden, so dass
ein Servlet auf eine Anfrage reagieren kann. Informationen einer Anfrage werden mit Hilfe
des so genannten Request-Objekts übermittelt, das als Parameter an die Methoden überge-
ben wird. Das Request-Objekt ist eine Instanz der Klasse javax.servlet.http.Http-
ServletRequest und kapselt die Daten, die vom Browser gesendet werden.

1. Download unter http://jakarta.apache.org/tomcat


2. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel07/Kap70201MyCalculator

276
eclipse_v01.book Seite 277 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

Das Response-Objekt wird ebenfalls an die Methoden übergeben. Es ist ein Objekt vom
Typ javax.servlet.http.HttpServletResponse. Es enthält die Daten, die an den
Client zurückgesendet werden. Wir verwenden es vor allem für das Verschicken des
HTML-Dokuments.
Der Programmcode des Servlets zur Addition zweier Zahlen sieht wie folgt aus:

package com.entwickler.eclipsebuch.web;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Rechenmaschine extends HttpServlet{


protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException{
PrintWriter out = response.getWriter();
out.print("<!DOCTYPE HTML PUBLIC ");
out.println("'-//W3C//DTD HTML 4.0 Transitional//EN'>");
out.println("<HTML><HEAD><TITLE>Addition: ");
out.println("</TITLE></HEAD><BODY>");
int x = Integer.parseInt(request.getParameter("x"));
int y = Integer.parseInt(request.getParameter("y"));
out.println("Berechnung: "+x+" + "+y+" = " + (x+y));
out.println("</BODY></HTML>");
}
}

HTML-Dokument zur Eingabe der Daten


Die Funktionalität des Servlets soll aus einem HTML-Dokument aufgerufen werden. Auf
diesem sollen zwei Zahlen eingegeben und bei Klick auf einen Button die Berechnung ge-
startet sowie das Ergebnis auf einem zweiten Dokument ausgegeben werden. Dazu fügen
wir dem Projekt eine neue Datei mit dem Namen eingabe.html hinzu. Der HTML-Code für
die Eingabemaske sieht wie folgt aus:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">


<HTML>
<HEAD>
<TITLE>My Calculator</TITLE>
</HEAD>
<BODY>
<FORM action="calculator" method="get">
X: <INPUT type="text" name="x">

277
eclipse_v01.book Seite 278 Montag, 30. Januar 2006 12:02 12

Webanwendungen

Y: <INPUT type="text" name="y">


<INPUT type="submit" value="Berechnen" >
</FORM>
</BODY>

Beim Klick auf den Submit Button BERECHNEN wird ein HTTP Get Request an den Webser-
ver gesendet. Dort wird ein Objekt der Klasse Rechenmaschine instanziiert und die do-
Get-Methode aufgerufen. Dazu verweist das action-Attribut des FORM-Tags auf calcu-
lator. Dieser Name wird in der Konfigurationsdatei zu dieser Anwendung (Deployment-
Deskriptor) spezifiziert und der entsprechenden Servlet-Klasse zugeordnet. Das name-
Attribut legt den Namen des Parameters fest, der für den Inhalt des Textfelds verwendet
wird. Dieser Wert wird im Servlet mithilfe der Methode getParameter(name) aus dem
Objekt request ausgelesen.

Deployment-Deskriptor
Der Deployment-Deskriptor ist ein XML-Dokument. Es trägt stets den Namen web.xml.
Damit die Informationen dem Tomcat Server zur Verfügung stehen, muss die Datei im
WEB-INF-Verzeichnis der Anwendung stehen. Der Deployment-Deskriptor sieht wie folgt
aus:

<?xml version="1.0" encoding="ISO-8859-1"?>


<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web
Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>rechner</servlet-name>
<servlet-class>
com.entwickler.eclipsebuch.web.Rechenmaschine
</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>rechner</servlet-name>
<url-pattern>/calculator</url-pattern>
</servlet-mapping>
</web-app>

Das Element <url-pattern> gibt den Namen vor, über den das Servlet von einem
HTML-Dokument aus angesprochen wird. Besitzt also das action-Attribut eines Form-
Tags den Wert calculator, so wird vom Tomcat Webserver das Servlet mit dem Namen
rechner (Element <servlet-name>) erzeugt. Das Element <servlet-class> gibt den
Typ (com.entwickler.eclipsebuch.web.Rechenmaschine) des Servlets an. Je
nachdem, ob das method-Attribut des Form-Tags im HTML-Dialog den Wert get oder
post besitzt, wird die Methode doGet bzw. doPost aufgerufen.

278
eclipse_v01.book Seite 279 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

Deployment
Nachdem wir nun die Programmierung abgeschlossen haben, müssen wir das Servlet noch
kompilieren und das Projekt auf den Server übertragen. Im Verzeichnis des Tomcat Web-
servers gibt es dafür ein Unterverzeichnis webapps. Alle Webanwendungen, die hier ge-
speichert sind, werden beim Start von Tomcat standardmäßig initialisiert.
Vor dem Deployment überprüfen wir, ob die für Webanwendungen verwendete Verzeich-
nisstruktur von uns korrekt eingehalten worden ist. Sie ist in Abb. 7.7 als Package Explorer
und Navigator View dargestellt.

Abb. 7.7: Verzeichnisstruktur einer Webanwendung im Package Explorer / Navigator

In Eclipse gehen wir beim manuellen Deployment folgendermaßen vor:


쐌 Auswahl des Menüpunkts FILE | EXPORT
쐌 Auswahl der Exportart FILE SYSTEM
쐌 Markieren des gesamten Projekts und Auswahl des Verzeichnisses webapps der Tom-
cat-Installation als DESTINATION DIRECTORY oder Markieren des gesamten Verzeichnisses
WEB-INF sowie der Datei eingabe.html. Das DESTINATION DIRECTORY muss ein Unterver-
zeichnis des webapps-Verzeichnisses mit Namen MyCalculator sein.

Starten der Webanwendung


Nach dem Export der Dateien kann der Tomcat Webserver gestartet werden. Dazu ist die
Datei startup aus dem bin-Verzeichnis der Tomcat-Installation aufzurufen. Wir starten den
Browser und geben als URL http://localhost:8080/MyCalculator/eingabe.html an. In den
folgenden Beispielen gehen wir davon aus, dass Client und Server auf demselben Rechner
ausgeführt werden, und verwenden localhost als Rechnernamen. In realen Anwendungen
liegen Client und Server auf unterschiedlichen Rechnern und es muss die IP-Adresse oder
ein Alias angegeben werden.
Nach Eingabe der URL sehen wir im Browser die Eingabemaske (siehe Abb. 7.8).

279
eclipse_v01.book Seite 280 Montag, 30. Januar 2006 12:02 12

Webanwendungen

Abb. 7.8: Eingabemaske der ersten Webanwendung

Nach Klick auf den Button mit der Beschriftung BERECHNEN erscheint das HTML-Doku-
ment aus Abb. 7.9.

Abb. 7.9: Ausgabe der ersten Webanwendung

Exception Handling
Falls wir aber auf der Eingabemaske keine gültige Zahl eingeben, so wird vom Servlet eine
Exception ausgelöst, für die ein Fehlertext in für den Benutzer wenig ansprechender Form
an den Browser gesendet wird. Wir erweitern daher unser Beispiel Rechenmaschine um
ein Exception Handling, das wir in die doGet-Methode aufnehmen:


try{
int x = Integer.parseInt(request.getParameter("x"));
int y = Integer.parseInt(request.getParameter("y"));
out.println("Berechnung: "+x+" + "+y+" = " + (x+y));
}
catch(Exception ex) {
out.println("<H1>");
out.println("Bitte geben Sie gültige ganze Zahlen ein.");
out.println("</H1>");
}

Weiterhin erweitern wir den Deployment-Deskriptor web.xml um das Tag <welcome-


file-list>. Mithilfe dieses Elements werden Dateien bzw. Dokumente festgelegt, die
standardmäßig ergänzt werden, wenn die Webanwendung ohne explizite Angabe eines
Dokuments aufgerufen wird. Durch die folgenden Zeilen kann eingabe.html in der URL
ausgelassen werden.

<welcome-file-list>
<welcome-file>eingabe.html</welcome-file>
</welcome-file-list>

Nach dem erneuten Export der Projektdateien und einem Beenden und Starten des Tomcat
Webservers sind die Änderungen wirksam. Nach Eingabe der Adresse (ohne speziellen Da-
teinamen) http://localhost:8080/MyCalculator/ erscheint die Eingabemaske. Stehen in den

280
eclipse_v01.book Seite 281 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

Textfeldern keine gültigen Zahlen, erscheint nach Klick auf den Button BERECHNEN die
Meldung, die in Abb. 7.10 angezeigt wird.

Abb. 7.10: Ausgabe der Fehlermeldung

7.2.3 Beispiel einer Webanwendung mit Java Server Pages


Eine Java Server Page (JSP) ist ein Dokument, welches HTML-Befehle und Java-Code
trennt. Im Gegensatz zu den Servlets, bei denen die HTML-Tags etwas unübersichtlich im
Java-Code enthalten sind, gibt es bei den JSP-Dokumenten Bereiche für HTML-Tags und
welche für Java-Code. Das JSP-Dokument wird vom Webserver in Java-Code, also in ein
Servlet übersetzt und anschließend kompiliert. Danach erfolgt die Verarbeitung wie bei den
selbst erstellten Servlets.

Erstellen des JSP-Dokuments


Um die Technologie im Detail zu untersuchen, legen wir in Eclipse ein neues Projekt mit
dem Namen MyCalcultorJSP1 an und erzeugen direkt im Projektverzeichnis eine Datei mit
dem Namen ausgabe.jsp. Der Code dieses Dokuments sieht dann wie folgt aus:

<%@ page language="java" %>

<%-- Java-Code (Skriptlet) --%>


<%
String ausgabe;
try{
int x = Integer.parseInt(request.getParameter("x"));
int y = Integer.parseInt(request.getParameter("y"));
ausgabe = "Berechnung: "+x+" + "+y+" = " + (x+y);
}
catch(Exception ex) {
ausgabe = "Bitte geben Sie gültige ganze Zahlen ein.";
}
%>

<%-- HTML-Code --%>


<html>
<head>
<title>Calculator</title>
</head>

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel07/Kap70202MyCalculatorJSP

281
eclipse_v01.book Seite 282 Montag, 30. Januar 2006 12:02 12

Webanwendungen

<body>
<H1>
<%-- Java-Ausdruck --%>
<%= ausgabe %>
</H1>
</body>
</html>

Im ersten Teil des Dokuments steht in diesem Beispiel der Java-Code zur Berechnung der
Summe der beiden eingegebenen Zahlen. Auf einem JSP-Dokument können wir dazu das
Objekt mit der Bezeichnung request verwenden. In diesem sind die Informationen des
Request gespeichert. Der zweite Teil beinhaltet die HTML-Tags. In JSP-Dokumenten kön-
nen HTML-Tags und Java-Code beliebig vermischt werden. Dies trägt aber nicht zur Über-
sichtlichkeit bei. Aus diesem Grund haben wir im oberen Teil des Dokuments die Variable
ausgabe definiert, deren Inhalt mithilfe des Java-Ausdrucks <%=ausgabe%> ausgegeben
wird.
Neben dem JSP-Dokument benötigen wir wiederum eine Eingabemaske, die nahezu iden-
tisch zu der aus dem vorherigen Abschnitt ist. Lediglich den Wert des action-Attributs des
Form-Tags verändern wir in ausgabe.jsp1. Dadurch wird bei einem Klick auf den Button
BERECHNEN das eben dargestellte JSP-Dokument aufgerufen.

Deployment-Deskriptor
Abschließend müssen wir noch die Datei web.xml im WEB-INF-Verzeichnis erzeugen.
Weil dieses Projekt über keine Servlets verfügt, sind die entsprechenden Elemente zur De-
finition von Servlets nicht erforderlich und die XML-Datei enthält folgenden Code:

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web


Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<welcome-file-list>
<welcome-file>eingabe.html</welcome-file>
</welcome-file-list>
</web-app>

Die Webanwendung besteht in diesem einfachen Fall aus drei Dateien: eingabe.jsp, aus-
gabe.jsp und web.xml, wobei die ersten beiden im Verzeichnis MyCalculatorJSP und der
Deployment-Deskriptor web.xml im Verzeichnis MyCalculatorJSP/WEB-INF liegen. Das
gesamte Verzeichnis MyCalculatorJSP kopieren wir in das webapps-Verzeichnis der Tom-
cat-Installation und starten den Server.

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel07/Kap70202MyCalculatorJSP

282
eclipse_v01.book Seite 283 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

Nach Eingabe von http://localhost:8080/MyCalculatorJSP/ in einem Browser auf demsel-


ben Rechner ist der für den Benutzer sichtbare Ablauf der Anwendung identisch zu dem des
vorherigen Beispiels. Allerdings wird beim ersten Aufruf des JSP-Dokuments aus diesem
ein Servlet generiert und kompiliert. Die entsprechenden Dateien liegen im work-Verzeich-
nis der Tomcat-Installation.
Nachdem wir nun gesehen haben, welche einzelnen Schritte für die Generierung einer
Webanwendung notwendig sind, untersuchen wir im Folgenden, wie uns ein Plug-In bei
der Entwicklung unterstützen kann.

7.3 Eclipse-Projekt Web Tools Platform (WTP)

7.3.1 Features
Das Eclipse-Projekt Web Tools Platform (WTP) erweitert Eclipse um Features, die für die
Entwicklung von Webanwendungen, J2EE-Anwendungen oder Web Services nützlich
sind. Die WTP stellt beispielsweise Editoren für HTML, JSP, XML, DTD, XSD etc. zur
Verfügung, unterstützt den Entwickler bei der Erstellung und dem Deployment von Enter-
prise Java Beans (EJB) oder hilft beim Erzeugen von Web Services und deren Clients.
Das WTP-Projekt ist ein Eclipse-Projekt, welches ursprünglich von IBM und ObjectWeb
unterstützt worden ist. Die beiden Hauptanteile an Code, die in das WTP-Projekt eingegan-
gen sind, sind Teile des WebSphere Studio Application Developer (WSAD) von IBM und
Teile des Lomboz-Plug-In von ObjectWeb.
Das WTP unterteilt sich in zwei Unterprojekte, das Web Standard Tools (WST) und das
J2EE Standard Tools (JST).
Beim WST geht es um die Unterstützung bei der Entwicklung von Webanwendungen und
WSDL-Dokumenten/Web Services. Bei der Programmierung helfen vor allem die Edito-
ren, die für unterschiedliche Arten von Dokumenten (HTML, JSP, XML etc) Features wie
Syntax-Highlighting Vervollständigung von Befehlen oder Tags, Auswahlmöglichkeiten
von Methoden oder Parametern etc. anbieten. Deployment-Deskriptoren werden ebenfalls
erzeugt und das Deployment wird automatisiert durchgeführt.
Im Bereich des JST geht es um die Entwicklung von J2EE-Anwendungen. Assistenten hel-
fen beim Generieren von EJBs und erstellen die notwendigen zusätzlichen Klassen, Inter-
faces und Deployment-Deskriptoren. Spezielle Editoren erlauben dabei unterschiedliche
Sichten auf die Dokumente.
Im Laufe dieses Kapitels werden wir viele Features von WTP (WST und JST) kennen ler-
nen. Auch wenn die WTP uns Arbeit abnimmt und vielfach Klassen, Interfaces oder De-
ployment-Deskriptoren komplett erzeugt, ist es für einen Entwickler dennoch erforderlich,
zu wissen, welche Informationen an welchen Stellen gespeichert werden. Aus diesem
Grund werden wir nicht nur die WTP, sondern auch die theoretischen Hintergründe von
J2EE und den verwandten Technologien untersuchen.

283
eclipse_v01.book Seite 284 Montag, 30. Januar 2006 12:02 12

Eclipse-Projekt Web Tools Platform (WTP)

7.3.2 Editoren

Structured Source Editor


Auf einem JSP-Dokument gibt es in der Regel unterschiedliche Teile, die in verschiedenen
Sprachen wie HTML, Java Script, Java etc. geschrieben sind. Die Möglichkeit, innerhalb
eines Dokuments die verschiedenen Sprachen zu vereinen, basiert auf dem Structured
Source Editor (SSE) Framework. SSE erweitert den Eclipse Editor auf den Gebieten, die
über den Java Editor hinausgehen. Die Web Tools Platform bietet Unterstützung bei der Er-
stellung von Dokumenten mit XML, JSP, HTML, XHTML, CSS, DTD oder JavaScript.
Die SSE-basierten Editoren sind so entwickelt, dass sie miteinander verknüpft werden kön-
nen. So ist das Editieren von HTML und Java Code auf einem JSP-Dokument identisch
zum Editieren von HTML in einem HTML-Dokument bzw. von Java Code im gewöhnli-
chen Java Editor.
SSE bietet dabei in Abhängigkeit von der Umsetzung für eine Sprache unter anderem fol-
genden Features:
쐌 Highlighting
쐌 Refactoring
쐌 Content Assist
쐌 Quick Fix/Assist
쐌 Parameter/Attribute Hints
쐌 Property Sheet
쐌 Document/Element Formatting
쐌 Outline View
쐌 Hover Info
쐌 Toggle Comment
쐌 Delimiter Highlighting (bracket matching)

XML Editor
Der XML Editor verfügt über zwei Ansichten. In der SOURCE PANE wird das Dokument text-
basiert angezeigt, wobei die XML-Elemente grün, Attribute lila, Strings blau sowie die In-
halte schwarz angezeigt werden. Diese Farben können auf der Eingabemaske PREFERENCES
unter WEB AND XML  XML FILES  XML STYLES geändert werden.
In der Design Pane wird das XML-Dokument in einer anderen Form dargestellt. Die Ele-
mente und Attribute erscheinen in einer Spalte und die Inhalte in einer zweiten. Auf diese
Weise erhält man eine Übersicht über die Informationen innerhalb des Dokuments und
kann diese in einfacher Weise ändern. Durch Klick auf die rechte Maustaste, können neue
Elemente, Attribute etc. hinzugefügt werden.
In der Outline View, die ebenfalls mit dem XML Editor verbunden ist, werden lediglich die
Elemente in einer Baumstruktur angezeigt. Bei Auswahl eines Elements wird das entspre-
chende in der SOURCE oder DESIGN PANE ebenfalls ausgewählt. Auf diese Weise können in
komplexen Dokumenten einzelne Elemente vergleichsweise schnell gefunden werden.

284
eclipse_v01.book Seite 285 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

JSP Editor
Beim Editieren von JSP-Dokumenten werden unter anderem JSP- und HTML-Tags sowie
Java Code innerhalb von Scriptlets verwendet. Der entsprechende Editor bietet beispiels-
weise folgende Features an:
쐌 Highlighting: farbliche Hervorhebung von Tags, Texten, Kommentaren etc.
쐌 Code-Assistent für einfache HTML-Tags
쐌 Code-Assistent für JSP Actions, wie z.B. <java:useBean …>
쐌 Code-Assistent für JSP-Ausdrücke, Java-Skriptlets und Direktiven
쐌 Code-Assistent für JSP Taglibs
Es ist zu erkennen, dass die unterschiedlichen Sprachen und deren Elemente unterstützt
werden. Die Farbgebung richtet sich dabei nach den jeweiligen Styles, die auf der Einga-
bemaske PREFERENCES unter WEB AND XML verändert werden können.
Auf dem JSP-Dokument wird der Bereich mit reinem HTML und der mit den Java-Befeh-
len farblich unterschiedlich hervorgehoben. Der Code-Assistent vervollständigt einerseits
im Bereich der Java-Skriptlets die Methodennamen etc. und gibt andererseits die Attribute
zu den HTML-Tags und JSP-Actions an. Zum Anzeigen der Attribute sind hinter dem Tag
die Tasten Ÿ | Leertaste zu drücken. Außerdem wird eine Syntaxprüfung der einzelnen
Bereiche durchgeführt, so dass vor allem einfache Tippfehler leicht beseitigt werden kön-
nen.

7.3.3 Installation
Neben der Eclipse-Installation (Version 3.1) müssen vor dem Verwenden der WTP folgen-
de Plug-Ins installiert werden:
쐌 Eclipse Modeling Framework (EMF, Version 2.1)
쐌 Graphical Editing Framework (GEF, Version 3.1)
쐌 Java EMF Model Runtime (JEM, Version 1.1)1
쐌 WebTools Platform; Runtime (WTP, Version 1.0)
Die Version von EMF, GEF und JEM hängt von der verwendeten Version der WTP ab. Es
gibt ebenfalls die Möglichkeit, WTP mit den drei anderen Plug-Ins komplett zu installieren
oder eine Eclipse-Installation mit integrierter WTP und den anderen notwendigen Plug-Ins
zu verwenden. Hierbei kann es aber zu Problemen kommen, wenn man weitere Plug-Ins
verwendet, die das EMF, GEF oder JEM in einer anderen Version nutzen. Beim Einsatz des
Visual Editors (VE) ist beispielsweise darauf zu achten, dass das WTP und der VE mit den-
selben Versionen arbeiten können.
Neben den Plug-Ins müssen wir zusätzlich noch XDoclet installieren. XDoclet ist eine En-
gine zur Code-Generierung. XDoclet verarbeitet Meta-Daten, die im JavaDoc-Bereich ei-
ner Java-Anwendung angegeben werden, und erstellt daraus beispielsweise Deployment-
Deskriptoren oder Ähnliches. Der Vorteil liegt dabei darin, dass bei der Entwicklung von
Enterprise Java Beans ein Großteil der Programmierarbeit wegfällt und die notwendigen

1. Download unter www.eclipse.org.

285
eclipse_v01.book Seite 286 Montag, 30. Januar 2006 12:02 12

Eclipse-Projekt Web Tools Platform (WTP)

Daten zentral in einer Datei, in der so genannten Bean-Klasse gewartet werden können. Wir
benutzen XDoclet in der Version 1.2.3.1 Da diese Version noch keine Java 5-Unterstützung
bietet, müssen wir die Datei xjavadoc-1.1.jar durch eine neue Version (z.B. xjavadoc-1.1-
j5.jar) ersetzen.

7.3.4 Einstellungen
J2EE Perspective
Die Web Tools Platform bietet uns die zusätzliche Perspektive J2EE. Bei Wahl dieser Per-
spektive wird zwischen verschiedenen Arten von Anwendungen unterschieden. Im Project
Explorer erscheinen die möglichen Projekte, die wir entwickeln können (siehe Abb. 7.11).

Abb. 7.11: Project Explorer View in der J2EE-Perspektive der WTP

XDoclet
Weiterhin müssen wir für die Verwendung von XDoclet angeben, wo sich die Installation
befindet. Dazu gibt es in den Preferences von Eclipse (Menü WINDOW | PREFERENCES) den
Punkt XDOCLET. Wichtig ist hier, den XDoclet Builder freizuschalten und den Pfad der
XDoclet-Installation anzugeben. Als Version verwenden wir 1.2.3. Um bei der Entwick-
lung von J2EE-Anwendungen den JBoss Server zu unterstützen, sollte zusätzlich bei EJB-
DOCLET und WEBDOCLET die Checkbox JBOSS angekreuzt werden.

Server
Abhängig von der zu entwickelnden Anwendung und der bestehenden Systemlandschaft,
werden unterschiedliche Server verwendet. Wir unterscheiden dabei zwischen den reinen
Webservern und den Applikationsservern. Die WTP unterstützt verschiedene Server von
Apache, BEA, IBM, JBoss, ObjectWeb und Oracle. Das bedeutet, dass für diese Server vor-
gefertigte Definitionsdateien existieren, in denen steht, welche Bibliotheken benötigt wer-
den oder wie die Server gestartet bzw. gestoppt werden können.
Leider kann die Konfiguration der Server aus Eclipse heraus nicht verändert oder ein neuer
Server/eine neue Version eines Servers eingebunden werden – zumindest nicht in der vor-
liegenden Version der WTP.

1. Download von XDoclet unter http://www.xdoclet.net/xdoclet/index.html.


Es ist wichtig, die bin-Version zu verwenden.

286
eclipse_v01.book Seite 287 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

Wie neue Server konfiguriert und in die WTP eingebunden werden, wird in Kapitel 7.6 aus-
führlich beschrieben.

7.3.5 Entwicklung einer Webanwendung


In diesem Kapitel wird gezeigt, wie wir eine Webanwendung mithilfe der WTP erzeugen.
Wir verwenden wiederum das einfache Beispiel von oben, bei dem lediglich zwei Zahlen
addiert werden.

Dynamic Web Project


Zunächst erzeugen wir ein neues Projekt und gehen dabei wie folgt vor:
쐌 Auswahl von FILE | NEW | DYNAMIC WEB PROJECT
쐌 Festlegen des Projektnamens mit WTP-Web011
쐌 Erweitern der Eingabemöglichkeiten durch Klick auf den Button SHOW ADVANCED <<
쐌 Als TARGET RUNTIME soll der Tomcat-Server in der Version 5.5 eingetragen werden.
Dazu klicken wir auf den Button NEW, wählen bei APACHE den entsprechenden Server
aus und geben das Projektverzeichnis an (siehe Abb. 7.12).

Abb. 7.12: Festlegen des Tomcat Server v.5.5 als Webserver

Im Project Explorer erscheint bei den Dynamic Web Projects das Projekt WTP-WEB01. Die
notwendigen Bibliotheken werden in den Classpath automatisch aufgenommen. Außerdem
wird das Verzeichnis, welches auf den Webserver beim Deployment übertragen wird, unter
WEBCONTENT vorgefertigt. Das bedeutet beispielsweise, dass der Deployment-Deskriptor
mit dem Namen web.xml im Verzeichnis WEB-INF schon erzeugt ist. Unter dem Eintrag
WTP-WEB01 werden die Servlets, Servlet Mappings etc. angezeigt (siehe Abb. 7.13).

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel07/Kap70301WTP-Web01

287
eclipse_v01.book Seite 288 Montag, 30. Januar 2006 12:02 12

Eclipse-Projekt Web Tools Platform (WTP)

Abb. 7.13: Dynamic Web Project in der Project Explorer View

Servlet
Um ein neues Servlet zu erzeugen, wählen wir im Kontextmenü des Eintrags SERVLETS die
Option NEW | SERVLET aus. Das Servlet bekommt den Namen Calculator im Package
com.entwickler.eclipsebuch. Auf der folgenden Eingabemaske sind noch die Infor-
mationen für den Deployment Deskriptor einzutragen. Dazu zählen der Name des Servlets
(Tag <servlet-name>), den wir ebenfalls mit Calculator bezeichnen, und das URL
Pattern (Tag <url-pattern>), wofür wir /Calculator eintragen.
Auf der dritten Eingabemaske geben wir vor allem an, welche Methoden generiert werden
sollen. Wir markieren die doGet-Methode und klicken auf FINISH.
Das neu erzeugte Servlet besitzt am Anfang einen XDcolet-Bereich, in dem die Informatio-
nen für den Deployment-Deskriptor angegeben werden:

/**
* Servlet implementation class for Servlet: Calculator
*
* @web.servlet
* name="Calculator"
* display-name="Calculator"
*
* @web.servlet-mapping
* url-pattern="/Calculator"
*
*/

288
eclipse_v01.book Seite 289 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

Abb. 7.14: WTP-Projekt, Erstellen eines Servlets

Dieser Bereich wird von XDoclet analysiert und daraus wird der Deployment-Deskriptor
web.xml erzeugt. In der Console View von Eclipse werden die entsprechenden Meldungen
angezeigt.
Wir müssen die Methode doGet noch wie folgt implementieren und eine Startseite für un-
ser Webprojekt schreiben.

protected void doGet(HttpServletRequest request,


HttpServletResponse response)
throws ServletException, IOException{

289
eclipse_v01.book Seite 290 Montag, 30. Januar 2006 12:02 12

Eclipse-Projekt Web Tools Platform (WTP)

PrintWriter out = response.getWriter();


out.print("<!DOCTYPE HTML PUBLIC ");
out.println("'-//W3C//DTD HTML 4.0 Transitional//EN'>");
out.println("<HTML><HEAD><TITLE>Addition: ");
out.println("</TITLE></HEAD><BODY>");
int x = Integer.parseInt(request.getParameter("x"));
int y = Integer.parseInt(request.getParameter("y"));
out.println("Berechnung: "+x+" + "+y+" = " + (x+y));
out.println("</BODY></HTML>");
}

HTML-Dokument
Die Startseite generieren wir, indem wir im Kontextmenü des Eintrags WEBCONTENT den
Punkt NEW | HTML anklicken und den Namen index.html vergeben. Auf der zweiten Seite
des Assistenten kann noch ein HTML-Template ausgewählt werden. Im Rumpf, also inner-
halb des <body>-Tags des Dokuments, erstellen wir das HTML-Formular:

<form action="Calculator" method="get">


<p>Geben Sie zwei Zahlen ein:</p>
<p>x: <input type="text" name="x" value="0"></p>
<p>y: <input type="text" name="y" value="0"></p>
<p><input type="submit" name="submit" value="Senden"></p>
</form>

Das HTML-Dokument wird standardmäßig im HTML-Editor der WTP geladen. Das be-
deutet, das Features wie Syntax Highlighting, Quick Fixes etc. zur Verfügung stehen. Wei-
terhin kann das HTML-Dokument im integrierten Webbrowser betrachtet werden (OPEN
WITH | WEB BROWSER), um das Layout zu kontrollieren.

Deployment
Bei der beschriebenen Entwicklung der Webanwendung wird im Projektverzeichnis ein
Verzeichnis mit dem Namen .deployables erstellt. Darin ist die gesamte Webanwendung
enthalten, die direkt in das webapps-Verzeichnis des Tomcat Servers kopiert werden kann.
Weiterhin gibt es bei Verwendung der WTP die Möglichkeit, den Tomcat Webserver direkt
aus Eclipse heraus zu starten und die Anwendung auf dem Server ablaufen zu lassen. Dazu
gehen wir wie folgt vor (siehe Abb. 7.15):
쐌 Anzeigen der Servers View (WINDOW | SHOW VIEW | SERVERS)
쐌 Auswahl von NEW | SERVER im Kontextmenü der Servers View
쐌 Markieren des Tomcat Servers in der entsprechenden Version
쐌 Konfigurieren des Tomcat- Installationsverzeichnisses
쐌 Hinzufügen des Webprojekts WTP-Web01

290
eclipse_v01.book Seite 291 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

Abb. 7.15: Einbinden des Tomcat Webservers in Eclipse

Im Kontextmenü des Eintrags für den Tomcat Server in der Servers View können wir nun
den Server starten, stoppen und die Projekte veröffentlichen.
Nach dem Start des Servers wählen wir im Project Explorer im Verzeichnis WebContent im
Kontextmenü des HTML-Dokuments index.html RUN AS | 1 RUN ON SERVER. Im integrierten
Webbrowser von Eclipse erscheint daraufhin die Startseite der Anwendung, wobei diese
Seite vom Webserver geladen wird (siehe URL: http://localhost:8080/WTP-Web01/in-
dex.html). Nach Eingabe zweier Zahlen und Klick auf den SENDEN-Button wird demnach
auch das Ergebnis angezeigt.

291
eclipse_v01.book Seite 292 Montag, 30. Januar 2006 12:02 12

Eclipse-Projekt Web Tools Platform (WTP)

7.3.6 Beispielanwendung Kreditkartenprüfer


Nachdem wir anhand von einfachsten Beispielen die Technologie und die Struktur einer
Webanwendung betrachtet haben, wollen wir uns jetzt mit den Möglichkeiten von Web-
anwendungen beschäftigen. Häufig müssen Anwendungen Anfragen (Requests) eines Be-
nutzers (Browsers) verarbeiten und dazu mit anderen Systemen außerhalb des Webservers
kommunizieren. Eine große Rolle spielen dabei Datenbanken, aus denen bestimmte Infor-
mationen gelesen oder in die Daten gespeichert werden sollen.
Wir wollen nun eine Webanwendung entwickeln, bei der der Benutzer die Gültigkeit einer
Kreditkartennummer überprüfen kann. Dazu sollen auf einer Webseite Name und Kredit-
kartennummer eingegeben werden. Nach dem Senden der Informationen an den Webserver
wird in einer Datenbank überprüft, ob der Kunde mit dem angegebenen Namen und der
Nummer existiert. Zurückgegeben wird die Information, ob die Karte gültig ist oder nicht.
Für diese Beispielanwendung benötigen wir eine Datenbank. Prinzipiell können wir auf
jede beliebige Datenbank zugreifen, für die ein entsprechender JDBC-Treiber existiert. Wir
verwenden die Datenbank MySQL, die mit einem JDBC-Treiber kostenlos zur Verfügung
gestellt wird1.

Anlegen einer neuen Datenbank in MySQL


Zuerst müssen wir die Datenbank anlegen, auf die wir zugreifen wollen. In dieser Beispiel-
anwendung besteht die Datenbank lediglich aus der Tabelle Kunden:

CREATE TABLE Kunden (


kkNummer varchar(20) NOT NULL PRIMARY KEY,
kName varchar(50) NOT NULL,
aktiv tinyint(1) NOT NULL default 0
);

Mithilfe der Batch-Datei kkp.sql2 kann die Datenbank erzeugt werden. Dazu starten wir die
MySQL-Konsole und geben folgende Zeile ein:

mysql> source ../kkp.sql

Erstellen des Eclipse-Projekts zur Beispielanwendung


Wir erstellen ein neues DYNAMIC WEB PROJECT für unsere Beispielanwendung. Dieses Pro-
jekt bezeichnen wir mit KKP und wählen als TARGET RUNTIME den Tomcat Webserver. An-
schließend testen wir die Datenbankverbindung, indem wir den Database Explorer als neue
View einfügen und dort im Kontextmenü über NEW CONNECTION eine neue Verbindung zur
Datenbank kkp herstellen. Für diese Verbindung benötigen wir einen JDBC-Treiber. Wir
verwenden MySQL Connector/J in der Version 3.1.11.

1. Der Download von MySQL ist unter http://www.mysql.com/ möglich.


2. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel07/Kap70303KKP-SQL

292
eclipse_v01.book Seite 293 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

Auf der Maske zur Spezifikation der Verbindungsparameter sind folgende Eingaben zu ma-
chen (siehe Abb. 7.16):
쐌 Auswahl des Datenbanksystems (MySql)
쐌 Name der Datenbank (kkp)
쐌 Klasse des JDBC-Treibers (com.mysql.jdbc.Driver)
쐌 Ort der JDBC-Treiber-Datei (..\mysql-connector-java-3.1.11-bin.jar)
쐌 URL der Datenbankverbindung (jdbc:mysql://127.0.0.1:3306/kkp)
쐌 Username und Passwort für die Datenbankverbindung (falls erforderlich)

Abb. 7.16: Spezifikation der Parameter für eine Datenbankverbindung

In verschiedenen Ansichten kann man sich anschließend das Schema der Datenbank, die
Inhalte einzelner Tabellen und weitere Informationen über die Datenbank ansehen.
Bei Wahl der Punkte DATA | SAMPLE CONTENTS oder DATA | OPEN im Kontextmenü der Ta-
belle Kunden können die Daten in unterschiedlichen Sichten betrachtet werden.

Herstellen einer Datenbankverbindung


Für die Herstellung einer Datenbankverbindung wird jeweils ein entsprechender JDBC-
Treiber benötigt. Dieser muss im Classpath einer Anwendung existieren, damit diese An-
wendung ihn verwenden kann. Bei einer J2EE-Anwendung bzw. einer Webanwendung
muss der Treiber dem Server bekannt sein. Wollen wir in einer Webanwendung beispiels-
weise die MySql-Datenbank ansprechen, dann können wir die Treiberdatei (mysql-connec-
tor-java-3.1.11-bin.jar) in das Verzeichnis WEB-INF/lib der Webanwendung kopieren.
Eine andere Möglichkeit besteht darin, die Treiberdatei in das shared/lib-Verzeichnis der
Tomcat-Installation zu kopieren.

293
eclipse_v01.book Seite 294 Montag, 30. Januar 2006 12:02 12

Eclipse-Projekt Web Tools Platform (WTP)

Anschließend kann der Treiber in einer Java-Klasse der Webanwendung wie folgt geladen
werden:

Class.forName("com.mysql.jdbc.Driver").newInstance();

Für den Datenbankzugriff benötigen wir noch die URL der Datenbank. Diese setzt sich aus
jdbc:mysql://hostname:port/dbname zusammen.
Den Namen der Datenbank haben wir mit kkp festgelegt.
Der Hostname ist bei uns localhost oder 127.0.0.1, da der Datenbankserver auf dem-
selben Rechner liegt wie die Webanwendung. Der Port ist 3306.
Das Connection-Objekt wird demnach auf folgende Weise erzeugt (gegebenenfalls müs-
sen noch Username und Passwort angegeben werden):

Connection conn;
conn = DriverManager.getConnection(
"jdbc:mysql://127.0.0.1:3306/kkp");

Wir erzeugen in dem Projekt eine neue Java-Klasse und bezeichnen sie mit Pruefung. In
dem Konstruktor der Klasse soll die Datenbankverbindung hergestellt und ein Objekt vom
Typ PreparedStatement erzeugt werden. Dieses Objekt wird dann in der Methode
gueltig verwendet, um ein Objekt vom Typ ResultSet zu generieren, wobei die beiden
Parameter Kreditkartennummer und Kundenname übergeben werden. Wenn dieser Kunde
existiert, dann wird anschließend überprüft, ob die Karte gültig ist. Ist dies der Fall, wird der
Wert true zurückgegeben, ansonsten false.
Der Programmcode der Klasse sieht dann wie folgt aus1:

package com.entwickler.eclipsebuch.kartenpruefer;

import java.sql.*;

public class Pruefung {


private Connection conn;
private PreparedStatement ps;

public Pruefung(){
try {
String sql;
Class.forName("com.mysql.jdbc.Driver").newInstance();
conn = DriverManager.getConnection(
"jdbc:mysql://127.0.0.1:3306/kkp");
sql ="SELECT kknummer, kname, aktiv FROM kunden ";
sql += "WHERE kknummer = ? AND kname = ?";

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel07/Kap70304KKP

294
eclipse_v01.book Seite 295 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

ps = conn.prepareStatement(sql);
} catch (Exception e) {
System.out.println("Es ist ein Fehler aufgetreten.");
}
}

public boolean gueltig(String name, String nummer){


try {
ResultSet rs;
ps.setString(1,nummer);
ps.setString(2,name);
rs = ps.executeQuery();
if (rs.next()){
if (rs.getBoolean("aktiv")){
return true;
}
}
return false;
} catch (Exception e) {
return false;
}
}
}

Diese Java-Klasse ist so konzipiert, dass sie aus einer beliebigen Java-Anwendung instan-
ziiert werden kann. Ihr Einsatz ist also nicht auf Webanwendungen beschränkt. Für unser
Beispiel benötigen wir noch eine Eingabeseite, auf der Kreditkartennummer und Kunden-
name eingegeben werden können, und ein Dokument, von dem aus ein entsprechendes Ob-
jekt instanziiert und die gueltig-Methode aufgerufen wird.

Eingabeseite der Webanwendung


Die Seite zur Eingabe der Kreditkartennummer und des Kundennamens besteht aus reinem
HTML. Auf ihr sind ein Formular mit den beiden Textfeldern für die Eingabe sowie ein
SUBMIT-Button definiert. Mithilfe des action-Attributs des <form>-Tags ist vorgegeben,
dass bei Klick auf den Button die Seite pruefung.jsp aufgerufen wird. Wir erzeugen die
HTML-Seite über das Kontextmenü des WebContent-Verzeichnisses des Projekts (NEW |
HTML) und bezeichnen sie mit index.html. Die Eingabeseite sieht folgendermaßen aus:

<html>
<head>
<title>Willkommen zur Kreditkartenprüfung</title>
</head>
<body>
<h2>Bitte geben Sie Namen und Kreidkartennummer ein:</h2>

295
eclipse_v01.book Seite 296 Montag, 30. Januar 2006 12:02 12

Eclipse-Projekt Web Tools Platform (WTP)

<form action="pruefung.jsp">
<p>Kundenname:
<input type='text' name='kundenName' size='50'></p>
<p>Kartennummer:
<input type='text' name='kartenNummer' size='20'></p>
<p>
<input type='submit' name='submit' value='Senden'>
<input type='reset' name='reset' value='Zurücksetzen'>
</p>
</form>
</body>
</html>

Integration der Java Bean in ein JSP-Dokument


Von der Eingabeseite aus kann das JSP-Dokument pruefung.jsp aufgerufen werden. Das
JSP-Dokument dient zur Überprüfung der eingegebenen Daten zu einer Kreditkarte. An-
schließend ist eine entsprechende Ausgabe zur Gültigkeit der Karte anzuzeigen. Auf dem
JSP-Dokument wird aus diesem Grund zunächst ein Objekt der Klasse Pruefung instan-
ziiert. Das Objekt ist eine Java Bean, deren Lebensdauer mit angegeben wird. Es gibt fol-
gende Gültigkeitsbereiche
쐌 Page: Die Bean gilt nur auf der aktuellen Seite.
쐌 Request: Die Bean gilt für den gesamten Request.
쐌 Session: Die Bean gilt für einen Benutzer während seiner Sitzung.
쐌 Application: Die Bean gilt während der gesamten Anwendung. Alle Benutzer grei-
fen auf dieselbe Bean zu. Wenn sie einmal instanziiert ist, wird sie erst bei Beendigung
der Anwendung zerstört.
Wir erzeugen das JSP-Dokument, indem wir im Kontextmenü des Verzeichnisses WebCon-
tent NEW | JSP auswählen. Bei Verwendung des TEMPLATES NEW JSP FILE (HTML) erhalten
wir ein JSP-Dokument, welches im Wesentlichen aus HTML-Tags besteht. Da wir von die-
sem Dokument eine Java Bean ansprechen werden, müssen wir diese instanziieren. Dazu
vergeben wir eine ID, also einen (Objekt-)Namen, unter dem wir die Bean im JSP-Doku-
ment ansprechen wollen. Weiterhin sind der Gültigkeitsbereich und der Klassenname mit
Package-Bezeichner anzugeben, von dem die Bean abgeleitet werden soll. Diese Angaben
zur Instanziierung sollten am Anfang des JSP-Dokuments stehen:

<%@ page language="java" contentType="text/html;


charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<jsp:useBean id="pruefungBean" scope="application"
class="com.entwickler.eclipsebuch.kartenpruefer.Pruefung" />

296
eclipse_v01.book Seite 297 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

Von dem JSP-Dokument kann auf alle öffentlichen Methoden der Bean vom Typ Prue-
fung aus dem Package com.entwickler.eclipsebuch.kartenpruefer zugegriffen
werden. Die Instanziierung der Bean erfolgt beim ersten Aufruf der Seite. Danach wird im-
mer auf dieselbe Bean zugegriffen.
In der Klasse Pruefung ist die öffentliche Methode gueltig definiert, die als Parameter
zwei String-Werte übergeben bekommt, von denen der erste Wert den Kundennamen und
der zweite die Kreditkartennummer angibt. Nach einer Prüfung auf Korrektheit wird true
zurückgegeben, falls die Kombination gültig ist, und false, wenn das nicht der Fall ist.
Abschließend erstellen wir folgendes JSP-Dokument, um eine entsprechende Meldung
auszugeben.

<%@ page language="java" contentType="text/html;


charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<jsp:useBean id="pruefungBean" scope="application"
class="com.entwickler.eclipsebuch.kartenpruefer.Pruefung" />

<%
String ausgabe;
if (pruefungBean.gueltig(request.getParameter("kundenName"),
request.getParameter("kartenNummer"))){
ausgabe = "Die Karte ist gültig !";
}
else{
ausgabe = "Die Karte ist nicht gültig !";
}
%>
<html>
<head>
<title>Prüfergebnis</title>
</head>
<body>
<H1> <%=ausgabe%> </H1>
</body>
</html>

Die Klasse com.entwickler.eclipsebuch.kartenpruefer.Pruefung muss im


Verzeichnis WEB-INF/classes der Webanwendung stehen, damit der Tomcat Webserver
diese findet. Außerdem muss der JDBC-Treiber in dem entsprechenden lib-Verzeichnis der
Anwendung stehen. Bei Verwendung der WTP wird im Verzeichnis .deployables das Web-
projekt automatisch erzeugt (siehe Abb. 7.17).

297
eclipse_v01.book Seite 298 Montag, 30. Januar 2006 12:02 12

Eclipse-Projekt Web Tools Platform (WTP)

Abb. 7.17: Darstellung der Webanwendung

Die Klasse Pruefung wird direkt vom Webserver bzw. dessen virtueller Maschine ausge-
führt und benötigt keinen eigenen Applikationsserver. Innerhalb der Klasse wird auf eine
Datenbank zugegriffen, deren Verbindung im Konstruktor der Klasse hergestellt wird. Bei
jedem Zugriff wird ein Datensatz mit den entsprechenden Parameterwerten gesucht. Liegt
keiner vor oder besitzt das Feld aktiv den Wert false, so ist die Kreditkarte nicht gültig
und es wird false, ansonsten true zurückgegeben.

Deployment auf den Webserver


Das Deployment auf den Webserver ist im vorherigen Abschnitt 7.3.5 detailliert beschrie-
ben. Es besteht die Möglichkeit, über die Servers View den Webserver (Apache Tomcat 5.5)
zu starten und das Projekt zu veröffentlichen.
Eine alternative Vorgehensweise sieht vor, das Webprojekt aus dem Verzeichnis .deploy-
ables in das webapps-Verzeichnis des Webservers zu kopieren und den Server direkt zu
starten.
Zum Entwickeln und Testen einer Anwendung ist sicherlich die erste Variante geeignet,
wohingegen die zweite für das Deployment auf den realen Webserver verwendet werden
sollte.

7.3.7 Server Debugging


Beim Starten des Webservers aus Eclipse heraus gibt es die Möglichkeit, den Debug-
Modus zu wählen. Auf diese Weise können wir die Java Beans, die auf dem Webserver ab-
laufen, debuggen. Dazu setzen wir wie gewohnt einen oder mehrere Haltepunkte (Break-
points) und starten die Webanwendung, indem wir im Kontextmenü der Startseite DEBUG
AS | DEBUG ON SERVER auswählen. Beim Erreichen des ersten Haltepunkts wird der Pro-
grammablauf unterbrochen und in einzelnen Schritten oder bis zum nächsten Haltepunkt
etc. fortgeführt.
Es besteht ebenfalls die Möglichkeit, innerhalb der Scriptlets auf einem JSP-Dokument
Haltepunkte zu setzen (Toggle Breakpoints). Dadurch ist es möglich, den Ablauf inner-
halb des JSP-Dokuments näher zu untersuchen und dabei die Werte der Variablen, Re-
quest-, Response-Objekte etc. zu überwachen.

298
eclipse_v01.book Seite 299 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

7.3.8 Lomboz Plug-In und WTP


Das Lomboz Plug-In, das von ObjectWeb zur Verfügung gestellt wird, basiert auf der WTP.
Da ObjectWeb den Programmcode des ehemaligen Lomboz Plug-In eingebracht hat, gehen
dessen Funktionalitäten mit in die WTP ein. Es gibt nach wie vor eine Installation des Lom-
boz Plug-In neben WTP. Allerdings bietet dieses keine nennenswerten zusätzlichen Funk-
tionalitäten im Vergleich zur WTP. Aus diesem Grund wird Lomboz nicht weiter unter-
sucht.

7.4 J2EE-Anwendungen mit Enterprise Java Beans


Nachdem wir uns mit Webanwendungen beschäftigt haben, wollen wir die Architektur um
eine weitere Schicht erweitern. Dazu untersuchen wir, wie Teile einer Anwendung auf ei-
nem Applikationsserver ablaufen können. Ein J2EE-Applikationsserver ist eine Ablauf-
umgebung für Java-Komponenten (Enterprise Java Beans). Eine Anwendung kann von au-
ßen auf diese Komponenten zugreifen. Der J2EE-Applikationsserver berücksichtigt dabei
Zugriffsrechte und andere Sicherheitsaspekte, beinhaltet ein Transaktionsmanagement und
ermöglicht das persistente Speichern von Daten. Im Folgenden werden wir Enterprise Java
Beans (EJB) bei einem J2EE-fähigen Applikationsserver (z.B. JBoss) registrieren, um dann
aus einer Java-Anwendung darauf zugreifen zu können. Zur Entwicklung von J2EE-An-
wendungen, insbesondere EJBs, benötigen wir außerdem das J2EE SDK1 von Sun.

7.4.1 Grundlegende Begriffe


Ein Applikationsserver stellt verschiedene Dienste (Services) zur Verfügung. Für ein bes-
seres Verständnis der Konfiguration von EJB-Komponenten für diese Dienste wollen wir
diese zunächst im Einzelnen betrachten.

JDBC
Java Database Connectivity (JDBC) dient dazu, aus einem Java-Programm auf Datenban-
ken zuzugreifen. JDBC besteht aus mehreren Teilen:
쐌 JDBC-API:
Die JDBC-API ist die Programmierschnittstelle. Sie definiert Interfaces, die unabhän-
gig von der zugrunde liegenden Datenbank sind. Die dahinter liegende Implementie-
rung wird von den Entwicklern der Datenbanktreiber vorgenommen. Da eine Java-An-
wendung nur die Interfaces sieht, ist sie theoretisch von der Datenbank unabhängig. In
der Regel kann eine Datenbank aber nicht ohne weiteres ausgetauscht werden, da even-
tuell spezielle SQL-Dialekte verwendet oder beispielsweise Stored Procedures imple-
mentiert worden sind, die von einer anderen Datenbank nicht direkt interpretiert werden
können.

1. Das J2EE SDK steht unter http://java.sun.com/j2ee/ kostenlos zur Verfügung.

299
eclipse_v01.book Seite 300 Montag, 30. Januar 2006 12:02 12

J2EE-Anwendungen mit Enterprise Java Beans

쐌 JDBC-Treiber:
Der JDBC-Treiber muss für eine Datenbank bereitgestellt werden, damit der Zugriff aus
einem Java-Programm mit Hilfe der JDBC-API möglich ist. Es gibt vier Klassen von
JDBC-Treibern:
a) Die Typ 1-Treiber werden auch als JDBC-ODBC-Bridge bezeichnet. ODBC (Open
Database Connectivity) ist ein Standard der Firma Microsoft, der den Zugriff auf
Datenbanken über eine genormte Schnittstelle ermöglicht. Die Typ 1-Treiber dienen
dazu, JDBC-Aufrufe in ODBC-Aufrufe umzuwandeln. Ein solcher Treiber wird in
der Java 2, Standard Edition bereits zur Verfügung gestellt, so dass der Zugriff auf
alle Datenbanken, für die ein ODBC-Treiber vorhanden ist, möglich ist.
b) Die Typ 2-Treiber sind herstellerspezifische Treiber für einzelne Datenbanksysteme.
Im Vergleich zu den Typ 1-Treibern ist die Leistungsfähigkeit höher und der Zugriff
schneller, da sie speziell auf die Technologie der Datenbank optimiert werden kön-
nen. Die Typ 2-Treiber werden zum Teil von den Herstellern der Datenbanken direkt
oder von Drittanbietern angeboten.
c) Die Typ 3-Treiber sind reine Java-Treiber, die für dreischichtige Anwendungen kon-
zipiert sind. Hierbei wird über das Netzwerk mit einer Middleware kommuniziert,
die sich um den Zugriff auf die Datenbank kümmert.
d) Die Typ 4-Treiber sind ebenfalls reine Java-Treiber, die für zweischichtige Client-
Server-Architekturen eingesetzt werden. Der Client kommuniziert direkt mit dem
Datenbank-Management-System, wobei dort eine herstellerspezifische interne und
nach außen unsichtbare Schicht implementiert ist, die als Middleware fungiert.
쐌 JDBC-Treibermanager:
Der JDBC-Treibermanager ist verantwortlich für die Verwaltung und das Laden der
verfügbaren JDBC-Treiber.

Namensdienst (Naming Service)


Ein Namensdienst verwaltet Zuordnungen von Namen zu bestimmten Datenobjekten. Alle
Datenobjekte werden nach einem standardisierten Namen angesprochen. Der Client ver-
wendet einen Namensdienst, um ein Objekt zu identifizieren. Häufig verwendete Namens-
dienste sind beispielsweise:
쐌 Domain Name Service (DNS): Dieser Namensdienst wird im Internet verwendet, um
die Zuordnung eines Domain Name auf eine IP-Adresse vorzunehmen. Es handelt sich
hierbei um einen verteilten Namensdienst, weil die zugrunde liegende Datenbank auf
vielen verschiednen Servern liegt, die sich gegenseitig aktualisieren können.
쐌 Common Object Service (COS) Naming: Dieser Namensdienst wird von CORBA ver-
wendet, um die Referenzen auf CORBA-Objekte zu verwalten.
Die Namensdienste speichern in der Regel nicht direkt die Objekte, sondern lediglich die
Referenzen auf diese. Beim DNS wird beispielsweise die IP-Adresse gespeichert, über die
auf den entsprechenden Webserver zugegriffen werden kann. Die Daten selber werden vom
Namensdienst nicht bereitgestellt.

300
eclipse_v01.book Seite 301 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

Applikationsserver benötigen Namensdienste, damit der Client die dort installierten Kom-
ponenten von außen ansprechen kann. Die Komponenten erhalten eindeutige Namen, über
die der Client diese identifiziert und anspricht.

Verzeichnisdienst (Directory Service)


Ein Verzeichnisdienst ist eine Art Datenbank, die für den Lesezugriff optimiert ist. Die In-
formationen werden in einem hierarchischen Informationsmodell gespeichert. Die in einem
Namensdienst definierten Namen können von einem Verzeichnisdienst gespeichert wer-
den. Der Verzeichnisdienst übernimmt dann das Auffinden des Namens und der Namens-
dienst liefert die Referenz auf ein Objekt.
Der Domain Name Service ist ebenfalls ein Directory Service. Der Directory Service DNS
hat die so genannten Domain Names gespeichert. Bei einer Anfrage wird der entsprechende
Eintrag gesucht und die korrespondierende IP-Adresse zurückgegeben. Der Dienst ist
hochgradig für diese Aufgabe spezialisiert und besitzt sehr effiziente Indizierungen, Such-
und Caching-Verfahren. Weitere Verzeichnisdienste sind:
쐌 Network Information Services (NIS)
쐌 Novell Directory Service (NDS)
쐌 Windows NT Domains/Active Directory Service
Diese Verzeichnisdienste verwalten zusätzlich Zugriffsrechte für Benutzer und Benutzer-
gruppen sowie für Computer oder bestimmte IP-Adressen.

LDAP
Aufgrund der Vielzahl unterschiedlicher Verzeichnisdienste, die in vielen Netzwerken Ver-
wendung finden, ist die Abb. der Zugriffsrechte sehr komplex. Unterschiedliche Benutzer-
namen und Passwörter für die verschiedenen Dienste machen es schwierig, Anwendungen
zu entwickeln, bei denen mehrere Verzeichnisdienste miteinander interagieren.
Das Lightweight Directory Access Protocol (LDAP) beseitigt diese Probleme. Mit LDAP
wird definiert, wie die Clients den Datenzugriff auf dem Server vornehmen. Es ist dabei ir-
relevant, um welchen Server es sich handelt und wie dort die Daten abgespeichert sind. Für
die verschiedenen Verzeichnisdienste muss es aber ein LDAP-Front-End geben, damit der
Zugriff möglich ist. Dies ist bei den gängigen Verzeichnisdiensten wie NDS, NIS oder Win-
dows NT Domains der Fall.
LDAP ist die Grundlage für das Java Naming and Directory Interface, das im Folgenden er-
läutert wird.

JNDI
Java Naming and Directory Interface (JNDI) ist ein Interface, das Methoden für den Zugriff
auf Namens- und Verzeichnisdienste bereitstellt. JNDI ist vergleichbar mit JDBC, das für
den Zugriff auf Datenbanken benötigt wird. JNDI entkoppelt die Java-Anwendung von
dem konkreten Namens- bzw. Verzeichnisdienst.

301
eclipse_v01.book Seite 302 Montag, 30. Januar 2006 12:02 12

J2EE-Anwendungen mit Enterprise Java Beans

Mit JNDI können wir ähnlich wie bei den JDBC-Typ-1- und -Typ-2-Treibern entweder
über so genannte Service Provider mit den einzelnen Verzeichnisdiensten interagieren oder
einen LDAP-Service-Provider (analog zu den JDBC-ODBC-Treibern) verwenden, der
wiederum für die wichtigsten Verzeichnisdienste angeboten wird.
JNDI bietet für den Fall Vorteile, dass mehrere Verzeichnisdienste in einer Anwendung ver-
bunden werden müssen. Außerdem macht JNDI es für den Entwickler insgesamt einfacher,
die Interaktion zwischen Client und Verzeichnisdienst zu realisieren, weil jeweils dieselbe
JNDI-API verwendet wird. Die Kommunikation der einzelnen Dienste ist in Abb. 7.18
exemplarisch dargestellt.
Wir werden im Folgenden bei unseren Beispielanwendungen JNDI nutzen, um die EJB-
Komponenten auf dem Applikationsserver zu identifizieren und vom Client aus zu refe-
renzieren. Dabei verwenden wir die Variante, bei der der Namensdienst des Applikations-
servers direkt angesprochen wird. Dazu ist es natürlich erforderlich, dass der Applikations-
server JNDI implementiert hat.

Abb. 7.18: Interaktion JNDI/LDAP und Namensdienste

7.4.2 Enterprise Java Bean (EJB)


Enterprise Java Beans (EJBs) sind Komponenten, die nach einem von Sun Microsystems
entwickelten Standard spezifiziert werden. Sie werden in verteilten J2EE-Anwendungen
eingesetzt und basieren auf der Programmiersprache Java. Es werden folgende Arten von
EJBs unterschieden:
쐌 Stateless Session Beans
쐌 Stateful Session Beans
쐌 Entity Beans (Container Managed/Bean Managed Persistence)
쐌 Message Driven Beans

302
eclipse_v01.book Seite 303 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

Die Stateful Session Beans gelten wie das Session-Objekt bei Webanwendungen für einen
Benutzer bei einer Sitzung. Die Stateless Session Beans stellen in der Regel Funktionalitä-
ten zur Verfügung, die alle Benutzer aufrufen können und für die keine Daten gespeichert
werden müssen. Entity Beans dienen zur Speicherung von Daten, auf die zumeist unter-
schiedliche Benutzer zugreifen. Bei Message Driven Beans werden Funktionalitäten nicht
direkt aufgerufen. Sie erhalten Nachrichten, auf die bestimmte Reaktionen, z.B. der Aufruf
von bestimmen Methoden, erfolgen kann.
Die EJBs werden über so genannte EJB-Container zur Verfügung gestellt. Der EJB-Con-
tainer ist ein Teil des EJB Applikationsservers, der neben der Instanziierung und dem
Löschen der Objekte noch Sicherheitskontrollen durchführt oder die Aufgaben des Trans-
aktionsmanagements übernimmt.

Stateless Session Beans


Die statuslosen Session Beans können für einfache Methodenaufrufe verwendet werden. Es
werden keinerlei Informationen gespeichert, so dass jede Anfrage unabhängig von der vor-
herigen bearbeitet wird. Prinzipiell genügt es, ein Objekt zu instanziieren, auf das alle
Clients zugreifen können. Eine solche Session Bean wird in der Regel verwendet, um eine
Funktionalität abzubilden, die mit einer Anfrage bearbeitet werden kann und das Ergebnis
zurückliefert. Beispielsweise kann für die Kreditkartenprüfung eine statuslose Session
Bean eingesetzt werden, da vom Client aus alle Informationen zu einer Anfrage an den Ser-
ver gesendet, dort verarbeitet und die Ergebnisse zurückgesendet werden. Bei einer weite-
ren Anfrage benötigt der Server keine Statusinformationen der vorherigen Anfrage. Neh-
men wir aber an, dass die Kreditkartenprüfung nur bestimmte Clients nutzen dürfen, so ist
beispielsweise eine Authentifizierung erforderlich. Wenn wir weiterhin mit statuslosen
Session Beans arbeiten wollen, muss der Client sich bei jeder Anfrage neu authentifizieren.
Da keine Informationen zu einer Anfrage gespeichert werden, hat der Server bei einer wei-
teren Anfrage „vergessen“, dass der Client sich schon einmal authentifiziert hat.

Stateful Session Beans


Bei den Stateful Session Beans bleiben hingegen Statusinformationen zu einer Sitzung er-
halten. Das bedeutet, dass bei mehreren Anfragen eines Clients während einer Sitzung auf
bestimmte Daten zugegriffen werden kann. Es können natürlich mehrere Sitzungen von un-
terschiedlichen Clients gleichzeitig verarbeitet werden, wobei für jede Session ein eigenes
Objekt erzeugt wird.
Eine Stateful Session Bean können wir beispielsweise nutzen, wenn wir die Kreditkarten-
prüfung mit Authentifizierung des Clients so realisieren wollen, dass der Client sich ledig-
lich einmal zu Beginn einer Sitzung authentifizieren muss und anschließend beliebig viele
Kreditkartenprüfungen durchführen kann.
Eine detaillierte Betrachtung der Session Beans und ihrer Verwendung erfolgt in Abschnitt
7.5.

303
eclipse_v01.book Seite 304 Montag, 30. Januar 2006 12:02 12

J2EE-Anwendungen mit Enterprise Java Beans

Entity Beans
Entity Beans werden verwendet, um Daten, die für viele Clients zur Verfügung stehen sol-
len, zu speichern. Daneben bieten Entity Beans auch beliebige Funktionalitäten an, die in
öffentlichen Methoden implementiert werden. Entity Beans werden in der Regel persistent
gespeichert. Das bedeutet, dass ihr Zustand jeweils in einer Datenbank vorhanden ist. Es
wird zwischen der Container Managed Persistence (CMP) und der Bean Managed Persis-
tence (BMP) differenziert. Der Unterschied besteht darin, dass bei CMP nur anzugeben ist,
welche Daten persistent gespeichert werden sollen, und der Applikationsserver die Umset-
zung selbstständig übernimmt. Bei der Variante BMP müssen wir im Code in bestimmten
Methoden direkt die Speicherung der Daten programmieren. Der EJB-Container ruft dann
die jeweiligen Methoden auf.
In Abschnitt 7.11 beschreiben wir im Einzelnen den Einsatz von Entity Beans und die Va-
rianten CMP und BMP anhand von Beispielen.

Message Driven Beans


Message Driven Beans (MDB) verwenden den Java Messaging Service (JMS). Über den so
genannten Message Listener empfangen sie Nachrichten. Abhängig von der Kommunika-
tionsart (Queue oder Topic) werden die Nachrichten an die Bean weitergeleitet oder von der
Bean abgeholt. Liegt eine Nachricht bei der Bean vor, wird ein Ereignis ausgelöst und eine
vordefinierte Methode aufgerufen.
Ein Client kann mit einer MDB demnach nur kommunizieren, indem er Nachrichten ver-
sendet. Direkte Methodenaufrufe sind vom Client nicht möglich. Die Kommunikation er-
folgt anonym und damit statuslos. Die Besonderheit der MDBs liegt aber vor allem darin,
dass bei der Kommunikation über eine Queue diese asynchron und zeitunabhängig erfolgen
kann. Bei der Kommunikation über ein Topic können dagegen mehrere MDBs dieselben
Nachrichten abonnieren und empfangen, so dass vom Client diese nicht mehrfach gesendet
werden müssen.
Der Abschnitt 7.12 beschäftigt sich detailliert mit den Kommunikationsvarianten Topic und
Queue sowie den Message Driven Beans.

7.4.3 Zugriff eines Clients auf eine EJB

EJB-Container
EJB-Container verwalten Enterprise Java Beans. Dort werden sie instanziiert und gelöscht.
Der EJB-Container ist Bestandteil eines J2EE-Applikationsservers (z.B. JBoss). Der EJB-
Container beinhaltet zum einen Instanzen der EJB-Klassen und zum anderen die dazuge-
hörigen so genannten Home-Objekte. Die Instanziierung der EJBs übernimmt der EJB-
Container. Es werden eine Instanz oder ein Pool von Instanzen erzeugt. Die Home-Objekte
zu den EJBs erzeugen dann vor dem Zugriff durch einen Client Referenzen auf diese beste-
henden Instanzen. Das erhöht die Performance, da nicht bei jedem neuen Zugriff die Ob-
jekte instanziiert werden müssen.

304
eclipse_v01.book Seite 305 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

Für die Installation einer EJB in einem EJB-Container sind folgende Schritte durchzufüh-
ren:
쐌 Zunächst werden Java-Archiv-Dateien mit entsprechenden Klassen und Interfaces so-
wie dem Deployment-Deskriptor an den Server übertragen. Der Deployment-Deskrip-
tor enthält vor allem eine Beschreibung der EJB. Für jede EJB generiert der Container
das korrespondierende Home-Objekt. Dieses beinhaltet die so genannte create-Me-
thode, mit deren Hilfe von einem Client aus eine Referenz auf eine Instanz der EJB-
Klasse erzeugt wird. Diese Instanz stammt aus dem Pool der Instanzen, der vom EJB-
Container gebildet worden ist. Im Allgemeinen ist also keine Instanziierung notwendig.
쐌 Das Home-Objekt muss beim Namensdienst des Servers registriert werden. Über diesen
Namen kann ein Client dann auf das Home-Objekt zugreifen und eine Referenz auf eine
Instanz der EJB-Klasse, also auf das EJB-Objekt, erhalten.

Client
Damit ein Client auf eine EJB zugreifen kann, muss dieser ein so genanntes Proxy-Objekt
erzeugen. Das ist ein clientseitiges Objekt, das die EJB repräsentiert. Die Methodenaufrufe
erfolgen über dieses Objekt und werden an den EJB-Container weitergeleitet, wobei Über-
gabeparameter ebenfalls übertragen werden. Die Datentypen der Übergabeparameter müs-
sen aus diesem Grund serialisierbar sein. Serialisierbar sind einfache Datentypen und Ob-
jekte von Klassen, die das Interface Serializable implementiert haben. Das Ergebnis
eines Methodenaufrufs wird als Rückgabewert an den Client zurückgesendet. Entspre-
chend muss der korrespondierende Datentyp serialisierbar sein.
Der Zugriff vom Client auf die öffentlichen Methoden einer EJB lässt sich demnach wie
folgt zusammenfassen:
쐌 Der Client benötigt für den Zugriff auf eine EJB zwei Interfaces. Das so genannte Home
Interface enthält unter anderem die Deklaration der create-Methode, die im Home-
Objekt implementiert ist. Das andere Interface wird als Remote Interface bezeichnet. Es
besitzt die öffentlichen Methoden der EJB. Der Client erzeugt als Erstes ein Objekt vom
Typ des Home Interface. Dazu wird der Name verwendet, mit dem die Bean sich beim
Namensdienst registriert hat. Der Namensdienst gibt dann eine Referenz auf das so ge-
nannte Home-Objekt im EJB-Container zurück. Mit Hilfe des Home-Objekts wird eine
Referenz der eigentlichen EJB an den Client gegeben.
쐌 Beim Aufruf einer Business-Methode des Bean-Objekts wird jeweils vom Applika-
tionsserver überprüft, ob der Client für den Aufruf der Methode berechtigt ist, und ge-
gebenenfalls eine Transaktion gestartet. Danach delegiert er den Methodenaufruf an die
Bean, die diesen dann ausführt. Falls sich der Zustand des Objekts ändert, wird dieser in
der Datenbank gespeichert und die Transaktion bei Bedarf beendet. Anschließend kann
der Client wieder auf den veränderten Zustand des Objekts zugreifen.
In Abb. 7.19 ist die Kommunikation zwischen Client und Server zum Zugriff auf eine EJB
vereinfacht dargestellt.

305
eclipse_v01.book Seite 306 Montag, 30. Januar 2006 12:02 12

Session Beans

Abb. 7.19: Vereinfachte Darstellung des Zugriffs auf EJBs

7.5 Session Beans

7.5.1 Bean-Klasse
In Abhängigkeit von der Art der Bean (Session, Entity oder Message Driven Bean) imple-
mentiert die Bean-Klasse das Interface SessionBean, EntityBean oder Message-
DrivenBean aus dem Package javax.ejb. Diese Interfaces bzw. dieses Package stammt
aus der Bibliothek j2ee.jar, die in den Classpath eines EJB-Projekts mit aufgenommen wer-
den sollte. Die Datei j2ee.jar stammt aus dem lib-Verzeichnis der Installation des J2EE
SDK1 von Sun.
In den Interfaces stehen jeweils Methoden, die vom EJB-Container zu bestimmten Zeit-
punkten aufgerufen werden. Beispielsweise gibt es die ejbActivate-Methode, die vom
EJB-Container aufgerufen wird, wenn eine Instanz aus dem Pool herausgenommen und
von einem Objekt (des Clients) referenziert wird.
Weiterhin definieren wir in der Bean-Klasse die ejbCreate-Methode. Diese korrespon-
diert mit der create-Methode des Home-Objekts und besitzt jeweils dieselbe Parameter-
liste. Die create-/ejbCreate-Methode kann überladen werden. Für jede ejbCreate-
Methode, die wir in die Bean-Klasse schreiben, müssen wir eine entsprechende create-
Methode mit derselben Signatur im Home Interface definieren. Wir rufen also nicht direkt
die ejbCreate-Methode des EJB-Objekts auf, sondern verwenden die create-Methode
des Home-Objekts, die dann wiederum die ejbCreate-Methode auslöst.
Neben diesen Methoden, die ausschließlich vom EJB-Container angesprochen werden, be-
inhaltet die EJB die Geschäftsmethoden, in denen sich die eigentliche Logik der Kompo-
nenten befindet. Die öffentlichen Methoden können direkt vom Client aufgerufen werden,
sobald eine Referenz auf die EJB besteht. Besitzt eine Methode einen Rückgabewert, wird
dieser nach der Ausführung der Methode an den Client zurückgesendet.

1. Download unter http://java.sun.com/j2ee/

306
eclipse_v01.book Seite 307 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

Die Geschäftsmethoden einer EJB unterscheiden sich nicht von den Methoden einer ein-
fachen Java-Klasse. Die Systematik über Zugriffsbeschränkungen und andere Sicherheits-
mechanismen sowie Transaktionsverarbeitung wird vom J2EE-Applikationsserver gehand-
habt. Eine Session Bean-Klasse zur Abbildung einer Warenkorb-Funktionalität kann dem-
nach wie folgt aussehen1:

package com.entwickler.eclipsebuch.warenkorb;

import java.util.*;
import javax.ejb.*;

public class WarenkorbBean implements SessionBean {


private String kName;
private String kNr;
private Vector<String> inhalt;

// ejbCreate-Methoden
public void ejbCreate(String name) throws CreateException {
this.ejbCreate(name,"0");
}

public void ejbCreate(String name, String nr)


throws CreateException {
kName = name;
kNr = nr;
inhalt = new Vector();
}

// Geschäftsmethoden
public void hinzufuegen(String artikel) {
inhalt.addElement(artikel);
}

public void entfernen(String artikel) {


inhalt.removeElement(artikel);
}

public Vector<String> getInhalt() {


return inhalt;
}

// Konstruktor
public WarenkorbBean() {}

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel07/Kap70501Warenkorb

307
eclipse_v01.book Seite 308 Montag, 30. Januar 2006 12:02 12

Session Beans

// Methoden des implementierten Interfaces SessionBean


public void ejbRemove() {}
public void ejbActivate() {}
public void ejbPassivate() {}
public void setSessionContext(SessionContext sc) {}
}

Da es sich bei der WarenkorbBean um eine Stateful Session Bean handelt, wird für jede
Session eine neue Instanz erzeugt und Statusinformationen können in den Instanzvariablen
benutzerabhängig gespeichert werden. Die Instanziierung wird ausschließlich vom EJB-
Container vorgenommen und kann von außen nicht gesteuert werden. Nach der Instanzi-
ierung wird das Objekt dem so genannten Instanzenpool zugeordnet, wobei die setSes-
sionContext-Methode ausgeführt wird.
Alle Methoden, die das Präfix ejb besitzen (ejbCreate, ejbRemove, ejbActivate und
ejbPassivate), werden zu verschiedenen Zeitpunkten im Lebenszyklus der EJB aus-
schließlich vom EJB-Container aufgerufen:
쐌 ejbActivate: Instanz wird aus dem Instanzenpool herausgenommen und durch einen
Client referenziert.
쐌 ejbPassivate: Instanz wird in den Instanzenpool zurückgelegt, wenn sie nicht mehr
benötigt wird.
쐌 ejbCreate: Client erzeugt eine Referenz auf eine EJB.
쐌 ejbRemove: Client zerstört Referenz auf eine EJB.

7.5.2 Interfaces
Remote Home Interface/Local Home Interface
Das Remote Home Interface (javax.ejb.EJBHome) und das Local Home Interface (ja-
vax.ejb.EJBLocalHome ) dienen dazu, dass der Client eine Referenz auf eine EJB er-
zeugen kann. Dazu gibt es im EJB-Container das Home-Objekt. Vom Client wird dieses
Objekt referenziert. Der Client definiert die Referenz vom Typ des Remote Home oder vom
Typ des Local Home Interface. Das ist davon abhängig, ob der Client die EJB aus derselben
oder aus einer anderen virtuellen Maschine heraus anspricht.
Wenn ein Client von außen aus einer anderen virtuellen Maschine auf die EJB zugreift,
dann ist das ein entfernter Zugriff und das Remote Home Interface findet seine Verwen-
dung. Wir bezeichnen das auch als einen Zugriff aus einem anderen Kontext.
Wird dagegen von einer EJB aus eine andere EJB innerhalb desselben Containers benutzt,
dann sprechen wir diese lokal an und greifen auf das Local Home Interface zurück. Beide
Komponenten (EJBs) werden hierbei auf derselben virtuellen Maschine bzw. im selben
Kontext ausgeführt.
Der Remote-Zugriff erfolgt über das Protokoll RMI (Remote Method Invocation). In der
throws-Klausel der Methoden des Remote Home Interface geben wir die Ausnahme
java.rmi.RemoteException an, die beim lokalen Zugriff nicht ausgelöst wird.

308
eclipse_v01.book Seite 309 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

Häufig wird beim Remote Home Interface das Wort Remote weggelassen. Wir werden im
weiteren Verlauf nur noch von Home Interface sprechen, wenn wir das Remote Home In-
terface meinen.
In den beiden Interfaces EJBHome und EJBLocalHome ist jeweils die remove-Methode
deklariert. Bei Aufruf dieser Methode durch den Client führt der EJB-Container die ejb-
Remove-Methode der EJB aus. Daneben definieren wir in dem Home/Local Home Inter-
face die create-Methoden. Diese lösen ebenfalls die ejbCreate-Methoden aus. Da die
ejbCreate-Methoden überladen sind, müssen die Parameterlisten mit denen der create-
Methoden übereinstimmen. Das Home Interface zur oben beschriebenen Session Bean
WarenkorbBean sieht dann wie folgt aus:

public interface WarenkorbHome extends javax.ejb.EJBHome{

public Warenkorb create(String name)


throws javax.ejb.CreateException,java.rmi.RemoteException;
public Warenkorb create(String name , String nr)
throws javax.ejb.CreateException,java.rmi.RemoteException;
}

Das Local Home Interface ist ähnlich aufgebaut:

package com.entwickler.eclipsebuch.warenkorb;

public interface WarenkorbLocalHome extends javax.ejb.EJBLocalHome


{
public void create(String name)
throws javax.ejb.CreateException;

public void create(String name, String nr)


throws javax.ejb.CreateException;
}

Remote/Local Interface
Vom Typ des Remote/Local Interface wird clientseitig mithilfe der create-Methode eine
Referenz auf eine EJB erzeugt. Das Interface beinhaltet die Deklaration der Geschäfts-
methoden des EJB und ist abgeleitet von dem Interface EJBObject/EJBLocalObject.
Damit steht dem Client eine Referenz bzw. ein Proxy-Objekt, über die die Methoden auf-
gerufen werden können, zur Verfügung.
Das zur Session Bean WarenkorbBean zugehörige Remote Interface lautet:

package com.entwickler.eclipsebuch.warenkorb;

public interface Warenkorb extends javax.ejb.EJBObject {


public void hinzufuegen(String artikel)
throws java.rmi.RemoteException;

309
eclipse_v01.book Seite 310 Montag, 30. Januar 2006 12:02 12

Session Beans

public void entfernen(String artikel)


throws java.rmi.RemoteException;
public java.util.Vector<String> getInhalt()
throws java.rmi.RemoteException;
}

Das Local Interface ist ähnlich wie das Remote Interface aufgebaut. Die throws-Klausel
fehlt, da beim lokalen Zugriff die java.rmi.RemoteException nicht ausgelöst wird.

package com.entwickler.eclipsebuch.warenkorb;

public interface WarenkorbLocal extends javax.ejb.EJBObject {


public void hinzufuegen(String artikel);
public void entfernen(String artikel);
public java.util.Vector<String> getInhalt();
}

7.5.3 Deployment-Deskriptor
Beim Deployment werden die notwendigen Java-Dateien (Klasse WarenkorbBean und
Interfaces) zusammen mit dem Deployment-Deskriptor an den Applikationsserver übertra-
gen. Für das Deployment auf den Applikationsserver JBoss, den wir hier ausschließlich
verwenden, werden die beiden XML-Dateien ejb-jar.xml und jboss.xml benötigt. Die Datei
ejb-jar.xml ist (nach dem EJB 2.0-Standard) folgendermaßen aufgebaut:

<?xml version="1.0" encoding="UTF-8"?>


<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.
//DTD Enterprise JavaBeans 2.0//EN"
"http://java.sun.com/dtd/ejb-jar_2_0.dtd">

Die erste Zeile identifiziert die Datei als eine XML-Datei, die folgenden Zeilen geben den
EJB-Standard an und beschreiben das XML-Dokument mithilfe einer DTD (siehe Kapitel
7.1.2).
Der Rumpf der Datei ist eingebettet in das Root-Element <ejb-jar> … </ejb-jar>. In-
nerhalb dieses Elements können verschiedene Tags verwendet werden, um die Klassen der
EJBs festzulegen, Zugriffsrechte oder Transaktionsverhalten zu verwalten etc. Die wich-
tigsten Tags sind:
쐌 <description> (optional)
Innerhalb dieses Elements kann eine Beschreibung des Deskriptors angegeben werden.
Häufig ist es hilfreich, die EJBs, deren Zugriffbeschränkungen oder das Transaktions-
verhalten zu beschreiben.
쐌 <display-name> (optional)
Der Display-Name wird von Tools verwendet, die Deployment-Deskriptoren erzeugen.
Dort werden Namen für das gesamte Java-Archiv oder für einzelne Beans vergeben, die
nicht mit den Klassen- bzw. Dateinamen übereinstimmen müssen.

310
eclipse_v01.book Seite 311 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

쐌 <enterprise-beans> (Die Angabe von mindestens einer EJB ist notwendig.)


Hier werden die EJBs angegeben, die in der jar-Datei zum Deskriptor vorhanden sein
müssen. Innerhalb dieses Elements werden die Tags <entity>, <session> oder
<message-driven> verwendet, um die speziellen EJBs festzulegen.
쐌 <assembly-descriptor> (optional)
Hier wird definiert, wie eine EJB in einer Anwendung verwendet wird. Es werden Rol-
len, Zugriffsrechte für einzelne Methoden angegeben oder das Transaktionsverhalten
definiert.
Der Rumpf des Deployment-Deskriptors sieht für das Warenkorb-Beispiel mit der einzigen
EJB WarenkorbBean wie folgt aus.

<ejb-jar >
<description>Deployment-Deskriptor Warenkorb</description>
<display-name>Warenkorb</display-name>
<enterprise-beans>
<!-- Session Beans -->
<session >
<ejb-name>Warenkorb</ejb-name>
<home>
com.entwickler.eclipsebuch.warenkorb.WarenkorbHome
</home>
<remote>
com.entwickler.eclipsebuch.warenkorb.Warenkorb
</remote>
<Local Home>
com.entwickler.eclipsebuch.warenkorb.WarenkorbLocalHome
</Local Home>
<local>
com.entwickler.eclipsebuch.warenkorb.WarenkorbLocal
</local>
<ejb-class>
com.entwickler.eclipsebuch.warenkorb.WarenkorbBean
</ejb-class>
<session-type>Stateful</session-type>
<transaction-type>Container</transaction-type>
</session>
</enterprise-beans>

<!-- Assembly Descriptor -->


<assembly-descriptor >
</assembly-descriptor>
</ejb-jar>

Das session-Element enthält die Bezeichner der EJB-Klasse und der Interfaces sowie die
Art der Session Bean (Stateful oder Stateless) und das Transaktionsverhalten. Im Element
<ejb-name> wird der Name bestimmt, der für die interne Verarbeitung erforderlich ist.

311
eclipse_v01.book Seite 312 Montag, 30. Januar 2006 12:02 12

Konfiguration der WTP für einen neuen Server

Als zweiten Schritt müssen wir noch die Datei jboss.xml schreiben. Diese wird vom JBoss
Server benötigt und beinhaltet die JNDI-Namen für die EJBs und für andere Ressourcen,
wie z.B. JDBC oder JavaMail. Je nach Art des Zugriffs (remote oder local) werden zwei
JNDI-Namen unterschieden.
Unser Beispiel besteht bisher nur aus der EJB WarenkorbBean und wir vergeben die
JNDI-Namen für diese:

<?xml version="1.0" encoding="UTF-8"?>


<!DOCTYPE jboss PUBLIC "-//JBoss//DTD JBOSS 3.0//EN" "http://
www.jboss.org/j2ee/dtd/jboss_3_0.dtd">

<jboss>
<unauthenticated-principal>nobody</unauthenticated-principal>
<enterprise-beans>
<session>
<ejb-name>Warenkorb</ejb-name>
<jndi-name>WarenkorbBean</jndi-name>
<local-jndi-name>WarenkorbLocal</local-jndi-name>
</session>
</enterprise-beans>
<resource-managers></resource-managers>
</jboss>

Der Wert des Tags <ejb-name> korrespondiert mit dem Wert des gleichnamigen Tags aus
dem Deployment-Deskriptor ejb-jar.xml. Auf diese Weise wird festgelegt, dass über den
JNDI-Namen WarenkorbBean auf die entsprechende EJB zugegriffen wird. Die Fest-
legung der zu dieser EJB gehörigen Klassen und Interfaces erfolgt im Deployment-Des-
kriptor ejb-jar.xml.
Nachdem wir gesehen haben, welche Klassen, Interfaces und Deployment-Deskriptoren
benötigt werden, ist es jetzt noch erforderlich, die entsprechende Datei auf dem Server zu
installieren. Prinzipiell ist eine Java-Archiv-Datei mit den entsprechenden Dateien zu er-
zeugen und diese in das Deployment-Verzeichnis des JBoss Servers zu kopieren. Wenn der
Server läuft, erkennt er sofort, dass eine neue Komponente vorhanden ist, und installiert
diese automatisch.
Es ist aber äußerst fehleranfällig, alle Schritte des Deployments manuell vorzunehmen, so
dass in der Regel Tools eingesetzt werden, die uns beim Deployment unterstützen. Aus die-
sem Grund wollen wir uns im Folgenden damit beschäftigen, wie wir durch die Eclipse
Web Tools Platform bei der Entwicklung und dem Deployment von EJBs unterstützt wer-
den. Zunächst konfigurieren wir einen neuen Server für die WTP.

7.6 Konfiguration der WTP für einen neuen Server


In Kapitel 7.3.8 haben wir bei der Entwicklung von Webanwendungen gesehen, wie wir die
Web Tools Platform für das Deployment auf den Tomcat Webserver konfigurieren müssen.
Um das Deployment auf einen Server vorzunehmen, der noch nicht vorkonfiguriert ist, be-

312
eclipse_v01.book Seite 313 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

darf es einiger Einstellungen. Diese können wir in den Dateien zu dem Plug-In org.eclip-
se.jst.server.generic.serverdefinitions durchführen oder wir erzeugen ein eigenes Plug-In,
das die notwendigen Informationen enthält.
In unserem Beispiel erzeugen wir ein Plug-In, das die Konfigurationsdaten für den JBoss
Server in der Version 4.0.2 enthält1.
Das Projekt erhält den Namen com.entwickler.eclipsebuch.jboss4 und beinhaltet die Datei-
en plugin.xml und plugin.properties sowie die Unterverzeichnisse
쐌 buildfiles
Dieses Verzeichnis enthält Ant-Skripte für das Deployment/Undeployment.
쐌 icons
Dieses Verzeichnis enthält Icons, die in Eclipse für die Server angezeigt werden kön-
nen.
쐌 META-INF
Dieses Verzeichnis enthält die Manifest.inf-Datei.
쐌 servers
Dieses Verzeichnis enthält die Server-Definitionsdateien, die die Informationen zu den
Servern beinhalten.

7.6.1 Server-Definitionsdatei jboss4.serverdef


Im Unterverzeichnis servers des Plug-In-Projekts com.entwickler.eclipsebuch.jboss4 er-
zeugen wir eine XML-Datei mit dem Namen jboss4.serverdef. Diese enthält die Konfigu-
rationsdaten des JBoss-Applikationsservers der Version 4.0.2. Für welchen Server die Kon-
figuration vorgenommen wird, ist dabei grundsätzlich unerheblich. Die entsprechenden
Daten müssen jeweils für andere Server angepasst werden.

Definition der Namespaces


Der Kopf der Datei jboss4.serverdef ist wie folgt aufgebaut. Hier erfolgt die Festlegung der
Kodierung des XML-Dokuments und der Namespaces:

<?xml version="1.0" encoding="UTF-8"?>


<tns:ServerRuntime
xmlns:tns=
"http://eclipse.org/jst/server/generic/ServerTypeDefinition"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://eclipse.org/jst/server/generic/
ServerTypeDefinition ServerTypeDefinitionSchema.xsd"
name="JBOSS 4.0.2" version="v4.0.2">

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel07/Kap70701WTPKonfiguration

313
eclipse_v01.book Seite 314 Montag, 30. Januar 2006 12:02 12

Konfiguration der WTP für einen neuen Server

Properties
Anschließend werden die so genannten Properties festgelegt. Dies geschieht nach folgen-
dem Schema:

<property id="serverRootDirectory"
label="%ApplicationServerDirectory"
type="directory"
context="runtime"
default="C:\Programme\jboss-4.0.2"
/>

Properties sind Variablen, die aus Eclipse heraus über die grafische Benutzeroberfläche
veränderbar sind. Es existieren verschiedene Attribute, die eingestellt werden können:
쐌 Die id gibt einen eindeutigen Bezeichner im Namespace tns (this namespace) an.
쐌 Der Parameter label ist die Beschriftung, die auf der GUI ausgegeben wird. Der Wert
%ApplicationServerDirectory wird in der Datei plugin-properties aus dem Ver-
zeichnis des neuen Plug-In aufgelöst.
쐌 In type wird der Typ des Werts für die Property definiert. Folgende Angaben sind mög-
lich:
쐌 string
In der GUI wird ein Textfeld für die Property angezeigt.
쐌 directory
In der GUI wird ein Feld für die Property angezeigt, von der aus ein Dateiverzeichnis-
Dialogfenster aufrufbar ist.
쐌 boolean
In der GUI wird eine Checkbox für die Property angezeigt.
쐌 file
In der GUI wird ein Feld für die Property angezeigt, von der aus ein Datei-Dialogfenster
aufrufbar ist.
쐌 Als Einstellung für den context einer Property gibt es die beiden Möglichkeiten ser-
ver und runtime. Die Einstellung runtime bewirkt, dass die Property für den Server
global gesetzt wird. Das heißt, dass z.B. das Verzeichnis für den Server gesetzt wird und
alle Instanzen des Servers dieses benutzen. Ist für eine Property dieses Attribut mit
server angegeben, so wird der Wert für eine Server-Instanz verwendet. Beispielsweise
wird die Server-Adresse oder der Port für jede Instanz angegeben.
쐌 Im Attribut default wird der Initialwert für die Property gesetzt.
Mit Hilfe von ${...} – z.B. ${serverRootDirectory} – können wir auf den Inhalt der Proper-
ty an anderen Stellen des Dokuments zugreifen.
Die Properties werden in der Datei jboss4.serverdef wie folgt definiert:

314
eclipse_v01.book Seite 315 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

<property id="serverRootDirectory"
label="%ApplicationServerDirectory"
type="directory"
context="runtime"
default="C:\Programme\jboss-4.0.2" />
<property id="serverAddress"
label="%serverAddress"
type="string"
context="server"
default="127.0.0.1" />
<property id="port"
label="%serverPort"
type="string"
context="server"
default="8080" />
<property id="serverConfig"
label="%jboss4serverConfig"
type="string"
context="server"
default="default" />
<property id="classPath"
label="%serverclassPath"
type="directory"
context="runtime"
default="C:\Programme\jboss-4.0.2" />

Mit diesen Properties werden das Verzeichnis der JBoss-Installation und eine Classpath-
Variable dafür definiert (Properties serverRootDirectory und classPath). Weil der
JBoss Server in unterschiedlichen Konfigurationen gestartet werden kann, gibt es hierfür
noch eine entsprechende Eigenschaft (Property serverConfig). Abhängig von der Start-
konfiguration des JBoss Servers werden bestimmte Dienste geladen oder nicht. Wir werden
mit der Konfiguration default arbeiten, die für unsere Belange notwendig und auch ausrei-
chend ist. Schließlich werden noch zwei Eigenschaften für die Server-Adresse und den Port
festgelegt (Properties serverAdress und serverPort)

Classpath
Die Definition des Classpath ist ein wichtiger Bestandteil der Server-Definition. Hier wird
festgelegt, welche Bibliotheken für die Entwicklung und für den Ablauf einer J2EE-An-
wendung notwendig sind.
Bei der Entwicklung einer EJB oder eines Servlets werden Bibliotheken benötigt, damit
Eclipse bzw. das JDK die entsprechenden Klassen kennt. Damit diese Bibliotheken stan-
dardmäßig in ein Eclipse-Projekt eingebunden werden, wenn wir ein entsprechendes Pro-
jekt erzeugen, müssen wir bestimmte Angaben in der Datei JBoss4.serverdef machen:

315
eclipse_v01.book Seite 316 Montag, 30. Januar 2006 12:02 12

Konfiguration der WTP für einen neuen Server

<classpath id="jboss.project" >


<archive path="${classPath}/client/jbossall-client.jar" />
<archive path=
"${classPath}/server/default/lib/javax.servlet.jar" />
</classpath>

Bei der Definition des Classpath gibt die id an, über welchen eindeutigen Namen dieser
Classpath referenziert wird. Das Element archive path legt die einzelnen Bibliotheken
fest, die in den Classpath eingebunden werden. Hierbei wird auf die Variable classPath,
die als Property definiert ist, verwiesen.
Dass es sich bei dem Classpath jboss.project um den Classpath handelt, den das
Eclipse-Projekt verwendet, wird dadurch erreicht, dass wir innerhalb des Elements pro-
ject für das Element classpathReference den Wert jboss.project angeben:

<project>
<classpathReference>jboss.project</classpathReference>
</project>

Für den Ablauf einer J2EE-Anwendung bzw. der EJBs innerhalb des JBoss Servers ist ein
weiterer Classpath notwendig:

<classpath id="jboss" >


<archive path="${javaHome}/lib/tools.jar"/>
<archive path="${javaHome}/jre/lib"/>
<archive path="${classPath}/server/${serverConfig}/conf"/>
<archive path="${classPath}/bin/run.jar" />
<archive path=
"${classPath}/server/${serverConfig}/lib/log4j.jar"/>
</classpath>

Angaben zum Starten und Stoppen des JBoss Servers


Zum Starten und Stoppen des JBoss Servers existieren im bin-Verzeichnis die beiden
Bibliotheken run.jar bzw. shutdown.jar. In run.jar gibt es die Klasse org.jboss.Main,
die zum Starten des Servers dient. Dabei kann beispielsweise mit –c die Startkonfiguration
(z.B. –c default) übergeben werden.
In der shutdown.jar dient die Klasse org.jboss.Shutdown zum Stoppen des Servers.
Dabei ist das Argument –S zu übergeben.
In der Konfigurationsdatei sind die beiden Elemente start und stop dazu da, diese An-
gaben zu speichern. Folgende Elemente stehen für die Konfiguration zur Verfügung:
쐌 mainClass: Klasse für das Starten bzw. Stoppen des Servers
쐌 workingDirectory: Verzeichnis der jar-Dateien run.jar bzw. shutdown.jar
쐌 programArguments: Argumente für den Aufruf der Programme
쐌 vmParameters: Parameter für die Virtuelle Maschine

316
eclipse_v01.book Seite 317 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

쐌 classpathReference: verwendeter Classpath


쐌 debugPort: Port, den der Eclipse Debugger im Debug-Modus verwendet
쐌 external: Anwendung oder Skript, auf die bzw. das verwiesen wird
In der Datei jboss4.serverdef wird das Element wie folgt definiert:

<start>
<mainClass>org.jboss.Main</mainClass>
<workingDirectory>${serverRootDirectory}/bin</workingDirectory>
<programArguments>-c ${serverConfig}</programArguments>
<vmParameters></vmParameters>
<classpathReference>jboss</classpathReference>
</start>

<stop>
<mainClass>org.jboss.Shutdown</mainClass>
<workingDirectory>${serverRootDirectory}/bin</workingDirectory>
<programArguments>-S</programArguments>
<vmParameters></vmParameters>
<classpathReference>jboss</classpathReference>
</stop

Publishers
Ant-Skript im Verzeichnis buildfiles
Beim Deployment einer J2EE-Anwendung wird aus den verwendeten Dateien ein jar-, ear-
oder war-Archiv erzeugt und dieses dann in ein spezielles Server-Verzeichnis kopiert.
In einem so genannten Build File mit dem Namen jboss4.xml im Verzeichnis buildfiles wer-
den diese Schritte zusammengefasst:

<target name="deploy.j2ee.ejb">
<jar destfile="${project.working.dir}/${module.name}.jar">
<zipfileset dir="${module.dir}">
<include name="**/*.*"/>
<exclude name="**/*.java"/>
</zipfileset>
</jar>
<move file="${project.working.dir}/${module.name}.jar"
todir="${server.publish.dir}"/>
</target>

Wird beispielsweise dieses Target deploy.j2ee.ejb aufgerufen, dann wird eine jar-Da-
tei mit dem Namen des EJB-Moduls und der Endung .jar erzeugt – das EJB-Modul wird bei
der Entwicklung des Eclipse-Projekts festgelegt. In dieser jar-Datei werden alle Dateien
des Verzeichnisses des EJB-Moduls bis auf die mit der Endung .java kopiert. Anschließend
wird die neu erzeugte jar-Datei in das für das Deployment vorgesehene Verzeichnis des
Servers verschoben.

317
eclipse_v01.book Seite 318 Montag, 30. Januar 2006 12:02 12

Konfiguration der WTP für einen neuen Server

Diese so genannten Ant-Skripte können für unterschiedliche Aufgaben geschrieben und


aus Eclipse heraus gestartet werden. Für das Deployment und das Undeployment von EJBs,
Web- und Enterprise-Anwendungen auf den JBoss Server wird im Unterverzeichnis build-
files die Datei jboss4.xml definiert:

<project name="deployextension" default="deploy.j2ee.web"


basedir=".">
<target name="deploy.j2ee.web">
<jar destfile="${project.working.dir}/${module.name}.war">
<zipfileset dir="${module.dir}">
<include name="**/*.*"/>
<exclude name="**/*.war"/>
</zipfileset>
</jar>
<move file="${project.working.dir}/${module.name}.war"
todir="${server.publish.dir}"/>
</target>
<target name="deploy.j2ee.ejb"> ... </target>
<target name="deploy.j2ee.ear"> ... </target>

<target name="undeploy.j2ee.web">
<delete file="${server.publish.dir}/${module.name}.war">
</delete>
</target>
<target name="undeploy.j2ee.ejb"> ... </target>
<target name="undeploy.j2ee.ear"> ... </target>
</project>

Ant Publisher des Plug-In org.eclipse.jst.server.generic.core


In der Datei jboss4.serverdef werden die so genannten Publisher definiert. Diese verweisen
auf die Build Files (z.B. buildfiles/jboss4.xml) bzw. auf die darin enthaltenen Targets (z.B.
deploy.j2ee.web). Auf diese Weise wird der Aufruf der jeweiligen Ant-Skripte von der
Eclipse Web Tools Platform aus gesteuert. Diese Steuerung übernimmt das Plug-In org.
eclipse.jst.server.generic.core, welches den Extension Point genericpublisher besitzt.
Für den ist wiederum org.eclipse.jst.server.generic.antpublisher als ID für
diesen Extension Point angegeben. Dieser verwendet die Klasse AntPublisher aus dem
Package org.eclipse.jst.server.generic.core.internal, die die Verarbeitung
der Ant-Skripte zum Deployment und Undeployment übernimmt.1
In der plugin.xml des Plug-In org.eclipse.jst.server.generic.core erfolgt die Festlegung in
den Zeilen:

1. Für detaillierte Erläuterungen zu Plug-Ins und den Begriffen Extension Point und Extension siehe Kapitel 9.

318
eclipse_v01.book Seite 319 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

...
<extension point=
"org.eclipse.jst.server.generic.core.genericpublisher">
<genericpublisher class=
"org.eclipse.jst.server.generic.core.internal.AntPublisher"
id="org.eclipse.jst.server.generic.antpublisher"/> </
extension>
...

In diesem Plug-In (org.eclipse.jst.server.generic.core) müssen wir keine Änderungen vor-


nehmen. Wir lesen hier nur die ID ab, die wir im Folgenden für die Definition der Publisher
in der Server-Definitionsdatei jboss4.serverdef benötigen.
Angabe des Ant Publisher im Element publisher von jboss4.serverdef
In der jboss4.serverdef wird das Element publisher definiert. Hier wird als id der Exten-
sion Point genericpublisher des Plug-In org.eclipse.jst.server.generic.core verwendet.
Weiterhin erfolgt die Angabe, wo das Build File mit den Ant-Skripten liegt. Anschließend
werden den Befehlen des Ant Publisher die Bezeichnungen der Targets des Ant-Skripts zu-
geordnet.

<publisher id="org.eclipse.jst.server.generic.antpublisher">
<publisherdata>
<dataname>build.file</dataname>
<datavalue>/buildfiles/jboss4.xml</datavalue>
</publisherdata>
<publisherdata>
<dataname>target.publish.j2ee.web</dataname>
<datavalue>deploy.j2ee.web</datavalue>
</publisherdata>
<publisherdata>
<dataname>target.publish.j2ee.ejb</dataname>
<datavalue>deploy.j2ee.ejb</datavalue>
</publisherdata>
<publisherdata>
<dataname>target.unpublish.j2ee.web</dataname>
<datavalue>undeploy.j2ee.web</datavalue>
</publisherdata>
<publisherdata>
<dataname>target.unpublish.j2ee.ejb</dataname>
<datavalue>undeploy.j2ee.ejb</datavalue>
</publisherdata>
<publisherdata>
<dataname>target.publish.j2ee.ear</dataname>
<datavalue>deploy.j2ee.ear</datavalue>
</publisherdata>

319
eclipse_v01.book Seite 320 Montag, 30. Januar 2006 12:02 12

Konfiguration der WTP für einen neuen Server

<publisherdata>
<dataname>target.unpublish.j2ee.ear</dataname>
<datavalue>undeploy.j2ee.ear</datavalue>
</publisherdata>
</publisher>

Module
Das Element module gibt die Modultypen an. Es gibt Web-, EJB- und Enterprise Appli-
cation-Module, die für das Plug-In org.eclipse.jst.server.generic.serverdefinitions in der
plugin.xml mit j2ee.web, j2ee.ejb, j2ee.ear definiert werden. Folgende Angaben
werden für jedes Modul gemacht:
쐌 type: Angabe des Modultypen, der in der plugin.xml definiert sein muss
쐌 publishDir: das Server-Verzeichnis, auf das die Module kopiert werden
쐌 publisherReference: die id des Generic Publisher, der für dieses Modul benutzt
werden soll

<module>
<type>j2ee.web</type>
<publishDir>
${serverRootDirectory}/server/${serverConfig}/deploy
</publishDir>
<publisherReference>
org.eclipse.jst.server.generic.antpublisher
</publisherReference>
</module>
<module>
<type>j2ee.ejb</type>
<publishDir>
${serverRootDirectory}/server/${serverConfig}/deploy
</publishDir>
<publisherReference>
org.eclipse.jst.server.generic.antpublisher
</publisherReference>
</module>
<module>
<type>j2ee.ear</type>
<publishDir>
${serverRootDirectory}/server/${serverConfig}/deploy
</publishDir>
<publisherReference>
org.eclipse.jst.server.generic.antpublisher
</publisherReference>
</module>

320
eclipse_v01.book Seite 321 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

Ports
Das Port-Element gibt den Port und die Protokolle für den Server an. Die Werte werden ver-
wendet, um den Server zu identifizieren. Folgende Elemente gibt es:
쐌 no: Port-Nummer
쐌 protocol: Bezeichner des Protokolls, das unterstützt wird
쐌 name: Name des Protokolls

<port>
<no>${port}</no>
<name>Http</name>
<protocol>http</protocol>
</port>

JNDI Properties
Die Daten für die JNDI-Verbindung werden in dem Element jndiConnection festgelegt.
Sie werden von Clients für den Zugriff auf den Applikationsserver benötigt. Folgende Ele-
mente stehen zur Verfügung:
쐌 providerUrl: die URL des JNDI Servers
쐌 initialContextFactory: Name der Initial Context Factory-Klasse für die JNDI-
Implementation des Servers
쐌 jndiProperty: Eigenschaften für die JNDI-Verbindung, wie z.B. Username, Pass-
wort etc.

<jndiConnection>
<providerUrl>jnp://${serverAddress}:1099</providerUrl>
<initialContextFactory>
org.jnp.interfaces.NamingContextFactory
</initialContextFactory>
<jndiProperty>
<name></name>
<value></value>
</jndiProperty>
</jndiConnection>

7.6.2 7.6.2 Konfiguration der plugin.xml


Neben der Definition des XML-Dokuments jboss4.serverdef und des Build Files
jboss4.xml müssen zusätzlich noch Einstellungen in der plugin.xml des Plug-Ins gemacht
werden. Dabei sind die folgenden Elemente zu definieren:
쐌 Extension Point org.eclipse.wst.server.core.runtimeTypes, Element runtimeType
In diesem Bereich werden allgemeine Angaben für den Server gemacht, z.B. Name und
Beschreibung für die Ausgabe in Eclipse oder die Gruppe, unter der der Server ange-
zeigt wird (JBoss). Außerdem wird festgelegt, für welche Art von Modulen der Server
verwendet werden kann:

321
eclipse_v01.book Seite 322 Montag, 30. Januar 2006 12:02 12

Konfiguration der WTP für einen neuen Server

<extension point="org.eclipse.wst.server.core.runtimeTypes">
<runtimeType
id="org.eclipse.jst.server.generic.runtime.jboss40"
name="%jboss40runtimeTypeName"
description="%jboss40runtimeTypeDescription"
vendor="%jboss40Category"
version="4.0"
class="org.eclipse.jst.server.generic.
core.internal.GenericServerRuntime"
>
<moduleType types="jst.web" versions="2.2, 2.3, 2.4"/>
<moduleType types="jst.ejb" versions="1.1, 2.0, 2.1"/>
<moduleType types="jst.ear" versions="1.2, 1.3, 1.4"/>
<moduleType types="jst.connector" versions="1.0, 1.5"/>
<moduleType types="jst.utility" versions="1.0"/>
<moduleType types="jst.appclient" versions="1.2,1.3,1.4"/>
</runtimeType>
</extension>

쐌 Extension Point org.eclipse.wst.server.core.serverTypes, Element serverType


In diesem Bereich werden spezielle Angaben für eine Instanz eines Servers gemacht,
Startkonfiguration, Timeout-Variablen etc.:

<extension point="org.eclipse.wst.server.core.serverTypes">
<serverType
runtime="true"
class="org.eclipse.jst.server.generic.
core.internal.GenericServer"
id="org.eclipse.jst.server.generic.jboss40"
initialState="stopped"
supportsRemoteHosts="false"
runtimeTypeId=
"org.eclipse.jst.server.generic.runtime.jboss40"
description="%jboss40serverTypeDescription"
launchConfigId="org.eclipse.jst.server.generic.
core.launchConfigurationType"
behaviourClass="org.eclipse.jst.server.generic.
core.internal.GenericServerBehaviour"
name="%jboss40serverTypeName"
startTimeout="50000"
stopTimeout="15000"
hasConfiguration="false"
launchModes="run,debug,profile">
</serverType>
</extension>

322
eclipse_v01.book Seite 323 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

쐌 Extension Point org.eclipse.wst.server.ui.serverImages, Element image


In diesem Bereich werden die Dateinamen der Icons referenziert:

<extension point="org.eclipse.wst.server.ui.serverImages">
<image
id="org.eclipse.jst.server.generic.image"
icon="icons/obj16/jboss.gif"
typeIds="org.eclipse.jst.server.generic.runtime.jboss40"/>
<image
id="org.eclipse.jst.server.generic.image"
icon="icons/obj16/jboss.gif"
typeIds="org.eclipse.jst.server.generic.jboss40"/>
</extension>

쐌 Extension Point org.eclipse.wst.server.ui.wizardFragments, Element fragment


Die Angabe des Wizards in dem Element fragment führt dazu, dass der entsprechende
Server bei der Auswahl der Server mit dem angegebenen Icon angezeigt wird (siehe
Abb. 7.20):

<extension point="org.eclipse.wst.server.ui.wizardFragments">
<fragment
id="org.eclipse.jst.server.generic.runtime"
typeIds="org.eclipse.jst.server.generic.runtime.jboss40"
class="org.eclipse.jst.server. generic.
ui.internal.GenericServerRuntimeWizardFragment"/>
<fragment
id="org.eclipse.jst.server.generic.server"
typeIds="org.eclipse.jst.server.generic.jboss40"
class="org.eclipse.jst.server.generic.
ui.internal.GenericServerWizardFragment"/>
</extension>

쐌 Extension Point org.eclipse.jst.server.generic.core.serverdefinition,


Element serverdefinition
Angabe des Ortes der Serverdefinitionsdatei jboss4.serverdef:

<extension point=
"org.eclipse.jst.server.generic.core.serverdefinition">
<serverdefinition
id="org.eclipse.jst.server.generic.runtime.jboss40"
definitionfile="/servers/jboss40.serverdef">
</serverdefinition>
</extension>

323
eclipse_v01.book Seite 324 Montag, 30. Januar 2006 12:02 12

Konfiguration der WTP für einen neuen Server

쐌 Extension Point org.eclipse.wst.common.project.facet.core.runtimes


Hier werden die von dem Server unterstützten Versionen für Web-, J2EE-Anwendun-
gen etc. angegeben:

<extension point=
"org.eclipse.wst.common.project.facet.core.runtimes">
<runtime-component-type
id="org.eclipse.jst.server.generic.runtime.jboss"/>
<runtime-component-version
type="org.eclipse.jst.server.generic.runtime.jboss"
version="4.0"/>
<adapter>
<runtime-component
id="org.eclipse.jst.server.generic.runtime.jboss"/>
<factory
class="org.eclipse.jst.server.core.
internal.RuntimeClasspathProvider$Factory"/>
<type
class="org.eclipse.jst.common.project.
facet.core.IClasspathProvider"/>
</adapter>
<supported>
<runtime-component
id="org.eclipse.jst.server.generic.runtime.jboss"
version="4.0"/>
<facet id="jst.web" version="2.2,2.3,2.4"/>
<facet id="jst.ejb" version="1.1,2.0,2.1"/>
<facet id="jst.ear" version="1.2,1.3,1.4"/>
<facet id="jst.connector" version="1.0,1.5"/>
<facet id="jst.utility" version="1.0"/>
<facet id="jst.appclient" version="1.2,1.3,1.4"/>
</supported>
</extension>

쐌 Extension Point org.eclipse.jst.server.core.runtimeFacetMappings


Die Angabe ist notwendig, damit der Server im Assistenten zur Erstellung eines neuen
EJB-Projekts erscheint:

<extension point=
"org.eclipse.jst.server.core.runtimeFacetMappings">
<runtimeFacetMapping
runtimeTypeId=
"org.eclipse.jst.server.generic.runtime.jboss40"
runtime-component=
"org.eclipse.jst.server.generic.runtime.jboss"
version="4.0"/>
</extension>

324
eclipse_v01.book Seite 325 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

쐌 Extension Point org.eclipse.wst.common.project.facet.ui.images


Angabe des Icon im Projekterstellungsassistenten:

<extension point=
"org.eclipse.wst.common.project.facet.ui.images">
<image runtime-component-type=
"org.eclipse.jst.server.generic.runtime.jboss"
path="icons/obj16/jboss.gif"/>
</extension>

Abb. 7.20: Assistent zur Auswahl einer Server-Instanz (GenericServerWizardFragment)

7.6.3 Konfiguration der plugin.properties


Da in den Dateien plugin.xml und jboss4.serverdef an vielen Stellen die Beschriftungstexte
nicht direkt, sondern mithilfe von Variablen definiert worden sind, müssen wir in der Datei
plugin.properties noch die Werte für diese Variablen setzen.
Dies erfolgt durch Zuordnungen von Beschriftungstexten zu den Variablen:

jboss4runtimeTypeName=JBoss 4.0.x
jboss4runtimeTypeDescription=JBoss 4.0.x
jboss4serverTypeName=JBoss 4.0.x
jboss4serverTypeDescription=JBoss 4.0.x
jboss4serverConfig=Server Co&nfiguration (minimal/default/all):

325
eclipse_v01.book Seite 326 Montag, 30. Januar 2006 12:02 12

Erstellung eines EJB-Projekts

Damit ist das Plug-In aufgebaut, wie es in Abb. 7.21 dargestellt ist. Für das Deployment ge-
nügt es, das Verzeichnis in das plugins-Verzeichnis von Eclipse zu kopieren und Eclipse
neu zu starten.
Um zu gewährleisten, dass die Änderungen wirksam und keine älteren Informationen aus
der plugin.xml von Eclipse gespeichert werden, sollte Eclipse mit der Startkonfiguration
-clean(eclipse.exe -clean) gestartet werden.

Abb. 7.21: Aufbau des Plug-In zur Konfiguration des JBoss Server 4

7.7 Erstellung eines EJB-Projekts

7.7.1 Anlegen des Projekts


Nach der Konfiguration des Plug-In für den JBoss Server erstellen wir ein neues Projekt
und gehen dabei wie folgt vor1:
쐌 Auswahl von FILE | NEW PROJECT und EJB à EJB PROJECT
쐌 Festlegen des Projektnamens Warenkorb
쐌 Festlegen der TARGET RUNTIME mit JBoss 4.0.x. Falls die Runtime nicht zur Auswahl
steht, muss durch Klick auf NEW die entsprechende Runtime erstellt werden. Dazu wer-
den der Applikationsserver und sein Installationsverzeichnis ausgewählt.
쐌 Als SOURCE FOLDER geben wir ejbModule an.
Nach Beendigung des Assistenten wird ein neues Projekt erzeugt, welches neben den J2SE-
Bibliotheken noch Bibliotheken aus dem Verzeichnis des JBoss Servers einbindet. Dabei
handelt es sich um die Bibliotheken, die in der jbsoo4.serverdef-Datei als Classpath mit der
ID jboss.project definiert worden sind.
Außerdem gibt es das Verzeichnis ejbModule. In diesem Verzeichnis werden die EJB mit
ihren zugehörigen Interfaces gespeichert. Im Unterverzeichnis META-INF steht beispiels-
weise der Deployment-Deskriptor ejb-jar.xml.

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel07/Kap70701Warenkorb

326
eclipse_v01.book Seite 327 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

7.7.2 Erzeugen des EJB


Zum Erzeugen einer EJB-Klasse wählen wir FILE | NEW | OTHER und EJB à XDOCLET EN-
TERPRISE JAVABEAN. Auf der zweiten Maske wählen wir die Art der Bean aus (Session Bean)
und geben als ANNOTATION PROVIDER XDoclet an. Durch Klick auf den Link PREFERENCES
kann das Installationsverzeichnis von XDoclet angegeben werden. Dabei sollte auch bei
EJBDOCLET der Punkt JBOSS angekreuzt werden.

Auf der folgenden Maske (siehe Abb. 7.22) werden unter anderem der Name des Package
und der Name der Klasse festgelegt. Bei dem Namen der Klasse wird üblicherweise das
Suffix Bean mit angegeben und es muss an dieser Stelle verwendet werden.

Abb. 7.22: Erstellen einer Session Bean

Wir können ein Package auswählen oder ein neues definieren. Wir bezeichnen das Package
mit com.entwickler.eclipsebuch.warenkorb und die Klasse mit WarenkorbBean.
Anschließend sind der EJB-, der JNDI- und der Display-Name zu bestimmen. Diese
Namen sind frei wählbar. Standardmäßig wird der Name der Bean ohne das Suffix Bean
vorgegeben. Außerdem ist auf dieser Maske festzulegen, ob es sich um eine Stateful oder
Stateless Session Bean handelt und ob der EJB-Container oder die Bean selbst die Trans-
aktionsverarbeitung übernehmen soll.
Auf der letzten Maske des Assistenten wird noch das zu implementierende Interface
javax.ejb.SessionBean ausgewählt und die Klasse als abstract definiert, was den
Vorteil hat, dass nicht alle Methoden des Interface in der Bean-Klasse implementiert wer-
den müssen.

327
eclipse_v01.book Seite 328 Montag, 30. Januar 2006 12:02 12

Erstellung eines EJB-Projekts

7.7.3 Bean-Klasse und XDoclet


In der neu erzeugten Klasse steht zu Beginn ein XDoclet-Bereich. Dieser beinhaltet die
Informationen für den Deployment-Deskriptor und für die Generierung der weiteren not-
wendigen Klassen und Interfaces:

/**
*
* <!-- begin-user-doc -->
* A generated session bean
* <!-- end-user-doc -->
* *
* <!-- begin-xdoclet-definition -->
* @ejb.bean name="Warenkorb"
* description="A session bean named Warenkorb"
* display-name="Warenkorb"
* jndi-name="Warenkorb"
* type="Stateful"
* transaction-type="Container"
*
* <!-- end-xdoclet-definition -->
* @generated
*/

Hier werden der EJB-Name, der JNDI- und der Display-Name sowie die Art des Bean
(Stateful) und die Variante der Transaktionsverarbeitung (Container) festgelegt. Die-
ser Bereich wird von XDoclet bei der Erzeugung des Deployment-Deskriptors etc. ver-
arbeitet. XDoclet startet automatisch, wenn ein neuer Build durchgeführt wird und die
Bean verändert worden ist. XDoclet überschreibt die vorherigen Dateien für Deployment-
Deskriptor etc., so dass in diesen Dateien keine manuellen Änderungen vorgenommen
werden sollten. Beim Arbeiten mit der WTP sollten alle Einstellungen für die Bean im
XDoclet-Bereich vorgenommen werden.

7.7.4 Einfügen einer neuen Methode in die Bean-Klasse


Zum Erzeugen von Methoden gibt es in der aktuellen Version der WTP keinen Assistenten.
Vor die Methode müssen wir daher den XDoclet-Bereich selber schreiben oder von einer
anderen Methode kopieren.
In der Klasse WarenkorbBean existieren nach dem Erzeugen der Klasse die beiden Me-
thoden ejbCreate und foo. Vor der Methode ejbCreate steht im XDoclet-Bereich:

328
eclipse_v01.book Seite 329 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

/**
*
* <!-- begin-xdoclet-definition -->
* @ejb.create-method
* view-type="remote"
* <!-- end-xdoclet-definition -->
* @generated
*
* //TODO: Must provide implementation for bean create stub
*/

Mit dem Ausdruck @ejb.create-method wird bestimmt, dass es sich um eine create-
Methode handelt, die aufgrund der Angabe view-type="remote" ausschließlich für den
Remote-Zugriff verwendet werden kann. Das bedeutet, dass die entsprechende create-
Methode im Remote Home Interface und nicht im Local Home Interface erzeugt wird. Soll
die Methode für beide Zugriffsarten gelten, ist view-type="both" einzustellen.
Bei der Methode foo handelt es ich um eine Methode für das Remote/Local Interface. Von
der WTP wird dafür der Ausdruck ejb.interface-method verwendet. Die Einstellung
des view–type legt fest, ob die Methode für das Remote, Local oder für beide Interfaces
gelten soll.
In unserem Beispiel benötigen wir zwei create-Methoden, die eine besitzt zwei und die
andere einen Parameter vom Typ String. Beim ersten handelt es sich jeweils um den Namen
des Kunden und der zweite ist gegebenenfalls die Kundennummer. Wird diese nicht mit
angegeben, wird sie mit 0 festgelegt. Der XDoclet-Bereich ist identisch zu dem oben ange-
gebenen für eine create-Methode. Im XDoclet-Bereich werden also keine spezifischen
Daten für eine spezielle Methode mit angegeben:

/**

*/
public void ejbCreate(String name, String nr)
throws CreateException{
kName = name;
kNr = nr;
}
/**

*/
public void ejbCreate(String name) throws CreateException{
this.ejbCreate(name,"0");
}

329
eclipse_v01.book Seite 330 Montag, 30. Januar 2006 12:02 12

Erstellung eines EJB-Projekts

Weiterhin werden in diesem Beispiel drei so genannte Business-Methoden programmiert,


die zum Hinzufügen bzw. Entfernen eines Artikels aus dem Warenkorb sowie zum Ausle-
sen des Inhalts des gesamten Warenkorbs dienen. Die Methoden heißen hinzufuegen,
entfernen und getInhalt. Vor dem Java-Code der Methoden ist wiederum ein XDoc-
let-Bereich erforderlich, damit die Methoden automatisch in das Local und Remote Inter-
face aufgenommen werden. Dieser Bereich enthält keine methodenspezifischen Daten und
ist für jede Methode gleich:

/**
*
* <!-- begin-xdoclet-definition -->
* @ejb.interface-method view-type="both"
* <!-- end-xdoclet-definition -->
* @generated
*
* //TODO: Must provide implementation for bean method stub
*/
public void hinzufuegen(String artikel){
inhalt.addElement(artikel);
}

/**

*/
public void entfernen(String artikel) {
inhalt.removeElement(artikel);
}

/**

*/
public Vector<String> getInhalt() {
return inhalt;
}

Auf Basis dieser Bean-Klasse werden das Remote, Local, Home und Local Home Interface
sowie die Klasse WarenkorbSession erzeugt und in das Verzeichnis ejbModule in das
entsprechende Package kopiert. WarenkorbSession ist von der abstrakten Klasse Wa-
renkorbBean abgeleitet. In ihr werden die Methoden aus dem Interface SessionBean
implementiert. In welcher Klasse wir die Implementierungen der Business- und der ejb-
Create-Methoden vornehmen, ist theoretisch egal. Wir werden jedoch den Programmcode
für die Methoden in die Basisklasse WarenkorbBean schreiben, weil bei jedem neuen Er-
zeugen der Dateien diese im Verzeichnis ejbModule überschrieben werden und Änderun-
gen jeweils verloren gehen. Die in WarenkorbBean implementierten Methoden werden an
die generierte Klasse WarenkorbSession vererbt. Standardmäßig wird die Klasse Wa-
renkorbSession im Deployment-Deskriptor als EJB-Klasse angegeben.

330
eclipse_v01.book Seite 331 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

Deployment
Der Deployment-Deskriptor, der im Verzeichnis ejbModule/META-INF steht und den Na-
men ejb-jar.xml besitzt, wird ebenfalls automatisch erzeugt und beinhaltet das session-
Element mit den Definitionen der EJB-Klasse und der Interfaces:

<!-- Session Beans -->


<session >
<description><![CDATA[]]></description>
<ejb-name>Warenkorb</ejb-name>
<home>com.entwickler.eclipsebuch.warenkorb.WarenkorbHome</home>
<remote>com.entwickler.eclipsebuch.warenkorb.Warenkorb</remote>
<Local Home>
com.entwickler.eclipsebuch.warenkorb.WarenkorbLocalHome
</Local Home>
<local>
com.entwickler.eclipsebuch.warenkorb.WarenkorbLocal
</local>
<ejb-class>
com.entwickler.eclipsebuch.warenkorb.WarenkorbSession
</ejb-class>
<session-type>Stateful</session-type>
<transaction-type>Container</transaction-type>
</session>

Weil wir bei der Erzeugung der EJB-Klasse angegeben haben, dass es sich um eine Stateful
Session Bean handeln soll, und dies im XDoclet-Bereich in der Klasse vermerkt worden ist,
wird an dieser Stelle im Element <session-type> der Wert Stateful angegeben.
Für die Darstellung des Deployment-Deskriptors gibt es noch eine Darstellung in tabella-
rischer Form in der Design View (siehe Abb. 7.23).

Abb. 7.23: Design View des Deployment-Deskriptors ejb-jar.xml

331
eclipse_v01.book Seite 332 Montag, 30. Januar 2006 12:02 12

Client für den Remote-Zugriff auf eine EJB

Beim Deployment auf den JBoss Server werden die JNDI-Namen für die Home-Objekte,
also die Namen, mit denen sie sich beim Namensdienst registrieren, in der Datei jboss.xml
gespeichert. Wir können XDoclet so einstellen, dass auch diese Datei automatisiert erstellt
wird. Hierzu markieren wir unter WINDOW | PREFERENCES  XDOCLET  EJBMODULE den
Eintrag JBoss.
Der JBoss Server kann aus Eclipse heraus gestartet werden. In der Servers View der WTP
muss dazu ein neuer Server hinzugefügt und die Bean veröffentlicht werden.
Anschließend wird der JBoss Server mit der Startkonfiguration default aufgerufen. Dabei
werden in der Konsole des JBoss Servers verschiedene Meldungen angezeigt.

[org.jboss.deployment.MainDeployer] Starting deployment of


package: file:/C:/Programme/jboss-3.2.3/server/default/deploy/
WarenkorbEJB.jar
[org.jboss.ejb.EjbModule] Creating
[org.jboss.ejb.EjbModule] Deploying Warenkorb
...
[org.jboss.ejb.EJBDeployer] Deployed: file:/C:/Programme/jboss-
3.2.3/server/default/deploy/WarenkorbEJB.jar
[org.jboss.deployment.MainDeployer] Deployed package: file:/C:/
Programme/jboss-3.2.3/server/default/deploy/WarenkorbEJB.jar

Einen Überblick über die laufenden Dienste des Servers oder über die zur Verfügung ste-
henden EJBs etc. erhalten wir, indem wir im Browser die URL http://localhost:8080/jmx-
console/ angeben. Klicken wir in der Gruppe jboss.j2ee auf jndiName=Warenkorb-
Bean,service=EJB, so gelangen wir zu einem Dokument, in dem zu erkennen ist, dass die
Bean als Stateful Session Bean zur Verfügung steht und über den JNDI-Namen Waren-
korbBean angesprochen werden kann.
Nachdem wir nun gesehen haben, dass die EJB im Container richtig installiert worden ist,
wollen wir ein Java-Client-Programm entwickeln, das auf die EJB zugreift und deren Funk-
tionalität aufruft.

7.8 Client für den Remote-Zugriff auf eine EJB

7.8.1 Herstellung einer Verbindung zum JBoss-Namensdienst


Der JBoss Server stellt in seinem Naming Service Modul (JBossNS) eine Implementierung
des Java Naming and Directory Interface (JNDI) zur Verfügung. Der Namensdienst nutzt
das JNP-Protokoll (Java Naming Provider), welches standardmäßig auf den Port 1099
horcht. Soll ein Client von außen (remote) auf den Namensdienst zugreifen, so muss eine
Verbindung zu dem Server und dem entsprechenden Port aufgebaut werden.
Für den Verbindungsaufbau benötigt der Client ein Objekt der Klasse InitialContext
aus dem Package javax.naming. Beim Instanziieren eines Objekts dieser Klasse muss
dem Konstruktor ein Hashtable-Objekt übergeben werden, in dem Informationen für den

332
eclipse_v01.book Seite 333 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

Verbindungsaufbau gespeichert sind. Im Hashtable-Objekt werden die Einstellungen un-


ter bestimmten Namen gesetzt. Folgende Eigenschaften können angegeben werden:
쐌 java.naming.factory.initial oder Context.INITIAL_CONTEXT_FACTORY
Diese Eigenschaft spezifiziert den Namen der initial context factory. Der Name der
Klasse lautet org.jnp.interfaces.NamingContextFactory für den JBoss-Na-
mensdienst. Dieser Name wird der Eigenschaft zugeordnet.
쐌 java.naming.provider.url oder Context.PROVIDER_URL
Mit dieser Eigenschaft wird der Ort des JBoss-Namensdienst-Providers angegeben, den
der Client verwendet. Die Klasse NamingContextFactory benötigt diese Informati-
on, um eine Verbindung zum Namensdienst aufzubauen. Der Wert der Eigenschaft ist
eine URL und kann wie folgt angegeben werden:
jnp://<host>:<port>/[<jndi_path>]
Der Ausdruck jnp: bezeichnet das Protokoll. Da JNP standardmäßig verwendet wird,
kann die Angabe entfallen. Der Teil <host> ist die Server-Adresse und kann als IP-
Adresse angegeben werden. Danach folgt der Port, der standardmäßig auf 1099 gesetzt
ist und ebenfalls nicht angegeben werden muss. Der Wert von <jndi_path> ist optional
und beschreibt das Verzeichnis, in dem die EJBs liegen. Zur besseren Strukturierung
unserer Komponenten im Applikationsserver könnten wir beispielsweise festlegen,
dass die JNDI-Namen für die EJB stets mit ejb/… beginnen. Dann würden wir das Ver-
zeichnis ejb als <jndi_path> angeben.
Liegt der Server auf demselben Rechner wie der Client, dann kann die Eigenschaft
Context.PROVIDER_URL den Wert localhost oder aber auch jnp://localhost:1099
annehmen.
쐌 java.naming.factory.url.pkgs oder Context.URL_PKG_PREFIXES
Der Wert dieser Eigenschaft spezifiziert die Liste von Package-Präfixen, die verwendet
werden, wenn die Context Factories geladen werden. Für den JBoss-Namensdienst lau-
ten die Präfixes org.jboss.naming:org.jnp.interfaces.
쐌 jnp.socketFactory
Diese Eigenschaft besitzt als Wert standardmäßig den Namen der Klasse org.jnp.
interfaces.TimedSocketFactory. Diese Klasse ermöglicht die Spezifikation von
Timeout-Daten.
쐌 jnp.timeout
Angabe der Dauer in Millisekunden, nach der der Versuch, eine Verbindung aufzubau-
en, abgebrochen wird. Standardmäßig ist der Wert 0. Das bedeutet aber nicht, dass keine
Zeit abgewartet wird, bis die Verbindung aufgebaut ist. Da die Kommunikation über
TCP/IP läuft, wird der dort eingestellte Timeout verwendet und die Dauer des
jnp.timeout gegebenenfalls dazu addiert.
쐌 jnp.sotimeout
Diese Eigenschaft gibt den Timeout bei der Kommunikation an und besitzt ebenfalls
standardmäßig den Wert 0. Hierbei handelt es sich wiederum um die Dauer, die zusätz-
lich zur Timeout-Dauer des TCP/IP-Protokolls eingestellt ist.

333
eclipse_v01.book Seite 334 Montag, 30. Januar 2006 12:02 12

Client für den Remote-Zugriff auf eine EJB

7.8.2 Beispiel eines Java-Client


Für den Java-Client erzeugen wir in Eclipse ein Java-Projekt1, das eine Klasse (Waren-
korbClient) mit main-Methode enthält. Zum Verbindungsaufbau mit dem JBoss-
Namensdienst schreiben wir eine Methode getContext, in der das dafür benötigte Objekt
der Klasse InitialContext instanziiert wird.

private InitialContext getContext() throws NamingException {


Hashtable props = new Hashtable();
props.put(Context.INITIAL_CONTEXT_FACTORY,
"org.jnp.interfaces.NamingContextFactory");
props.put(Context.PROVIDER_URL, "jnp://localhost:1099");
props.put(Context.URL_PKG_PREFIXES,
"org.jboss.naming:org.jnp.interfaces");
props.put("jnp.socketFactory",
"org.jnp.interfaces.TimedSocketFactory");
props.put("jnp.timeout","0");
props.put("jnp.sotimeout","0");
InitialContext context = new InitialContext(props);
return context;
}

In der Methode wird ein neues Hashtable-Objekt erzeugt, in das die einzelnen Eigen-
schaften mit entsprechenden Werten eingetragen werden. Das Setzen der Eigenschaften
jnp.socketFactory, jnp.timeout und jnp.sotimeout ist nicht unbedingt erforder-
lich, da wir hier Standardwerte verwenden.
Das InitialContext-Objekt stellt eine Verbindung zum Namensdienst des JBoss Ser-
vers her. Über die lookup-Methode des InitialContext-Objekts generieren wir eine
Referenz auf das Home-Objekt. Die lookup-Methode bekommt als String den JNDI-Na-
men der EJB, auf welche wir zugreifen wollen, übergeben und gibt eine Referenz auf das
zugehörige Home-Objekt zurück. Dieses konvertieren wir in ein Objekt vom Typ des Home
Interface. Anschließend können wir eine create-Methode aufrufen, um eine Referenz auf
die EJB zu erhalten. Dazu ist es erforderlich, dass das Home und das Remote Interface im
Client eingebunden sind.
In unserem Beispiel fügen wir dem Projekt die beiden Interfaces Warenkorb und Waren-
korbHome aus dem Package com.entwickler.eclipsebuch.warenkorb, welche wir
im EJB-Projekt generiert haben, hinzu. Anschließend schreiben wir eine Methode get-
Home, in der das Home-Objekt referenziert wird. Der lookup-Methode übergeben wir die
Klassenkonstante JNDI_NAME aus dem Interface WarenkorbHome, in der der JNDI-Name
der EJB (WarenkorbBean) steht.

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel07/Kap70306WarenkorbClient

334
eclipse_v01.book Seite 335 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

Zurückgegeben wird ein Objekt der Klasse Object. Mithilfe der Klassenmethode narrow
aus der Klasse javax.rmi.PortableRemoteObject überprüfen wir, ob das Objekt in
ein Objekt vom Typ WarenkorbHome konvertiert werden kann, und nehmen anschließend
das Konvertieren vor.

private WarenkorbHome getHome() throws NamingException {


Context ctx = getContext();
Object ref = ctx.lookup(WarenkorbHome.JNDI_NAME);
WarenkorbHome wkh = (WarenkorbHome)
PortableRemoteObject.narrow(ref,WarenkorbHome.class);
return wkh;
}

Nachdem wir eine Referenz auf das Home-Objekt erzeugt haben, können wir mithilfe der
create-Methode dieses Objekts eine Referenz auf das eigentliche EJB-Objekt (Waren-
korbBean) generieren.
Wir schreiben den Programmcode zum Testen der Funktionalität in die Methode testBean:

public void testBean() {


try {
Warenkorb myBean = getHome().create("Meier");
myBean.hinzufuegen("Saft");
myBean.hinzufuegen("Bier");
myBean.hinzufuegen("Wurst");
Vector v = myBean.getInhalt();
for (int i=0;i<v.size();i++){
System.out.println(v.elementAt(i));
}
} catch (Exception e) {
e.printStackTrace();
System.out.println(e.getMessage());
}

Der Aufruf der Methode getHome().create erzeugt eine Referenz auf die entfernt lie-
gende EJB. Das Home-Objekt ruft zunächst die ejbCreate-Methode (mit einem Parame-
ter vom Typ String) der EJB auf. Die Attribute werden mit den entsprechenden Werten ge-
setzt. Das entfernt liegende EJB-Objekt wird zu diesem Zeitpunkt im Allgemeinen nicht
instanziiert, denn die Instanziierung übernimmt der EJB-Container selbstständig und wir
haben von außen (vom Client) keinen Einfluss darauf.
Der Aufruf der Methode hinzufuegen fügt dem in der EJB enthaltenen Vector-Objekt
jeweils ein neues Element hinzu. Der Inhalt des Warenkorbs, also das Vector-Objekt, kann
mit der getInhalt-Methode aus der EJB ausgelesen wird.

335
eclipse_v01.book Seite 336 Montag, 30. Januar 2006 12:02 12

Webclient für den Zugriff auf eine EJB

In der main-Methode derselben Klasse erzeugen wir ein Objekt vom Typ Warenkorb-
Client und rufen die testBean-Methode auf:

public static void main(String[] args) {


WarenkorbClient test = new WarenkorbClient();
test.testBean();
}

Da es sich bei der Bean um eine Stateful Session Bean handelt, wird bei jedem neuen Pro-
grammstart des Clients jeweils ein neues EJB-Objekt verwendet und auf die Daten der vor-
herigen Session kann nicht mehr zugegriffen werden.
Angenommen, WarenkorbBean wäre eine Stateless Session Bean. Dann würden alle Ses-
sions auf dasselbe Objekt zugreifen und bei jedem neuen Start des Client-Programms wür-
den zusätzliche Elemente in das Vector-Object der EJB aufgenommen werden und die
Einkaufsliste geriete immer länger.
Wenn wir versuchen, das Java-Programm zu starten, erscheinen Fehlermeldungen, da wir
noch folgende Bibliotheken in das Projekt einbinden müssen. Die Dateien stehen im Instal-
lationsverzeichnis des JBoss Servers:
쐌 ../client/jboss-client.jar
쐌 ../client/jboss-common-client.jar
쐌 ../client/jboss-j2ee.jar
쐌 ../client/jboss-jaas.jar
쐌 ../client/jbosssx-client.jar
쐌 ../client/jboss-transaction-client.jar
쐌 ../client/jnet.jar
쐌 ../client/jnp-client.jar
쐌 ../client/log4j.jar
쐌 ../server/default/lib/jnpserver.jar

7.8.3 WTP zur Erstellung eines Java-Clients


Für die Erstellung eines Java-Clients bietet die WTP einen Assistenten. Allerdings wird mit
dessen Hilfe lediglich ein Java-Projekt erstellt, welches einige Bibliotheken einbindet. Eine
Unterstützung bei der Entwicklung der Methoden für den Zugriff auf die EJB existiert in
der zugrunde liegenden Version nicht. Aus diesem Grund werden die in der Web Tools Plat-
form als Application Client Projects bezeichneten Projekte nicht näher untersucht.

7.9 Webclient für den Zugriff auf eine EJB


Der Zugriff eines Webclients bzw. Browsers auf eine EJB erfolgt über einen Webserver. In
Kapitel 7.2 haben wir gesehen, wie von einem Browser aus auf einen Webserver zugegrif-
fen werden kann. Wir haben JSP-Dokumente entwickelt, in denen Java-Skriptlets ausge-
führt werden. Außerdem konnten wir auf einfache Java Beans zugreifen. In diesem Ab-

336
eclipse_v01.book Seite 337 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

schnitt wollen wir uns damit beschäftigen, wie auf die EJBs, die im EJB-Container eines
Applikationsservers liegen, aus einer Webanwendung heraus zugegriffen werden kann.
Abb. 7.24 skizziert die Architektur einer solchen Anwendung.

Abb. 7.24: Architektur des Zugriffs eines Webclients auf eine EJB

In der Beispielanwendung werden in einer Browser-Oberfläche Bezeichnungen von Arti-


keln eingegeben. Anschließend soll die Möglichkeit bestehen, die Liste der Artikel anzuse-
hen und eine Bestellung aufzugeben.
Wir entwickeln diese Anwendung nach dem Model View Controller (MVC) Pattern.
Als Model dient die in Kapitel 0 bereits entwickelte Session Bean Warenkorb.
Für die View benötigen wir HTML- und JSP-Dokumente, die die Eingabe der Bestellung
verarbeiten und den Inhalt des Warenkorbs ausgeben.
Außerdem verwenden wir ein Servlet, welches die Kommunikation mit dem Applikations-
server bzw. mit dem Model und dem Browser bzw. mit der View übernimmt. Dieses be-
zeichnen wir als Controller Servlet.
Die Architektur ist in Abb. 7.25 dargestellt.

Web Server EJB Container

View
JSP / HTML

Model
Session Bean

C o n tr o l l e r
Controller Servlet

Abb. 7.25: Model View Controller-Architektur der Anwendung

337
eclipse_v01.book Seite 338 Montag, 30. Januar 2006 12:02 12

Webclient für den Zugriff auf eine EJB

Das Controller Servlet muss zunächst eine Verbindung zum Applikationsserver herstellen
und ein Objekt vom Typ des Local bzw. Remote Interface erzeugen. Über diese Objekte
können die einzelnen Methoden aufgerufen werden. Das Controller Servlet läuft im Kon-
text, also unter der virtuellen Maschine des Webservers. Das EJB-Objekt und dessen
Home-Objekt laufen dagegen im Kontext, also unter der virtuellen Maschine des Applika-
tionsservers. Die beiden Kontexte können unterschiedlich sein, wie das beim Beispiel des
Java-Client-Programms der Fall war (siehe Kapitel 7.8). Die beiden Kontexte können aber
auch identisch sein, wenn wir beispielsweise den JBoss Server so starten, dass der integrier-
te Webserver auch gleichzeitig gestartet wird. Bei der Startkonfiguration default, die wir
bisher ausschließlich für den JBoss Server verwendet haben, wird der Tomcat-Server mit-
gestartet. Demnach untersuchen wir zunächst den lokalen Zugriff auf eine EJB im selben
Kontext.

7.9.1 Lokaler Zugriff innerhalb derselben virtuellen Maschine


Wenn in einer Webanwendung eine EJB aufgerufen wird, dann fungiert der Webserver als
Client beim Applikationsserver. Im folgenden Beispiel werden wir aus einer Java Bean
(dem Controller Servlet) heraus, die im Kontext des Tomcat Webservers ausgeführt wird,
auf eine Enterprise Java Bean, die im EJB-Container des JBoss Servers im selben Kontext
ablaufen soll, zugreifen.

Local Interface und Local Home Interface


Beim lokalen Zugriff innerhalb derselben virtuellen Maschine erfolgt der Zugriff nicht über
das RMI-Protokoll. Wir verwenden das Local Interface und das Local Home Interface1.

package com.entwickler.eclipsebuch.warenkorb;

public interface WarenkorbLocal extends javax.ejb.EJBLocalObject


public void hinzufuegen(String artikel ) ;
public void entfernen(String artikel ) ;
public java.util.Vector getInhalt( ) ;
}

Das Local Home Interface ist wie folgt aufgebaut:

package com.entwickler.eclipsebuch.warenkorb.WarenkorbLocal;

public interface WarenkorbLocalHome


extends javax.ejb.EJBLocalHome{

public static final String


COMP_NAME="java:comp/env/ejb/WarenkorbBean";
public static final String JNDI_NAME="WarenkorbLocal";

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel07/Kap70901WarenkorbWebClient

338
eclipse_v01.book Seite 339 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

public WarenkorbLocal create(String name , String nr)


throws javax.ejb.CreateException;

public WarenkorbLocal create(java.lang.String name)


throws javax.ejb.CreateException;

public WarenkorbLocal create()


throws javax.ejb.CreateException;
}

Der Wert WarenkorbLocal der öffentlichen Konstanten JNDI_NAME wird vom Namens-
dienst verwendet, um das entsprechende Local-Home-Objekt zu lokalisieren. Das Element
local-jndi-name definiert in der Datei jboss.xml den entsprechenden JNDI-Namen.

Eingabeseite
Für den Webclient erzeugen wir ein neues Eclipse-Projekt. In dieses Projekt binden wir die
beiden Interfaces WarenkorbLocal und WarenkorbLocalHome ein. Für die Eingabe der
Artikel, die in den Warenkorb aufgenommen werden sollen, verwenden wir eine einfache
HTML-Seite mit dem Namen index.html:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">


<html>
<head><title>Eingabeseite</title></head>
<body>
<h1><center>Warenkorb</center></h1>
<form action="Hinzufuegen" method="get">
<p>Artikel: <input type='text' name='artikel' size='50'></p>
<p><input type='submit' name='submit' value='Senden'='20'>
<input type='reset' name='reset' value='Zurücksetzen'></p>
</form>
</body>
</html>

Im <form>-Tag wird angegeben, dass bei Klick auf den SENDEN-Button die doGet-Metho-
de des Controller Servlets aufgerufen wird. Das Controller Servlet wird in dem Deploy-
ment-Deskriptor mit dem Namen Hinzufuegen definiert und referenziert die Servlet-
Klasse Hinzufuegen.

Controller Servlet, init-Methode


Beim ersten Zugriff auf das Controller Servlet wird dieses vom Webserver instanziiert und
initialisiert. Dabei wird die Methode init aufgerufen. Diese Methode überschreiben wir
und lokalisieren dabei den Namensdienst bzw. referenzieren das Local-Home-Objekt:

339
eclipse_v01.book Seite 340 Montag, 30. Januar 2006 12:02 12

Webclient für den Zugriff auf eine EJB

public void init() throws ServletException {


super.init();
try {
InitialContext initialContext = new InitialContext();
Object o = initialContext.lookup(
WarenkorbLocalHome.JNDI_NAME);
if (o==null){
System.out.println("Fehler beim Lokalisieren von " +
WarenkorbLocalHome.JNDI_NAME);
}else{
wkh = (WarenkorbLocalHome) o;
}
} catch (NamingException e) {
e.printStackTrace();
}
}

Für das Lokalisieren des Namensdienstes wird ein Objekt der Klasse InitialContext
verwendet. Da der Namensdienst sich im selben Kontext wie der Webserver befindet, sind
keine Informationen wie URL, Port oder Timeout-Daten erforderlich.
Es gibt keine Remote-Kommunikation, bei der Objekte serialisiert und wieder aufgebaut
werden, sondern es werden lediglich die Referenzen auf die Objekte ausgetauscht.
Mithilfe der lookup-Methode wird das Home-Objekt referenziert und, falls es unter dem
angegebenen Namen (WarenkorbLocalHome.JNDI_NAME) existiert, in ein Objekt vom
Typ WarenkorbLocalHome konvertiert. Die entsprechende Variable wkh ist als Instanz-
variable dieser Klasse definiert. Damit kann von allen Methoden der Klasse aus ein Objekt
der Session Bean referenziert werden.

Controller Servlet, doGet-Methode


In der doGet-Methode wird der eingegebene Artikel in den Warenkorb aufgenommen. Das
heißt, dass die hinzufuegen-Methode der Session Bean Warenkorb aufgerufen wird.
Dabei muss unterschieden werden, ob der erste Artikel in den Warenkorb gelegt wird und
dieser vorher neu angelegt werden muss oder ob er schon existiert.

protected void doGet(HttpServletRequest request,


HttpServletResponse response)
throws ServletException, IOException {
WarenkorbLocal myBean;
RequestDispatcher disp;
try {
HttpSession session = request.getSession();
if (session.getAttribute("Warenkorb") == null){
myBean = wkh.create("");
session.setAttribute("Warenkorb",myBean);

340
eclipse_v01.book Seite 341 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

}else{
myBean = (WarenkorbLocal) session.getAttribute("Warenkorb");
}
myBean.hinzufuegen(request.getParameter(("artikel")));
request.setAttribute("artikel",myBean.getInhalt());
disp = request.getRequestDispatcher("ausgabe.jsp");
disp.forward(request, response);
} catch (CreateException e) {
e.printStackTrace();
}
}

In der doGet-Methode wird in der Variablen session eine Referenz auf das HttpSes-
sion-Objekt der aktuellen Sitzung gespeichert. Das HttpSession-Objekt gehört zu einer
Sitzung eines Benutzers. Es beinhaltet eine Collection, in der unter verschiedenen Namen
Objekte abgelegt werden können. Die Methode getAttribute gibt das Objekt zurück,
welches unter dem Namen gespeichert worden ist. Ist für diesen Namen kein Objekt vor-
handen, wird null zurückgeliefert.
Wenn also während einer Sitzung die doGet-Methode zum ersten Mal aufgerufen wird,
existiert in der Collection unter dem Namen Warenkorb kein Objekt und es wird mit der
create-Methode des Local-Home-Objekts eine neue Referenz auf die Session Bean er-
stellt und im HttpSession-Objekt unter dem Namen Warenkorb gespeichert.
Zu unterscheiden ist dabei, dass die Session Bean aus dem EJB-Container und das Http-
Session-Objekt des Webservers zwei unabhängige Objekte sind, wobei von dem Http-
Session-Objekt aus eine Instanz der Session Bean referenziert wird.
Wenn der Warenkorb angelegt ist und unter dem Namen Warenkorb ein Objekt existiert,
dann wird dieses verwendet.
Anschließend wird der neue Artikel hinzugefügt (Aufruf der hinzufuegen-Methode der
Session Bean) und das Vector-Objekt artikel, welches die Namen der Artikel enthält, für
diese Anfrage im HttpRequest-Objekt gespeichert.
Danach wird die Kontrolle an die JSP ausgabe.jsp weitergegeben. In dem Dokument ist die
View realisiert. Da die View die Namen der Artikel anzeigen soll, muss sie das entsprechen-
de Vector-Objekt artikel referenzieren. Dies erfolgt durch:

<jsp:useBean id="artikel" scope="request"


class="java.util.Vector" />

Es wird also die Bean artikel des Request vom Typ java.util.Vector verwendet.
Anschließend können die einzelnen Elemente des Vector-Objekts angesprochen werden.
Das JSP-Dokument ist wie folgt aufgebaut1:

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel07/Kap70901WarenkorbWebClient

341
eclipse_v01.book Seite 342 Montag, 30. Januar 2006 12:02 12

Webclient für den Zugriff auf eine EJB

<%@ page language="java" contentType="text/html;


charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<jsp:useBean id="artikel" scope="request"
class="java.util.Vector" />
<html>
<head><title>Ausgabe der Artikeldaten</title></head>
<body>
<p><b>Folgende Artikel sind im Warenkorb:</b><br></p>
<%
for (int i = 0;i<artikel.size();i++){
out.println((i+1) + ". " + artikel.elementAt(i) + " <br>");
}
%>
<p><a href='index.html'>Zurück zur Eingabe</a></p>
</body>
</html>

Deployment
Für das Deployment benötigen wir noch den Deployment-Deskriptor web.xml, in dem wir
das Servlet Mapping vornehmen. Dabei wird beispielsweise festgelegt, welches Servlet
verwendet wird, wenn eine bestimme URL als action-Attribut des HTML-Formulars an-
gegeben wird.
Wir legen den Servlet-Namen mit Hinzufuegen fest und geben als URL-Pattern /Hinzu-
fuegen vor. Der Bezeichner der Klasse muss vollständig qualifiziert sein und lautet
com.entwickler.eclipsebuch.wkClient.Hinzufuegen:

<web-app>
...
<servlet>
<display-name>Hinzufuegen</display-name>
<servlet-name>Hinzufuegen</servlet-name>
<servlet-class>
com.entwickler.eclipsebuch.wkClient.Hinzufuegen
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Hinzufuegen</servlet-name>
<url-pattern>/Hinzufuegen</url-pattern>
</servlet-mapping>
...
</web-app>

342
eclipse_v01.book Seite 343 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

Das Projekt besteht demnach aus den folgenden Dateien:


쐌 View: index.html, ausgabe.jsp
쐌 Controller Servlet: com.entwickler.eclipsebuch.wkClient.Hinzufuegen
쐌 Model: Local und Local Home Interface und Session Bean. Die Session Bean
gehört nicht zum Client-Projekt, sondern muss lediglich im EJB-Container enthalten
sein. Das Local und das Local Home Interface sind im Kontext des Webservers bekannt,
weil es sich hierbei um den Kontext des EJB-Containers handelt. Für die Entwicklung
des Eclipse-Projekts ist es aber notwendig, die Interfaces aufzunehmen. Beispielsweise
können die Quelldateien mit im JavaSource-Verzeichnis gespeichert oder als Biblio-
thek in den Classpath aufgenommen werden.

Abb. 7.26: Struktur der Webanwendung WarenkorbWEB

Beim Deployment wird aus den Dateien ein so genanntes Webarchiv erzeugt und in das de-
ploy-Verzeichnis des Webservers kopiert. Der Webserver ist in unserem Fall in dem JBoss
Server integriert und wir verwenden das zugehörige deploy-Verzeichnis ../server/default/
deploy.
Bei Verwendung der Web Tools Platform fügen wir in der Servers View wiederum den Ser-
ver JBOSS 4.0.X hinzu. Über die Maske ADD AND REMOVE PROJECTS ordnen wir das EJB-Pro-
jekt Warenkorb und das Webprojekt WKClient dem Server zu. Das Deployment erfolgt
anschließend mithilfe der für die WTP hinterlegten Ant-Skripte automatisch.
Nach Eingabe von http://localhost:8080/WKClient/index.html erhalten wir im Browser
eine Seite, die in Abb. 7.27 angezeigt wird.

343
eclipse_v01.book Seite 344 Montag, 30. Januar 2006 12:02 12

Webclient für den Zugriff auf eine EJB

Abb. 7.27: Beispiel Warenkorb, Eingabemaske

Wenn wir einen Artikel eingeben und auf den Button SENDEN klicken, erscheint die Ausga-
beseite, die in Abb. 7.28 dargestellt ist.

Abb. 7.28: Beispiel Warenkorb, Ausgabeseite

7.9.2 Zugriff bei unterschiedlichen virtuellen Maschinen


Wenn wir einen anderen Webserver als den Tomcat Webserver oder diesen in einer anderen
Version verwenden möchten, werden Webserver und Applikationsserver in unterschiedli-
chen Kontexten mit unterschiedlichen virtuellen Maschinen gestartet. Wir können dann
aber weiterhin auf den EJB-Container des JBoss Servers zugreifen. Dies ist jedoch ein ent-
fernter Zugriff. Das bedeutet, dass über RMI der Namensdienst und das Remote Home-Ob-
jekt angesprochen werden.
Im Vergleich zum Webclient aus dem vorherigen Abschnitt muss lediglich die init-Me-
thode des Controller Servlets verändert werden. Das InitialContext-Objekt benötigt
die Informationen für die Remote-Kommunikation, die wir bei dem Java-Client in Kapitel
7.8 kennen gelernt haben:

public void init() throws ServletException {


try {
Hashtable props = new Hashtable();
props.put(InitialContext.INITIAL_CONTEXT_FACTORY,
"org.jnp.interfaces.NamingContextFactory");
props.put(InitialContext.PROVIDER_URL,
"jnp://127.0.0.1:1099");
props.put(InitialContext.URL_PKG_PREFIXES,
"org.jboss.naming:org.jnp.interfaces");

344
eclipse_v01.book Seite 345 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

InitialContext initialContext = new InitialContext(props);


WarenkorbHome wkh = (WarenkorbHome)
initialContext.lookup(WarenkorbHome.JNDI_NAME);
myBean = wkh.create();
} catch (RemoteException e) {
e.printStackTrace();
} catch (CreateException e) {
e.printStackTrace();
} catch (NamingException e) {
e.printStackTrace();
}
}

Weiterhin müssen wir die Aufrufe der entfernt liegenden Methoden der EJB jeweils in ei-
nen try-catch-Block einschließen, in dem die java.rmi.RemoteException abgefan-
gen wird.

7.10 Datenbankzugriff aus einer EJB


Datenbankverbindungen werden beim JBoss Server über die JCA-Implementierung (J2EE
Connector Architecture) verwaltet. Alle Zugriffe verwenden die so genannten JCA Re-
source Adapter, die das Connection Pooling, Sicherheitsbeschränkungen und Transaktio-
nen handhaben.
JBoss wird mit der Hypersonic-Datenbank ausgeliefert. Der Zugriff auf diese kann über ei-
nen vordefinierten JNDI-Namen erfolgen. Die Datenbank kann für Testzwecke sehr gut
eingesetzt werden. Allerdings wird in realen Projekten in der Regel ein anderes Datenbank-
system verwendet, so dass wir untersuchen, wie wir eine andere Datenbank in den JBoss
Server einbinden können.

7.10.1 Einstellungen für den Zugriff auf die MySQL-Datenbank


Wir verwenden die MySQL-Datenbank, für die der proprietäre JDBC-Treiber Connector/J
kostenlos zur Verfügung steht. Die zugehörige Java-Archiv-Datei (mysql-connector-java-
3.0.9-stable-bin.jar) muss in das Verzeichnis server/default/lib der JBoss-Installation ko-
piert werden, falls mit der Startkonfiguration default gearbeitet wird.
Die Konfiguration der Datenquelle (DataSource) erfolgt in Konfigurationsdateien, bei
denen es sich um XML-Dateien mit der Endung –ds.xml handelt. Im Verzeichnis docs/
examples/jca der JBoss-Installation liegen Beispieldateien für gängige Datenbanken, die
als Grundlage für den speziellen Treiber dienen können.
Für den Zugriff auf MySQL ist im Verzeichnis server/default/deploy also die folgende
XML-Datei mysql–ds.xml1 zu speichern:

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel07/Kap71001Datenbankkonfiguration

345
eclipse_v01.book Seite 346 Montag, 30. Januar 2006 12:02 12

Datenbankzugriff aus einer EJB

<?xml version="1.0" encoding="UTF-8"?>


<datasources>
<local-tx-datasource>
<jndi-name>OnlineshopDS</jndi-name>
<connection-url>
jdbc:mysql://127.0.0.1:3306/onlineshop
</connection-url>
<driver-class>com.mysql.jdbc.Driver </driver-class>
<user-name>j2ee</user-name>
<password>j2ee</password>
</local-tx-datasource>
</datasources>

Wir vergeben in dieser Datei für den Datenbanktreiber den JNDI-Namen onlineshopDS.
Dieser verweist auf die Datenbank onlineshop, die im MySQL-Datenbankserver existieren
muss. Ist die Datenbank mit einem Usernamen und Passwort geschützt, so können diese In-
formationen ebenfalls angegeben werden. Ansonsten kann dieser Teil auch weggelassen
werden.
Eine neue Datenquelle wird wie eine EJB in das deploy-Verzeichnis kopiert und vom JBoss
Server verarbeitet, so dass kein Neustart des Servers erforderlich ist.
Für den Zugriff auf die Datenbank onlineshop aus einer EJB heraus, müssen wir, wie beim
Zugriff auf eine EJB, ein InitialContext-Objekt erzeugen und dessen lookup-Metho-
de verwenden. Diese erzeugt ein Objekt, das in ein Objekt vom Typ javax.sql.Data-
Source umgewandelt werden kann. Mithilfe der getConnection-Methode des Data-
Source-Objekts wird ein java.sql.Connection-Objekts generiert. Dieses wird dann
für die weitere Bearbeitung der Daten genutzt. Demnach sind folgende Befehle und Metho-
denaufrufe notwendig:

InitialContext ctx = new InitialContext();


DataSource jdbcFactory =
(DataSource) ctx.lookup("java:OnlineshopDS");
conn = jdbcFactory.getConnection();

Die Erzeugung des InitialContext-Objekts kommt ohne weitere Eigenschaften aus, da


der Datenbankzugriff aus dem Kontext des JBoss Servers heraus verarbeitet wird.
Der Vorteil dieser Variante ist, dass der Client also ausschließlich mit dem Applikations-
server kommuniziert und nicht direkt auf die Datenbank zugreift. Prinzipiell ist es für den
Client irrelevant, welche Datenbank oder welcher Treiber verwendet wird und wo die Da-
tenbank liegt.
Der Client benötigt keine EJB, um auf die Datenbank zuzugreifen, sondern es ist auch mög-
lich, vom Client aus eine Verbindung zur Datenbank über den JNDI-Namen herzustellen.
Dazu ist aber ein Remote-Zugriff auf den Namensdienst des Applikationsservers erforder-
lich. Der Zugriff könnte dann wie folgt aussehen:

346
eclipse_v01.book Seite 347 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

Hashtable props = new Hashtable();


props.put(InitialContext.INITIAL_CONTEXT_FACTORY,
"org.jnp.interfaces.NamingContextFactory");
props.put(InitialContext.PROVIDER_URL, "jnp://127.0.0.1:1099");
InitialContext initialContext = new InitialContext(props);
DataSource jdbcFactory =
(DataSource) ctx.lookup("java:OnlineshopDS");
conn = jdbcFactory.getConnection();

7.10.2 Beispiel Onlineshop


Wir wollen jetzt unser Warenkorb-Beispiel zu einem Online-Shop erweitern. Ein Kunde
soll eine Bestellung online aufgeben können. Dazu ist es notwendig, dass der Kunde sich
registriert hat und ein Passwort eingibt. In unserer Datenbank onlineshop existiert dafür die
Tabelle Kunden mit den Feldern kNr, kName, kAdresse und passwort. Nur wenn ein Kunde
in der Tabelle existiert und die richtige Kombination aus Kundennummer und Passwort ein-
gegeben hat, wird die Bestellung akzeptiert und in dieselbe Datenbank geschrieben. Hierfür
benötigen wir die Tabellen Bestellung und Bestellpositionen. Weiterhin wollen wir, dass der
Kunde die Artikel nicht frei eingibt, sondern dass er sie aus einer Liste auswählt. Die Arti-
kel seien in der Tabelle Artikel gespeichert und sollen auf der Eingabeseite mit ihren Namen
und Preisen angezeigt werden. Daraus ergibt sich das Datenmodell, das in Abb. 7.29 dar-
gestellt wird.
Mithilfe der Batch-Datei Onlineshop.sql1 kann die Datenbank erzeugt werden. Dazu geben
wir in der Konsole der MySQL-Datenbank folgende Zeile ein:

mysql>source ../Onlineshop.sql;

Abb. 7.29: Datenmodell der Beispielanwendung Onlineshop

Bean-Klasse DatenbankBean
Wir erzeugen in dem Projekt Onlineshop eine neue EJB-Klasse mit dem Namen Daten-
bankBean im Package com.entwickler.eclipsebuch.datenbank. In dieser Kompo-
nente sollen die Datenbankfunktionalitäten gekapselt werden. Diese EJB ist eine statuslose
Session Bean. Das bedeutet, dass alle User auf dasselbe Objekt zugreifen. In der EJB dür-
fen demnach keine benutzerabhängigen Daten gespeichert werden.

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel07/Kap71001Datenbankkonfiguration

347
eclipse_v01.book Seite 348 Montag, 30. Januar 2006 12:02 12

Datenbankzugriff aus einer EJB

Die EJB verfügt über eine Instanzvariable vom Typ java.sql.Connection, die von allen
Usern genutzt wird. Der Verbindungsaufbau zur Datenbank Onlineshop wird in der ejb-
Create-Methode realisiert:

public void ejbCreate(){


try {
InitialContext ctx = new InitialContext();
DataSource jdbcFactory = (DataSource)
ctx.lookup("java:OnlineshopDS");
conn = jdbcFactory.getConnection();
} catch (Exception e) {
e.printStackTrace();
}

Die login-Methode der Klasse DatenbankBean nutzt dieses Datenbankverbindungs-


objekt, um zu überprüfen, ob der Kunde unter der als Parameter angegebenen Kundennum-
mer existiert und das richtige Passwort angegeben hat. Dazu wird ein ResultSet-Objekt
erzeugt, welches den Datensatz enthält, auf den die Kundennummer-Passwort-Kombina-
tion passt. Ist das ResultSet leer, gibt die next-Methode false zurück und die Anmel-
dung ist fehlgeschlagen. Besitzt das ResulSet dagegen einen Datensatz, so schiebt die
next-Methode den Datensatzzeiger auf den ersten und einzigen Datensatz und gibt true
zurück. In diesem Fall war die Anmeldung erfolgreich1.

public boolean login(String knr, String pwd){


Statement stat;
try {
stat = conn.createStatement();
String sql = "SELECT kNr, passwort FROM kunden " +
"WHERE kNr=" + knr + " AND passwort='" + pwd + "'";
ResultSet rs = stat.executeQuery(sql);
boolean akzeptiert = rs.next();
rs.close();
stat.close();
return akzeptiert;
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}

Für das Eintragen in die Datenbank implementieren wir die Methode insertBestellung
in die EJB-Klasse DatenbankBean. Diese Methode erhält als Parameter ein Vector-Ob-
jekt mit den Artikelnummern und der Kundennummer übergeben. Im ersten Schritt legen
wir einen Datensatz in der Tabelle Bestellung der Datenbank Onlineshop mit einer neu ge-

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel07/Kap7308Onlineshop

348
eclipse_v01.book Seite 349 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

nerierten Bestellnummer, der Kundennummer und dem heutigen Datum an. Für die Gene-
rierung einer eindeutigen Bestellnummer berechnen wir das Maximum der Werte und ad-
dieren den Wert 1 hinzu.

private int getSchluesselwert(String tabelle, String feld){


try {
String sql = "SELECT max("+feld+") as wert FROM "+tabelle;
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery(sql);
rs.next();
String s = rs.getString("wert");
if (s!= null){
return Integer.parseInt(s) + 1;
}else{
return 0;
}
} catch (Exception e) {
return 0;
}
}

Die Berechnung des aktuellen Datums wird mit Hilfe der Klasse Calendar vorgenommen.
Wir können dann einen Datensatz in die Tabelle Bestellung folgendermaßen einfügen:

public void insertBestellung(Vector v, String knr){


try {
int bnr = this.getSchluesselwert("Bestellung", "bNR");
Calendar cal = Calendar.getInstance();
String datum = cal.get(Calendar.DAY_OF_MONTH) + "." +
(cal.get(Calendar.MONTH)+1) + "." +
cal.get(Calendar.YEAR);
String sql = "INSERT INTO Bestellung (bNr,kNr,bDatum) " +
"VALUES (?,?,?)";
PreparedStatement prepStat1 = conn.prepareStatement(sql);

prepStat1.setString(1,bnr);
prepStat1.setString(2,knr);
prepStat1.setString(3,datum);
prepStat1.executeUpdate();
prepStat1.close();

// Hier folgt der zweite Teil des Einfügens, nämlich


// das Einfügen der Bestellpositionen.

} catch (SQLException e) {
e.printStackTrace();
}
}

349
eclipse_v01.book Seite 350 Montag, 30. Januar 2006 12:02 12

Datenbankzugriff aus einer EJB

Im zweiten Schritt suchen wir für jedes Element des Vector-Objekts einen entsprechenden
Datensatz in der Artikeltabelle. Bei Existenz der Artikelnummer schreiben wir einen Da-
tensatz in die Tabelle Bestellpos. Im anderen Fall löschen wir die Artikelnummer aus dem
Vector-Objekt. Wir fügen also folgenden Programmcode in die Methode insertBe-
stellung ein:

sql = "SELECT ANr FROM Artikel WHERE ANR=?";


prepStat1 = conn.prepareStatement(sql);
for (int i=v.size()-1;i>=0;i--){
prepStat1.setString(1,(String) v.elementAt(i));
ResultSet rs = prepStat1.executeQuery();
if (rs.next()){
int posNr = this.getSchluesselwert("BestellPos","PosNr");
sql = "INSERT INTO BestellPos "
+ "(posNr,bNr,aNr,menge) VALUES (?,?,?,?)";
PreparedStatement prepStat2 = conn.prepareStatement(sql);
prepStat2.setInt(1,posNr);
prepStat2.setInt(2,bnr);
prepStat2.setString(3,(String) v.elementAt(i));
prepStat2.setString(4,"1");
prepStat2.executeUpdate();
prepStat2.close();
}else{
v.remove(i);
}
rs.close();
}
prepStat1.close();

Controller Servlet BestellungSpeichern


Wir müssen den Webclient erweitern, damit wir die zusätzliche Funktionalität abbilden
können. Auf der Seite ausgabe.jsp, auf der bisher lediglich der Inhalt des Warenkorbs aus-
gegeben worden ist, fügen wir die Eingabemöglichkeit von Kundennummer und Passwort
hinzu.

<body>
<p><b>Folgende Artikel sind im Warenkorb:</b><br></p>
<%
for (int i = 0;i<artikel.size();i++){
out.println((i+1) + ". " + artikel.elementAt(i) + " <br>");
}%></p>
<p><a href='index.html'>Zurück zur Eingabe</a></p>
<form action="BestellungSpeichern" method="get">
<p>Kundennr: <input type="text" name="knr"> </p>
<p>Passwort: <input type="password" name="pwd"> </p>
<p><input type="submit" name="Bestellen" value="Bestellen"></p>
</form>
</body>

350
eclipse_v01.book Seite 351 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

Bei Klick auf den Button BESTELLEN (siehe Abb. 7.30) soll die Bestellung dann aufgegeben
und in unsere Datenbank eingetragen werden. Der Einfachheit halber nehmen wir zunächst
einmal an, dass der Benutzer ausschließlich Artikelnummern und nicht die Artikelbezeich-
nungen in der Eingabemaske eingibt.

Abb. 7.30: Warenkorb und Bestellung (ausgabe.jsp)

Bei Klick auf den Button BESTELLEN wird BestellungSpeichern als Request-URL an-
gegeben. Im Deployment-Deskriptor der Webanwendung wird festgelegt, dass hierbei das
Controller Servlet BestellungSpeichern verwendet werden soll.
In der doGet-Methode des Servlets benötigen wir Referenzen auf die beiden Session Beans
DatenbankBean und WarenkorbBean. Die Referenz auf WarenkorbBean ist im Http-
Session-Objekt session der Webanwendung gespeichert und kann über das Http-
Request-Objekt angesprochen werden. Existiert diese Referenz nicht, so ist noch kein
WarenkorbBean erzeugt worden und es kann keine Bestellung gespeichert werden. In die-
sem Fall wird die Startseite wieder aufgerufen:

if (session.getAttribute("Warenkorb") == null){
disp = request.getRequestDispatcher("index.html");
}else{
wk = (WarenkorbLocal) session.getAttribute("Warenkorb");
request.setAttribute("artikel",wk.getInhalt());
String knr = request.getParameter("knr");
String pwd = request.getParameter("pwd");
if (db.login(knr,pwd)){
db.insertBestellung(wk.getInhalt(),knr);
disp = request.getRequestDispatcher("bestellung.jsp");
}else{
disp = request.getRequestDispatcher("ausgabe.jsp");
}
}

Existiert der Warenkorb, dann muss die Kundennummer-Passwort-Kombination überprüft


werden. Dazu wird die login-Methode der Session Bean DatenbankBean eingesetzt. Ist
die Kombination falsch, wird die Seite ausgabe.jsp wieder aufgerufen, ansonsten müssen

351
eclipse_v01.book Seite 352 Montag, 30. Januar 2006 12:02 12

Entity Beans

die bestellten Artikel mithilfe der Methode insertBestellung in die Datenbank ge-
schrieben und auf der Seite bestellung.jsp ausgegeben werden:

<jsp:useBean id="artikel" scope="request"


class="java.util.Vector" />
<html><head><title>Bestellung</title></head>
<body>
<h1>Die Bestellung war erfolgreich.</h1>
<p>Folgende Artikel wurden bestellt:</p>
<%
for (int i = 0;i<artikel.size();i++){
out.println((i+1) + ". " + artikel.elementAt(i) + " <br>");
}%>
</body></html>

Auf diesem JSP-Dokument wird auf das Objekt artikel zurückgegriffen, welches in der
doGet-Methode des Controller Servlets für den Request gespeichert worden ist:

request.setAttribute("artikel",wk.getInhalt());

Die Abarbeitung des JSP-Dokuments gehört zum gleichen Request wie der Aufruf der do-
Get-Methode. Die Kontrolle wird vom Controller Servlet innerhalb des Request an das
JSP-Dokument gesendet, bevor das neu erzeugte HTML-Dokument im http-Response an
den Client gesendet wird.
Nachdem wir untersucht haben, wie wir Stateful und Stateless Session Beans (Waren-
korbBean und DatenbankBean) entwickeln, um damit bestimmte Funktionalitäten abbil-
den zu können, werden wir uns im Folgenden mit den Entity Beans beschäftigen. Wir er-
weitern das Beispiel aus diesem Abschnitt und definieren Entity Beans für die einzelnen
Artikel des Onlineshops. Das Ziel unseres Beispiels ist, dass der Anwender nicht mehr die
Artikelnummern eingibt, sondern die Artikel aus einer Liste auswählt.

7.11 Entity Beans

7.11.1 Grundlagen und Persistenz


Entity Beans modellieren Geschäftsobjekte. Sie dienen dazu, Daten persistent zu speichern.
Die Entity Beans sind nicht an eine Sitzung oder an einen Anwender gebunden. Sie werden
vom EJB-Container erzeugt und stehen, abhängig von Sicherheitsbeschränkungen, jedem
Client zur Verfügung. Es können also verschiedene Clients auf dieselben Entity Beans zu-
greifen.
Entity Beans werden eingesetzt, um Daten (Artikel-, Kunden-, Bestelldaten etc.) aus der
Datenbank im Applikationsserver vorzuhalten und damit die Performance für den Zugriff
zu steigern.

352
eclipse_v01.book Seite 353 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

Persistenz bedeutet, dass beim Erzeugen der Entity Beans die Daten direkt aus der Daten-
bank übernommen werden und dass jede Datenänderung an die Datenbank weitergegeben
wird. Wird eine Entity Bean entfernt, so wird der korrespondierende Datensatz gelöscht.
Der Zustand eines Entity Bean-Objekts wird also stets in der Datenbank gespeichert.
Bei der Persistenz werden die Container Managed Persistence (CMP) und die Bean Mana-
ged Persistence (BMP) unterschieden. Wie der Name das schon andeutet, übernimmt bei
der CMP der EJB-Container die Aufgabe, die Daten der Entity Bean persistent zu spei-
chern. Im Deployment-Deskriptor ist dabei anzugeben, welche Daten der Bean gespeichert
werden sollen. Im Falle der BMP müssen wir innerhalb der Bean das Speichern der Daten
selber programmieren.
Werden Entity Beans für die Datenhaltung eingesetzt, sollten wir nicht mehr direkt auf die
Daten in der Datenbank zugreifen. Daraus folgt aber auch, dass wir gewisse Funktionali-
täten des Datenbanksystems nicht mehr nutzen können. Beispielsweise ist die Frage zu
beantworten, wie bestimmte Objekte/Datensätze gesucht werden können oder wie wir die
Daten verknüpfen.
Die Entity Bean-Klasse enthält wie eine Session Bean-Klasse Business- und ejbCreate-
Methoden. Weil aber ein Entity Bean-Objekt von einem Client erzeugt und von einem
anderen Client genutzt werden kann, muss dieses auffindbar sein. Die Identifikation der
Entity Bean-Objekte erfolgt über einen Primärschlüssel. Um auf bestehende Entity Bean-
Objekte zuzugreifen, verwenden wir die so genannten Finder-Methoden.

7.11.2 Finder-Methoden
Finder-Methoden werden nach der EJB-Spezifikation im Home Interface zu einer Entity
Bean deklariert. Sie liefern entweder ein Objekt oder eine Collection von Objekten vom
Typ des Entity Bean. Der Name einer Finder-Methode im Home Interface beginnt immer
mit dem Präfix find, worauf in der Regel das Kriterium folgt, nach dem gesucht werden
soll. Beispiele für Finder-Methoden sind:
쐌 findByPrimaryKey
쐌 findByArtikelname
쐌 findInPLZBereich
Die Methoden können beliebige Parameter enthalten, die nach der Java RMI API gültig
sind. Die Parameter werden benötigt, um die Objekte einzugrenzen.
Die Methode findByPrimaryKey muss für jede Entity Bean deklariert werden. Sie erhält
genau einen Parameter, der den Schlüsselwert (primary key) übergibt. Handelt es sich um
einen Schlüssel, der aus mehreren Feldern besteht, so ist eine Klase mit den Schlüsselattri-
buten zu definieren. Diese muss das Interface java.io.Serializable implementieren,
damit das übergebene Objekt nach der Java RMI API gültig ist. Die Methode find-
ByPrimaryKey liefert eine Referenz auf das eindeutig bestimmte Entity Bean-Objekt
zurück. Falls ein entsprechendes Objekt nicht existiert, wird eine Exception vom Typ
javax.ejb.ObjectNotFoundException ausgelöst.

353
eclipse_v01.book Seite 354 Montag, 30. Januar 2006 12:02 12

Entity Beans

Die Methode findInPLZBereich soll beispielsweise alle Kunden aus einem bestimmten
PLZ-Bereich zurückgeben. Dabei kann es sich um beliebig viele Entity Bean-Objekte han-
deln, deren Referenz in einer Collection gespeichert sind. Falls das angegebene Kriterium
auf kein Entity Bean-Objekt passt, ist die Collection leer. Es wird aber keine Exception aus-
gelöst.
Neben der java.rmi.RemoteException, die jede Methode in der throws-Klausel ent-
hält, wird für die Finder-Methoden zusätzlich die javax.ejb.FinderException ange-
geben. Die Klasse javax.ejb.ObjectNotFoundException ist eine Subklasse davon.

Finder-Methoden bei Container Managed Persistence


Bei Container Managed Persistence müssen wir die Finder-Methoden nicht selbstständig
implementieren, weil der EJB-Container diese erzeugt und die Aufgabe des Suchens und
Findens übernimmt. Wir müssen also keinen Java-Code hierfür schreiben. Allerdings kann
der EJB-Container nur für die Methode findByPrimaryKey die entsprechende Finder-
Methode in der Entity Bean selbstständig implementieren. Für die anderen Methoden reicht
die Deklaration nicht aus. Der EJB-Container kann nicht wissen, wie beispielsweise die
Identifikation der Kunden in einem bestimmten PLZ-Gebiet vorzunehmen ist, ohne dass
weitere Informationen angegeben werden. Aus diesem Grund machen wir noch einige Ein-
stellungen im Deployment-Deskriptor ejb-jar.xml, damit eine automatische Implementie-
rung erfolgt. Im Abschnitt 7.11.3 untersuchen wir das Thema anhand von Beispielen.

Finder-Methoden bei Bean Managed Persistence


Die Implementierung der Finder-Methoden ist bei Bean Managed Persistence erforderlich.
Zu jeder Finder-Methode aus dem Home Interface ist eine Methode mit dem Präfix ejb-
Find in der Entity Bean zu programmieren.
Die ejbFindByPrimaryKey-Methode erhält als Parameter den Schlüsselwert und besitzt
als Rückgabewert ebenfalls den Schlüsselwert. Die Methode überprüft, ob ein korrespon-
dierender Datensatz in der Datenbank vorhanden ist. Existiert ein solcher Datensatz, wird
der Schlüsselwert zurückgegeben, ansonsten wird eine Exception vom Typ ObjectNot-
FoundException ausgelöst.
In den anderen ejbFindXXX-Methoden werden ebenfalls nach den angegebenen Kriterien
Datensätze aus der Datenbank gesucht. Anschließend wird jeweils der Schlüsselwert der
gefundenen Datensätze in ein Collection-Objekt gespeichert und diese Collection zu-
rückgegeben. Beispiele zu den Finder-Methoden bei BMP betrachten wir in Abschnitt
7.11.4.

7.11.3 Entity Bean (Container Managed Persistence)


Entwicklung
In diesem Beispiel sollen die Artikel des Onlineshops als Entity Beans vorgehalten werden.
Neben der Finder-Methode ejbFindByPrimaryKey soll es noch die Möglichkeit geben,
dass alle Artikel, die für den Online-Verkauf verfügbar sind, ausgegeben werden. Für die

354
eclipse_v01.book Seite 355 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

Entity Bean ArtikelBean sei Container Managed Persistence eingestellt. Die zugehörige
Datenbanktabelle ist im SQL-Modell1 folgendermaßen definiert:

CREATE TABLE Artikel (


aNr INT NOT NULL PRIMARY KEY,
aBz VARCHAR(50) NOT NULL,
aPr DOUBLE,
online BIT);

Die Entity Bean-Klasse (ArtikelBean) ist eine abstrakte Klasse, welche vom EJB-Con-
tainer genutzt wird, um eine konkrete Implementierung zu generieren, die dann im EJB-
Container abläuft.
Die Klasse ArtikelBean ist abgeleitet von javax.ejb.EntityBean. Für die persisten-
ten Daten (Felder) werden Zugriffsmethoden (Setter- und/oder Getter-Methoden) abstrakt
deklariert. Zusätzlich werden diese Daten im Deployment-Deskriptor ejb-jar.xml2 inner-
halb des <entity>-Elements in den Elementen <cmp-field> gespeichert:

<ejb-jar>

<enterprise-beans>

<entity >
<ejb-name>Artikel</ejb-name>

<cmp-field ><field-name>ANr</field-name></cmp-field>
<cmp-field ><field-name>ABz</field-name></cmp-field>
<cmp-field ><field-name>APr</field-name></cmp-field>
<cmp-field ><field-name>online</field-name></cmp-field>

Die Namen geben die Namen der persistenten Daten innerhalb der Bean an. Die Konven-
tion ist, dass es sich hierbei um die Namen der Zugriffsmethoden ohne das Präfix set/get
handelt. Es sind also nicht die Namen der Felder in der Datenbanktabelle. Die Zuordnung
zwischen den Namen der Datenbanktabelle und den Namen der Daten der Entity Bean er-
folgt in der Datei jbosscmp-jdbc. Dort wird auch der Name der Datenquelle angegeben, die
wir über den JNDI-Namen ansprechen. Diesen haben wir für die MySQL-Datenbank On-
lineshop im vorherigen Abschnitt 7.10 festgelegt und in der Datei onlineshop-ds.xml im
server/default/deploy-Verzeichnis der JBoss-Installation gespeichert.

1. Die Datenbank haben wir in Kapitel 7.10 erzeugt. Das Skript liegt auf der beiliegenden CD im Verzeichnis
../Kapitel07/Kap71001Datenbankkonfiguration.
2. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel07/Kap71101Onlineshop

355
eclipse_v01.book Seite 356 Montag, 30. Januar 2006 12:02 12

Entity Beans

Die Datei jbosscmp-jdbc1 sieht demnach wie folgt aus:

<?xml version="1.0" encoding="UTF-8"?>


<!DOCTYPE jbosscmp-jdbc PUBLIC "-//JBoss//DTD JBOSSCMP-JDBC 3.0//
EN" "http://www.jboss.org/j2ee/dtd/jbosscmp-jdbc_3_0.dtd">

<jbosscmp-jdbc>
<defaults>
<datasource>java:/OnlineshopDS</datasource>
<datasource-mapping>mySQL</datasource-mapping>
<preferred-relation-mapping>
foreign-key
</preferred-relation-mapping>
</defaults>
<enterprise-beans>
<entity>
<ejb-name>Artikel</ejb-name>
<table-name>Artikel</table-name>
<cmp-field>
<field-name>ANr</field-name>
<column-name>aNr</column-name>
</cmp-field>
<cmp-field>
<field-name>ABz</field-name>
<column-name>aBz</column-name>
</cmp-field>
<cmp-field>
<field-name>APr</field-name>
<column-name>aPr</column-name>
</cmp-field>
<cmp-field>
<field-name>online</field-name>
<column-name>online</column-name>
</cmp-field>
</entity>
</enterprise-beans>
</jbosscmp-jdbc>

Mit diesen Informationen kann der EJB-Container selbstständig die Persistenz abbilden
und die Finder-Methode findByPrimaryKey implementieren. Wir wollen uns aber noch
einmal der abstrakten Klasse ArtikelBean widmen und untersuchen, welche Methoden
zu definieren sind:

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel07/Kap71101Onlineshop

356
eclipse_v01.book Seite 357 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

package com.entwickler.eclipsebuch.entitybeans;
import javax.ejb.EntityBean;

public abstract class ArtikelBean implements EntityBean {


public java.lang.Integer ejbCreate() throws
javax.ejb.CreateException {
// Nach der EJB 2.0 Spezifikation wird immer null zurückgegeben.
return null;
}
public void ejbPostCreate() throws javax.ejb.CreateException {
}
public java.lang.Integer ejbCreate(Integer anr, String abz,
Float apr) throws javax.ejb.CreateException {
this.setANr(anr);
this.setABz(abz);
this.setAPr(apr);
this.setOnline(new Boolean(true));
// Nach der EJB 2.0 Spezifikation wird immer null zurückgegeben.
return null;
}
public void ejbPostCreate(Integer anr, String abz, Float apr)
throws javax.ejb.CreateException {
}
// Hier folgt der zweite Teil der Klasse ArtikelBean

Im ersten Teil der Klasse ArtikelBean werden die ejbCreate- und ejbPostCreate-
Methoden definiert. Die ejbCreate-Methode wird wie bei den Session Beans von den
Home-Objekten aufgerufen, wenn eine Referenz auf die EJB erzeugt werden soll. Die
ejbCreate-Methoden dienen zum Initialisieren der Daten des Entity Bean-Objekts. Sie
werden aufgerufen, bevor eine Referenz des Objekts an das Home-Objekt bzw. an den
Client gegeben wird. Als Rückgabedatentyp wird die Klasse des Primärschlüssels verwen-
det. Allerdings wird die Referenz auf den Primärschlüssel vom EJB-Container zu einem
anderen Zeitpunkt erzeugt, so dass jeweils null zurückgegeben wird. Die Definition des
Rückgabedatentyps hat sich während der Entwicklung des EJB-Standards verändert. Bei
EJB 1.0 waren die Methoden noch vom Typ void.
Die ejbPostCreate-Methode wird nach dem Initialisieren des Entity Bean-Objekts, aber
noch vor dem Zugriff durch einen Client aufgerufen.


// 2. Teil der Klasse ArtikelBean
public abstract java.lang.Integer getANr();
public abstract void setANr(java.lang.Integer aNr);

public abstract java.lang.String getABz();


public abstract void setABz(java.lang.String aBz);

357
eclipse_v01.book Seite 358 Montag, 30. Januar 2006 12:02 12

Entity Beans

public abstract java.lang.Float getAPr();


public abstract void setAPr(java.lang.Float aPr);

public abstract java.lang.Boolean getOnline();


public abstract void setOnline(java.lang.Boolean online);
}

Im zweiten Teil der Klasse ArtikelBean werden die Zugriffsmethoden deklariert. Die
Deklarationen werden vom EJB-Container benötigt, damit sie von diesem implementiert
werden können. Ein weiterer Vorteil der abstrakten Deklaration in dieser Klasse ist, dass sie
in den ejbCreate- und in anderen Business-Methoden verwendet werden können.
Die Methoden des Interface EntityBean sind:
쐌 public void ejbLoad()
쐌 public void ejbStore()
쐌 public void ejbActivate()
쐌 public void ejbPassivate()
쐌 public void setEntityContext(javax.ejb.EntityContext ctx)
쐌 public void unsetEntityContext()
쐌 public void ejbRemove() throws javax.ejb.RemoveException
Sie werden zu bestimmten Zeitpunkten bzw. bei bestimmten Ereignissen aufgerufen. Weil
die Entity Bean-Klasse abstrakt ist, müssen die Methoden des Interface an dieser Stelle
nicht überschrieben werden. Bei CMP erfolgt eine Implementierung der Methoden durch
den EJB-Container.
Für das Deployment benötigen wir wie bei den Session Beans die Deployment-Deskripto-
ren ejb-jar.xml und jboss.xml1:

<ejb-jar >
<enterprise-beans>
<!-- Entity Beans -->
<entity >
<ejb-name>Artikel</ejb-name>
<home>
com.entwickler.eclipsebuch.entitybeans.ArtikelHome
</home>
<remote>
com.entwickler.eclipsebuch.entitybeans.Artikel
</remote>
<Local Home>
com.entwickler.eclipsebuch.entitybeans.ArtikelLocalHome
</Local Home>

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel07/Kap7310Onlineshop

358
eclipse_v01.book Seite 359 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

<local>
com.entwickler.eclipsebuch.entitybeans.ArtikelLocal
</local>
<ejb-class>
com.entwickler.eclipsebuch.entitybeans.ArtikelBean
</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>java.lang.Integer</prim-key-class>
<reentrant>False</reentrant>
<cmp-version>2.x</cmp-version>
<abstract-schema-name>Artikel</abstract-schema-name>

<cmp-field ><field-name>ANr</field-name></cmp-field>
<cmp-field ><field-name>ABz</field-name></cmp-field>
<cmp-field ><field-name>APr</field-name></cmp-field>
<cmp-field ><field-name>online</field-name></cmp-field>
<primkey-field>ANr</primkey-field>
</entity>
</enterprise-beans>
</ejb-jar>

Die ersten Einträge beschreiben das Home, Remote Home, Local und Local Home Inter-
face und die Bean-Klasse. Das Element <persistence-type> gibt die Art der Persistenz
(CMP: Container oder BMP: Bean) an. In den Tags <abstract-schema-name> und
<cmp-field><field-name> werden die Namen angegeben, die vom Container für die
Tabelle und die Felder verwendet werden. Hierbei muss es sich nicht notwendigerweise um
den Namen der Datenbanktabelle und der Felder handeln. Die Zuordnung der Namen, die
der Container nutzt, zu den Namen in der Datenbank haben wir in der Datei jbosscmp-jdbc
vorgenommen.
In der Datei jboss.xml werden wie bei den Session Beans wiederum vor allem die JNDI-
Namen vergeben:

<jboss>
<enterprise-beans>
<entity>
<ejb-name>Artikel</ejb-name>
<jndi-name>ArtikelBean</jndi-name>
<local-jndi-name>ArtikelLocal</local-jndi-name>
</entity>
</enterprise-beans>
</jboss>

Webclient für den Zugriff


Der Zugriff auf Entity Beans unterscheidet sich nicht wesentlich von dem Zugriff auf Ses-
sion Beans. Aus diesem Grund werden wir lediglich den Webclient, der im selben Kontext

359
eclipse_v01.book Seite 360 Montag, 30. Januar 2006 12:02 12

Entity Beans

läuft wie die Anwendung mit der Entity Bean, untersuchen und keinen entfernten Zugriff
auf die Entity Bean durchführen. Die notwendigen Veränderungen für den Remote-Zugriff
können direkt von den Session Beans (siehe Kapitel 7.8 und 7.9) übernommen werden.
Zum Testen des im vorherigen Abschnitt erstellten Entity Bean erweitern und verändern wir
den Webclient, den wir in Kapitel 7.9 entwickelt haben. Nach Eingabe der Artikelnummer
auf der ersten Eingabeseite soll auf der folgenden Seite der Warenkorb mit Artikelnummer
und Artikelbezeichnung ausgegeben werden. Um diese Funktionalität umzusetzen, erstel-
len wir zusätzlich die Klasse ArtikelCtrl. Im Konstruktor der Klasse erzeugen wir eine
Referenz auf das Local Home-Objekt der Entity Bean. Die Variable für diese Referenz kann
nicht lokal im Konstruktor definiert werden, sondern muss eine Instanzvariable sein, da die
Generierung der Referenz auf die Entity Bean selbst erst später in einer anderen Methode
erfolgt.
Nachdem wir das Local Home und das Local Interface in dem Projekt eingebunden haben,
programmieren wir den Konstruktor der Klasse ArtikelCtrl wie folgt1:

private ArtikelLocal artBean;


private ArtikelLocalHome ah;

public ArtikelCtrl (){


try {
InitialContext initialContext = new InitialContext();
ah = (ArtikelLocalHome)
initialContext.lookup(ArtikelLocalHome.JNDI_NAME);
}catch (Exception e) {
e.printStackTrace();
}
}

Außerdem implementieren wir die Methode getArtikelbezeichnung in die Klasse


ArtikelCtrl:

public String getArtikelbezeichnung(int id){


try{
artBean = ah.findByPrimaryKey(new Integer(id));
return artBean.getABz();
} catch (FinderException e) {
return "Artikelname unbekannt.";
}
}

Diese Methode erhält die Artikelnummer, also den Wert des Primärschlüssels, des Entity
Bean-Objekts, für das die Artikelbezeichnung zurückgegeben werden soll. Mithilfe der
Finder-Methode findByPrimaryKey, die eine Methode des Local Home Interface ist,

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel07/Kap71102OnlineshopWebClient

360
eclipse_v01.book Seite 361 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

wird das entsprechende Entity Bean-Objekt referenziert. Falls kein Entity Bean-Objekt zu
der Artikelnummer existiert, wird eine FinderException ausgelöst.
Aus dem JSP-Dokument, das für die Ausgabe des Warenkorbs verantwortlich ist, geben wir
statt der Artikelnummer die Artikelbezeichnung aus, indem wir auf die Methode get-
Artikelbezeichnung zugreifen:

<%@ page language="java" %>


<jsp:useBean id="warenkorbBean" scope="session" class=
"com.entwickler.eclipsebuch.onlineshopWebClient.WarenkorbCtrl"/
>
<jsp:useBean id="artikelBean" scope="session" class=
"com.entwickler.eclipsebuch.onlineshopWebClient.ArtikelCtrl"/>
<!DOCTYPE HTML PUBLIC "-//w3c//dtd html 4.0 transitional//en">
<html>

<p><b>Folgende Artikel sind im Warenkorb:</b><br></p><p>
<%
warenkorbBean.add(request.getParameter("artikel"));
java.util.Vector v = (java.util.Vector)
WarenkorbBean.getList().clone();
for (int i = 0;i<v.size();i++){
out.print((i+1) + ". ");
out.println(artikelBean.getArtikelbezeichnung(
Integer.parseInt((String) v.elementAt(i)))+ " <br>");
}
session.setAttribute("warenkorbBean",warenkorbBean);
%>

<(html>

Die Ausgabe wird in Abb. 7.31 dargestellt.

Abb. 7.31: Ausgabe der Artikelbezeichnungen des Warenkorbs

361
eclipse_v01.book Seite 362 Montag, 30. Januar 2006 12:02 12

Entity Beans

Erstellung von Finder-Methoden


In Kapitel 7.11 werden Finder-Methoden detailliert beschrieben. Im vorliegenden Ab-
schnitt wollen wir die konkrete Umsetzung untersuchen. Auf der Startseite unserer kleinen
Webanwendung soll das Artikelangebot dargestellt werden. An die Stelle der Eingabe der
Artikelnummer tritt die Auswahl eines Artikels aus einer Liste. Dazu müssen also zunächst
alle Informationen zu den einzelnen Artikeln aus der Datenbank präsentiert werden.
Hierfür programmieren wir in die Klasse ArtikelBean die Finder-Methode ejbFind-
AllOnline. Diese Methode liefert alle Artikel, die online verfügbar sind, oder anders aus-
gedrückt, für die das Kennzeichen online auf 1 steht.
In Anlehnung an die Structured Query Language (SQL) für den Zugriff auf Datenbanken
wurde von Sun in der EJB-Spezifikation die EJB Query Language (EJB-QL) für den Zu-
griff auf die EJBs im EJB-Container eingeführt.
Da wir eine EJB mit Container Managed Persistence verwenden, dürfen wir die Finder-Me-
thode zum Auffinden aller Artikel, die online verfügbar sind, nicht selber implementieren,
sondern müssen dies dem EJB-Container überlassen. Wir deklarieren dazu im Home Inter-
face die Methode findAllOnline(). Die Deklaration reicht selbstverständlich nicht da-
für aus, dass der EJB-Container die Methode implementieren kann. Es ist noch ein Eintrag
im Deployment-Deskriptor erforderlich. Dazu fügen wir im Element <entity> der Datei
ejb-jar.xml das Element <query> ein:


<entity >

<query>
<query-method>
<method-name>findAllOnline</method-name>
<method-params>
</method-params>
</query-method>
<ejb-ql>
<![CDATA[SELECT OBJECT(a)
FROM Artikel as a
WHERE a.online = TRUE]]>
</ejb-ql>
</query>
</entity>

Das <ejb-ql>-Element beinhaltet eine Zeichenkette, die in diesem Fall aus drei Bereichen
besteht:
1. In der FROM-Klausel wird der Bezeichner für den so genannten Abstract Schema Name,
der im Deployment-Deskriptor definiert ist, angegeben. Es handelt sich hierbei nicht
um den Tabellennamen aus der Datenbank. Selbstverständlich können aber beide Be-
zeichner den gleichen Namen besitzen. Der Bezeichner Artikel wird durch den Alias
a ersetzt.

362
eclipse_v01.book Seite 363 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

2. In der SELECT-Klausel stehen die Felder, die von der EJB-QL-Abfrage berücksichtigt
werden. Wird hier Object(a) angegeben, ist damit gemeint, dass alle Felder mit auf-
genommen werden.
3. In der WHERE-Klausel wird ein Filter angegeben, so dass nicht nach allen Entity Beans
gesucht wird, sondern nur nach denen, für die das Kriterium erfüllt ist.
In der Spezifikation der Sprache EJB-QL gibt es keine ORDER BY-Klausel wie bei SQL.
Das ist unter anderem darauf zurückzuführen, dass die Groß- und Kleinschreibweise bei
der Sortierung in verschiedenen Datenbanken und in Java unterschiedlich behandelt wird.
Dennoch stellen einige Anbieter von Applikationsservern eine proprietäre Lösung zur Ver-
fügung.

Einsatz von Finder-Methoden im Client


Wir wollen uns wieder auf einen Webclient beschränken, um den Gebrauch von Finder-Me-
thoden zu behandeln. Zunächst erweitern wir die Klasse ArtikelCtrl um die Methode
getArtikel, in der der Aufruf der Finder-Methode findAllOnline umgesetzt wird. Der
Aufruf erfolgt über die Referenz auf das Home-Objekt ah, welches im Konstruktor der
Klasse erzeugt worden ist. Die Methode liefert die Collection mit den Referenzen auf die
gefundenen Entity Beans zurück.

public Collection getArtikel(){


try {
return ah.findAllOnline();
} catch (FinderException e) {
return null;
}
}

Wir werden auf dem JSP-Dokument der Eingabeseite mit dieser Collection weiterarbeiten
und die Referenzen auf die ArtikelBean-Objekte verwenden. Der Code für die Eingabe-
seite index.jsp1 sieht dann wie folgt aus:

<%@ page language="java" %>


<%@ page import=
"com.entwickler.eclipsebuch.entitybeans.ArtikelLocal" %>
<jsp:useBean id="artikelBean" scope="session" class=
"com.entwickler.eclipsebuch.onlineshopWebClient.ArtikelCtrl"/>

<% session.setAttribute("artikelBean",artikelBean); %>

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel07/Kap7310OnlineshopWebClient

363
eclipse_v01.book Seite 364 Montag, 30. Januar 2006 12:02 12

Entity Beans

Im ersten Bereich wird eine Instanz der Klasse ArtikelCtrl aus dem Package com.ent-
wickler.eclipsebuch.onlineshopWebClient unter dem Namen artikelBean
referenziert. Damit diese Instanz auch auf den anderen Seiten der Anwendung während der
gesamten Sitzung verfügbar ist, wird sie als Attribut im session-Objekt gespeichert.

<html>
<head><title> Artikelauswahl </title></head>
<body>
<h1><center>Artikelauswahl</center></h1>
<table border='1'>
<tr><td>Artikelnummer</td>
<td>Artikelbezeichnung</td><td>Artikelpreis</td></tr>

Der zweite Bereich besteht aus reinem HTML. Es wird eine Tabelle mit den Spaltenüber-
schriften Artikelnummer, Artikelname und Artikelpreis definiert. Anschließend werden in
die Zellen die entsprechenden Daten der Artikel eingetragen.

<%
java.util.Collection colArtikel = artikelBean.getArtikel();
java.util.Iterator it = colArtikel.iterator();
while (it.hasNext()){
ArtikelLocal artikel = (ArtikelLocal) it.next();
out.write("<tr><td>");
out.write("<a href='ausgabe.jsp?artikel=");
out.write(artikel.getANr().toString() + "'>");
out.write(artikel.getANr() + "</a>");
out.write("</td><td>");
out.write(artikel.getABz());
out.write("</td><td>");
out.write(artikel.getAPr().toString());
out.write("</tr>");
}
%>
</table>
</body>
</html>

Der dritte Bereich beinhaltet den Java-Code, in dem zunächst die Referenzen auf die Entity
Beans in einer Collection gespeichert werden. Da in der Collection Objekte vom Typ Ob-
ject stehen, müssen sie noch in Objekte vom Typ ArtikelLocal konvertiert werden, be-
vor auf die Getter-Methoden zugegriffen werden kann. Bei Eingabe der URL http://local-
host:8080/OnlineshopWEB/index.jsp erscheint die Ausgabe, die in Abb. 7.32 zu sehen ist.

364
eclipse_v01.book Seite 365 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

Abb. 7.32: Ausgabe der Artikel

Erzeugen neuer Entity Bean-Objekte


Die Erzeugung einer neuen Entity Bean bedeutet, dass ein Datensatz in die entsprechende
Datenbanktabelle eingetragen wird und die Entity Bean allen Anwendern zur Verfügung
steht. Das Erstellen einer neuen Entity Bean wird in der Regel nur von einem bestimmten
Benutzerkreis vorgenommen.
Zum Erzeugen einer Entity Bean wird die create-Methode des Home-Objekts aufgeru-
fen. Dadurch wird die entsprechende ejbCreate-Methode der Entity Bean ausgeführt.
Diese ist bei CMP von dem EJB-Container implementiert worden und legt einen neuen
Datensatz an. Außerdem wird eine Referenz auf das neue Objekt von der create-Methode
zurückgegeben.
Wir schreiben ein JSP-Dokument erfassung.jsp1 zur Erfassung der Daten und fügen in die
Klasse ArtikelCtrl die Methode create hinzu. In der Methode create wird eine neue
Referenz auf ein neues Entity Bean-Objekt erzeugt.

public class ArtikelCtrl {


private ArtikelLocal artBean;
private ArtikelLocalHome ah;
...
public void create(Integer id, String abz, Float preis) {
try {
artBean = ah.create(id,abz,preis);
} catch (CreateException e) {
e.printStackTrace();
}
}
...
}

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel07/Kap7310OnlineshopWebClient

365
eclipse_v01.book Seite 366 Montag, 30. Januar 2006 12:02 12

Entity Beans

Auf dem JSP-Dokument erfassung.jsp stehen Textfelder zur Eingabe der Artikelnummer,
der Artikelbezeichnung und des Preises zur Verfügung. Die Eingabemaske ist in Abb. 7.33
dargestellt.

Abb. 7.33: Erfassungsmaske für Artikel

Der Einfachheit halber nehmen wir an, dass jeder Artikel, der über dieses Formular ein-
gegeben wird, online zur Verfügung steht und dass das Online-Kennzeichen in der Tabelle
Artikel auf true gesetzt wird. Nach Eingabe eines Artikels und Klick auf den ERFASSEN-
Button wird dieselbe Seite erneut aufgerufen. Es wird überprüft, ob eine Eingabe erfolgt ist,
indem getestet wird, ob der Parameter für die Artikelnummer gleich null ist oder nicht.
Das JSP-Dokument ist wie folgt aufgebaut:

<%@ page language="java" %>


<jsp:useBean id="artikelBean" scope="request" class=
"com.entwickler.eclipsebuch.onlineshopWebClient.ArtikelCtrl"/>
<!DOCTYPE HTML PUBLIC "-//w3c//dtd html 4.0 transitional//en">

<html>
<head><title>Erfassung der Artikel</title></head>
<body bgcolor="#FFFFFF">

<% if (request.getParameter("id") != null){


int id = Integer.parseInt(request.getParameter("id"));
String abz = request.getParameter("abz");
float preis = Float.parseFloat(request.getParameter("apr"));
artikelBean.create(new Integer(id),abz,new Float(preis));
}
%>

<form action='erfassung.jsp'>
<table>
<tr><td>Artikelnummer.: </td>
<td><input type='text' name='id'> </td></tr>
<tr><td>Artikelbezeichnung.: </td>
<td><input type='text' name='abz'></td></tr>

366
eclipse_v01.book Seite 367 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

<tr><td>Preis: </td>
<td><input type='text' name='apr'></td></tr>
</table>
<p>
<input type='submit' name='submit' value='Erfassen'>
</p>
</form>
</body>
</html>

WTP zur Erstellung der Entity Bean


Für die Erstellung der Entity Bean ArtikelBean können wir auch die Web Tools Platform
und XDoclet nutzen. Wir erzeugen dazu eine neue Java-Klasse, bezeichnen sie mit Arti-
kelBean und schreiben den Code im XDoclet-Bereich selbstständig. In der zugrunde
liegenden Version der WTP gibt es keinen Assistenten, der für Entity Beans den XDoclet-
Bereich generiert.
Für die Klasse ArtikelBean sind folgende Angaben zu machen:

/**
* @ejb.bean name="Artikel"
* type="CMP"
* cmp-version="2.x"
* schema="Artikel"
* local-jndi-name="ArtikelLocal"
* jndi-name="Artikel"
* reentrant="true"
* primkey-field="aNr"
*
* @ejb.persistence
* table-name="Artikel"
*
* @ejb.home local-class=
* "com.entwickler.eclipsebuch.onlineshop.ArtikelLocalHome"
* @ejb.interface local-class=
* "com.entwickler.eclipsebuch.onlineshop.ArtikelLocal"
* @ejb.home class=
* "com.entwickler.eclipsebuch.onlineshop.ArtikelHome"
* @ejb.interface class=
* "com.entwickler.eclipsebuch.onlineshop.Artikel"
*
* @ejb.pk class="java.lang.Integer"
*/

367
eclipse_v01.book Seite 368 Montag, 30. Januar 2006 12:02 12

Entity Beans

In diesem Bereich legen wir fest, wie die Entity Bean, ihre Interfaces und JNDI-Namen hei-
ßen, von welcher Art die Persistenz ist, welches Attribut der eindeutige Primärschlüssel ist
sowie aus welcher Tabelle die Daten stammen.
Die Daten, die in dieser Entity Bean gespeichert werden sollen, definieren wir, indem wir
die abstrakten Zugriffsmethoden festlegen. Für den Primärschlüssel aNr muss im XDoclet-
Bereich diese Information mit angegeben werden:

/**
* @ejb.persistent-field
* @ejb.persistence
* column-name="aNr"
* sql-type="INTEGER"
* read-only="false"
* @ejb.pk-field
*
* @ejb.interface-method view-type="both"
*
*/
public abstract Integer getANr();

/**
* @ejb.persistence
*/
public abstract void setANr(Integer nr);

Die Zugriffsmethoden für die Daten, die kein Primärschlüssel sind, werden folgenderma-
ßen definiert:

/**
* @ejb.persistence
* column-name="aBz"
* sql-type="VARCHAR"
* read-only="false"
* @ejb.interface-method view-type="both"
*/
public abstract java.lang.String getABz();

/**
* @ejb.persistence
*/
public abstract void setABz(java.lang.String aBz);

Im XDoclet-Bereich zu einer Zugriffsmethode werden also zunächst die Informationen für


die Container Managed Persistence angegeben, nämlich, wie das zugehörige Feld in der Ta-

368
eclipse_v01.book Seite 369 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

belle der Datenbank heißt, von welchem Typ es ist und ob es verändert werden darf. Diese
Daten werden in den Deployment-Deskriptor ejb-jar.xml übernommen:

<cmp-field id="CMPAttribute_2">
<description><![CDATA[]]></description>
<field-name>aBz</field-name>
</cmp-field>

Wenn wir für XDoclet eingestellt haben, dass auch die JBoss-spezifischen Deployment-
Deskriptoren erzeugt werden sollen (WINDOW | PREFERENCES | XDCOLET | EJBDOCLET 
JBOSS), werden die beiden Dateien jboss.xml und jbosscmp-jdbc.xml generiert. In der jbos-
scmp-jdbc.xml werden für die Daten der Entity Bean die zugehörigen Namen der Felder in
der Tabelle der Datenbank angegeben:

<cmp-field>
<field-name>aBz</field-name>
<read-only>false</read-only>
<column-name>aBz</column-name>
</cmp-field>

Sind alle Angaben im XDoclet-Bereich gemacht, können wir über das Kontextmenü des
Projekts mit RUN XDOCLET die Generierung der Klassen und Interfaces starten. In der Con-
sole View wird angezeigt, welche Dateien erzeugt werden.
Bei den neuen Klassen und Interfaces handelt es sich im Wesentlichen um die Local/Remo-
te und Local Home/Remote Home Interfaces, wobei die Klasse ArtikelCMP als Vorlage
für die zugehörigen Entity Beans definiert wird. ArtikelCMP ist abgeleitet von der Klasse
ArtikelBean und ebenfalls abstrakt. Die Erweiterung besteht vor allem darin, dass die
Methoden des Interface javax.ejb.EntityBean formal implementiert werden.
In der Datei jbosscmp-jdbc.xml muss noch angegeben werden, wie auf die Datenbank zu-
gegriffen werden soll. Im Element <defaults> wird dem Attribut datasource der
JNDI-Name der Datenbank java:/OnlineshopDS und dem Attribut datasourcemapping
der Name des Datenbanktyps mySQL zugeordnet:

<defaults>
<datasource>java:/OnlineshopDS</datasource>
<datasource-mapping>mySQL</datasource-mapping>
<preferred-relation-mapping>
foreign-key
</preferred-relation-mapping>
</defaults>

Mit diesen Einstellungen kann der JBoss Server den Zugriff auf die entsprechende Daten-
bank für die Persistenz realisieren.

369
eclipse_v01.book Seite 370 Montag, 30. Januar 2006 12:02 12

Entity Beans

Sind alle Einstellungen richtig vorgenommen, kann das Deployment der Entity Bean auf
den JBoss Server erfolgen. Dazu erzeugen wir in der Server View eine neue Instanz und
ordnen dieser die Entity Bean zu.

7.11.4 Entity Bean (BMP)


Entwicklung
Die Entwicklung einer Entity Bean mit Bean Managed Persistence bedeutet, dass der Ent-
wickler alle Zugriffe auf die Datenbank selbstständig programmiert und dass diese nicht
vom EJB-Container unterstützt werden. Eine BMP Entity Bean implementiert wie eine
CMP Entity Bean das Interface javax.ejb.EntityBean. Die Methoden des Interface
werden wie folgt verwendet:
쐌 public void setEntityContext(javax.ejb.EntityContext ctx)
Diese Methode wird vom EJB-Container aufgerufen, nachdem die Instanz erzeugt wor-
den ist. In der Regel wird hier die Verbindung zur Datenbank aufgebaut.
쐌 public void unsetEntityContext()
Diese Methode wird vom EJB-Container aufgerufen, bevor die Instanz zerstört wird.
Hier wird dann die Verbindung zur Datenbank wieder geschlossen.
쐌 public void ejbLoad()
Der EJB-Container ruft diese Methode auf, um den Status der Entity Bean durch Laden
der Daten aus der Datenbank zu synchronisieren. Sie wird beispielsweise zu Beginn ei-
ner Transaktion aufgerufen. Zumeist wird eine Auswahlabfrage durchgeführt und die
Daten des entsprechenden Datensatzes werden in die Instanzvariablen der Entity Bean
gespeichert.
쐌 public void ejbStore()
Der EJB-Container ruft diese Methode auf, um den Zustand der Entity Bean durch Spei-
chern der Daten in die Datenbank zu synchronisieren. Sie wird beispielsweise am Ende
einer Transaktion aufgerufen. Mithilfe einer Update-Abfrage können die Daten der En-
tity Bean in den entsprechenden Datensatz gespeichert werden.
쐌 public void ejbRemove() throws javax.ejb.RemoveException
Die ejbRemove-Methode wird aufgerufen, wenn eine Entity Bean gelöscht wird. Sie
dient wiederum zur Synchronisation der Daten mit der Datenbank, so dass hier eine De-
lete-Abfrage an die Datenbank für den entsprechenden Datensatz gesendet wird.
쐌 public void ejbActivate()
Die Erzeugung von Instanzen der Entity Bean-Klasse übernimmt ausschließlich der
EJB-Container. Aus z.B. Performancegründen werden zum Teil Instanzen erzeugt und
einem so genannten Instanzenpool zugeordnet, ohne dass direkt auf diese zugegriffen
wird. Wenn eine Entity Bean benötigt wird, wird eine Instanz aus dem Pool verwendet.
Eine Instanziierung findet also nicht statt. Es wird aber die ejbActivate-Methode
aufgerufen.
쐌 public void ejbPassivate()
Wenn ein EJB-Objekt nicht mehr benötigt wird, wird nicht die Instanz zerstört, sondern
das Objekt wird in den Instanzenpool zurückgelegt. Dabei wird die ejbPassivate-
Methode aufgerufen.

370
eclipse_v01.book Seite 371 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

Neben den Methoden des Interface EntityBean gehören zu einer Entity Bean noch fol-
gende Methoden:
쐌 ejbCreate
In dieser Methode wird in der Regel eine neue Entity Bean erzeugt und persistent ge-
speichert. Dazu muss in der entsprechenden Datenbanktabelle ein Datensatz eingefügt
werden.
쐌 ejbPostCreate
Diese Methode wird im Anschluss an die ejbCreate-Methode aufgerufen und sie be-
sitzt dieselbe Signatur.
쐌 ejbFindByPrimaryKey
Dieser Methode wird der Schlüsselwert übergeben und bei Existenz eines korrespon-
dierenden Datensatzes wird der Wert wieder zurückgegeben.
쐌 Finder-Methoden
Eine Finder-Methode sucht bestimmte Datensätze nach einem vorgegebenen Kriterium
und speichert die Werte des Primärschlüssels in einem Collection-Objekt, welches von
der Methode zurückgegeben wird.

Beispiel
Wir erweitern wiederum das bisher verwendete Beispiel des Onlineshops. Bisher wurden
die Bestellungen eines Kunden in der Datenbank gespeichert, ohne dass Entity Beans ver-
wendet worden sind. Nun wollen wir eine BMP Entity Bean für die Bestellung verwenden.
Die Bean besitzt als Schlüsselattribut die Bestellnummer vom Typ Integer. Weiterhin
werden die Attribute Kundennummer und Bestelldatum sowie die Bestellpositionen ge-
speichert. Für die Bestellpositionen verwenden wir eine Klasse, in der die Artikelnummer
mit der dazugehörigen Menge gespeichert wird. Wir bezeichnen sie mit Bestellposi-
tion und programmieren die entsprechenden Getter- und Setter-Methoden.
Die Klasse BestellungBean sieht dann wie folgt aus1:

import com.entwickler.eclipsebuch.helper.Bestellposition;
import javax.ejb.EJBException;
import javax.ejb.EntityBean;
import javax.ejb.EntityContext;
import javax.ejb.RemoveException;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

import java.rmi.RemoteException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel07/Kap71103Onlineshop

371
eclipse_v01.book Seite 372 Montag, 30. Januar 2006 12:02 12

Entity Beans

import java.sql.SQLException;
import java.sql.Statement;
import java.util.Hashtable;
import java.util.Iterator;

public abstract class BestellungBean implements EntityBean {


// Deklaration der persistenten Daten
private Integer bNr;
private Integer kNr;
private String bDatum;
private Hashtable bestellpositionen = new Hashtable();

// Deklaration von weiteren Instanzvariablen


private Connection conn;
private EntityContext context;

// ejbCreate- und ejbPostCreate-Methoden


public Integer ejbCreate(Integer b, Integer k,
String d, Hashtable p) {…}
public void ejbPostCreate(Integer b, Integer k,
String d, Hashtable p) {…}

// Finder-Methoden
public Integer ejbFindByPrimaryKey(Integer b)
throws FinderException{…}

// Methoden des Interface EntityBean


public void ejbActivate() throws EJBException, RemoteException
{…}
public void ejbLoad() throws EJBException, RemoteException {…}
public void ejbPassivate() throws EJBException, RemoteException
{…}
public void ejbRemove() throws RemoveException, EJBException,
RemoteException {…}
public void ejbStore() throws EJBException, RemoteException {…}
public void setEntityContext(EntityContext arg0)
throws EJBException, RemoteException {…}
public void setKNr(Integer integer) {…}
public void unsetEntityContext()
throws EJBException, RemoteException {…}

// Setter- und Getter-Methoden für die Daten der Entity Bean


public String getBDatum() {…}
public Hashtable getBestellpositionen() {…}
public Integer getBNr() {…}
public Integer getKNr() {…}
public void setBDatum(String string) {…}

372
eclipse_v01.book Seite 373 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

public void setBestellpositionen(Hashtable hashtable) {…}


public void setBNr(Integer integer) {…}

// weitere Business-Methoden

}

Eine Bestellung besteht aus mehreren Bestellpositionen. Für eine einzelne Bestellposition
wird ein Objekt der Klasse Bestellposition erzeugt und in einem Hashtable-Objekt
gespeichert. Die Identifizierung der Objekte innerhalb des Hashtable-Objekts erfolgt
über die Bestellpositionsnummer, die auch in der Datenbanktabelle gespeichert ist.
Beim Anlegen, Ändern, Lesen oder Löschen von Daten müssen in diesem Beispiel jeweils
die beiden Tabellen Bestellung und Bestellpos angesprochen werden. Beim Ändern der Da-
ten zu einer Bestellung ergibt sich zusätzlich das Problem, dass untersucht werden muss,
welche Bestellpositionen hinzugefügt oder gelöscht worden sind.
Eine neue Bestellung wird angelegt, indem eine Entity Bean vom Home-Objekt referen-
ziert wird. Vorher muss aber der EJB-Container das Entity Bean-Objekt erzeugen und in
den so genannten Instanzenpool ablegen. Hierbei wird die Methode setEntityContext
des EntityBean-Interface aufgerufen. In dieser Methode programmieren wir den Verbin-
dungsaufbau zur Datenbank. Bei dieser Variante wird für jede Entity Bean ein eigenes Con-
nection-Objekt erzeugt bzw. eine eigene Datenbankverbindung hergestellt.
Alternativ können wir auch den Verbindungsaufbau in eine statuslose Session Bean ausla-
gern und jedes Mal dasselbe Connection-Objekt referenzieren. In diesem Fall benötigen
wir nur eine Datenbankverbindung.
Bei der im Folgenden beschriebenen Methode wird aber für jede Entity Bean eine eigene
Datenbankverbindung hergestellt. Je nach Konfiguration des Servers kann die Anzahl der
offenen Datenbankverbindungen beschränkt sein.

public void setEntityContext(EntityContext arg0)


throws EJBException, RemoteException {
try {
context = arg0;
InitialContext ctx = new InitialContext();
DataSource jdbcFactory = (DataSource)
ctx.lookup("java:/OnlineshopDS");
conn = jdbcFactory.getConnection();
} catch (SQLException e) {
e.printStackTrace();
} catch (NamingException e) {
e.printStackTrace();
}
}

373
eclipse_v01.book Seite 374 Montag, 30. Januar 2006 12:02 12

Entity Beans

Das an die Methode setEntityContext übergebene EntityContext-Objekt speichern


wir in der Instanzvariablen context ab. Über dieses Objekt können wir beispielsweise auf
den Primärschlüssel oder das Home-Objekt zugreifen.
Die korrespondierende Methode, die aufgerufen wird, wenn die Entity Bean entfernt wird,
trägt den Bezeichner unsetEntityContext. Hier wird die Verbindung zur Datenbank ge-
schlossen:

public void unsetEntityContext()


throws EJBException, RemoteException {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}

Wenn das EntityBean referenziert wird und damit aus dem Instanzenpool in den aktiven
Status übergeht, wird die ejbActivate-Methode aufgerufen. Dies geschieht beispiels-
weise dann, wenn eine Entity Bean aus einer Finder-Methode heraus referenziert wird.

public void ejbActivate() throws EJBException, RemoteException {


bNr = (Integer) context.getPrimaryKey();
}

Entsprechend wird die ejbPassivate-Methode aufgerufen, wenn die Entity Bean in den
Instanzenpool zurückgelegt und somit nicht mehr referenziert wird.

public void ejbPassivate() throws EJBException, RemoteException {


bNr = null;
}

Das Erzeugen einer neuen Entity Bean erfolgt mithilfe der ejbCreate-Methode, die vom
EJB-Container aufgerufen wird, wenn das Home-Objekt die create-Methode aufruft. Ne-
ben der Entity Bean müssen ein Datensatz in der Tabelle Bestellung und eventuell Daten-
sätze in der Tabelle Bestellpos angelegt werden. Wir definieren zwei ejbCreate-Metho-
den. Die erste erhält alle notwendigen Daten als Parameter übergeben. Dazu zählen die
Bestellnummer, die Kundennummer, das Bestelldatum und ein Hashtable-Objekt mit den
einzelnen Bestellpositionen (Artikelnummer und Menge):

374
eclipse_v01.book Seite 375 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

public Integer ejbCreate(Integer b, Integer k,


String d, Hashtable p){
try {
PreparedStatement prepStat;
String sql;
Integer posNr;
sql = "INSERT INTO Bestellung (bNr, kNr, bDatum) " +
"VALUES (?,?,?)";
prepStat = conn.prepareStatement(sql);
prepStat.setInt(1,b.intValue());
prepStat.setInt(2,k.intValue());
prepStat.setString(3,d);
prepStat.executeUpdate();
prepStat.close();
sql = "INSERT INTO Bestellpos (posNr, bNr, aNr, menge) " +
"VALUES (?,?,?,?)";
prepStat = conn.prepareStatement(sql);
Iterator it = p.keySet().iterator();
while (it.hasNext()){
posNr = (Integer) it.next();
Bestellposition pos = (Bestellposition) p.get(posNr);
prepStat.setInt(1,posNr.intValue());
prepStat.setInt(2,b.intValue());
prepStat.setInt(3,pos.getANr());
prepStat.setDouble(4,pos.getMenge());
prepStat.executeUpdate();
}
prepStat.close();
bNr = b;
kNr = k;
bDatum = d;
bestellpositionen = p;
return bNr;
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}

Da die Bestellnummer in der Regel nicht vorgegeben wird, sondern in Abhängigkeit von
den bereits vergebenen generiert wird, schreiben wir eine zweite ejbCreate-Methode, in
der ein neuer Wert für den Schlüssel berechnet wird.

public Integer ejbCreate(Integer k, String d, Hashtable p){


r = this.newKeyValue();
return ejbCreate(bNr,k,d,p);
}

375
eclipse_v01.book Seite 376 Montag, 30. Januar 2006 12:02 12

Entity Beans

In der Methode newKeyValue erzeugen wir ein ResultSet-Objekt aus allen existieren-
den Bestellnummern, wobei dies absteigend sortiert wird. Zu dem ersten und damit größten
Wert addieren wir den Wert eins hinzu und geben das Ergebnis als neuen Schlüsselwert zu-
rück.

private Integer newKeyValue(){


try {
String sql;
sql = "SELECT bNr FROM Bestellung ORDER BY bNr DESC";
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery(sql);
if (!rs.next()){
return new Integer(0);
}else{
return new Integer(rs.getInt("bNr")+1);
}
} catch (SQLException e) {
throw new EJBException();
}
}

Der EJB-Container verwendet die ejbLoad- und ejbStore-Methode, falls eine Entity
Bean aus der Datenbank gelesen oder Veränderungen an die Datenbank gegeben werden
sollen. Die Methoden sehen folgendermaßen aus:

public void ejbLoad() throws EJBException, RemoteException {


String sql;
Bestellposition pos;
try {
Statement stat = conn.createStatement();
sql = "SELECT bNr, kNr, BDatum FROM Bestellung " +
"WHERE bNr=" + this.bNr.toString();
ResultSet rs = stat.executeQuery(sql);
if (rs.next()){
this.kNr = new Integer(rs.getInt("kNr"));
this.bDatum = rs.getString("bDatum");
rs.close();
sql = "SELECT posNr, bNr, aNr, menge FROM Bestellpos " +
"WHERE bNr=" + this.bNr.toString() + " ORDER BY posNr";
rs = stat.executeQuery(sql);
while (rs.next()){
pos = new Bestellposition
(rs.getInt("aNr"),rs.getDouble("menge"));

376
eclipse_v01.book Seite 377 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

bestellpositionen.put(rs.getObject("posNr"),pos);
}
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}

Bei der ejbStore-Methode werden im ersten Schritt die Daten in der Tabelle Bestellung
aktualisiert. Anschließend werden alle Bestellpositionen aus der Tabelle Bestellpos ge-
löscht, die nicht in der Hashtable der Entity Bean enthalten sind. Außerdem werden die Be-
stellpositionen, die sowohl in der Entity Bean als auch in der Tabelle Bestellpos existieren,
aktualisiert. Weiterhin werden neue Datensätze für die Bestellpositionen angelegt, die es
nicht in der Tabelle Bestellpos, aber in der Entity Bean gibt.

public void ejbStore() throws EJBException, RemoteException {


try {
PreparedStatement prepStat;
String sql;
sql = "UPDATE Bestellung SET kNr=?, bDatum=? WHERE bNr=?";
prepStat = conn.prepareStatement(sql);
prepStat.setInt(1,kNr.intValue());
prepStat.setString(2,bDatum);
prepStat.setInt(3,bNr.intValue());
prepStat.executeUpdate();
Statement stat;
stat = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
sql = "SELECT bNr, posNr FROM Bestellpos " +
"WHERE bNr=" + this.bNr.intValue() + " ORDER BY posNr";
ResultSet rs = stat.executeQuery(sql);
while (rs.next()){
if (!bestellpositionen.containsKey(
new Integer(rs.getInt("posNr")))){
/* Loeschen der Datensätze, die nicht in der Entity Bean
enthalten sind. */
Statement statDel = conn.createStatement();
sql = "DELETE FROM Bestellpos " WHERE bNr=" +
bNr.toString() + " AND posNr=" + rs.getString("posNr");
statDel.executeUpdate(sql);
}
}
rs.close();
Iterator it = bestellpositionen.keySet().iterator();

377
eclipse_v01.book Seite 378 Montag, 30. Januar 2006 12:02 12

Entity Beans

while(it.hasNext()){
int posnr = ((Integer) it.next()).intValue();
sql = "SELECT bNr, posNr FROM Bestellpos WHERE bNr=" +
this.bNr.intValue() + " AND posNr=" + posnr;
rs = stat.executeQuery(sql);
Bestellposition pos =(Bestellposition)
bestellpositionen.get(new Integer(posnr));
if (rs.next()){
/* Daten existieren in der Tabelle Bestellpos
und werden überschrieben. */
sql = "UPDATE Bestellpos SET aNr=?, menge=? " +
"WHERE bNr=? AND posNr=?";
}else{
sql = "INSERT INTO Bestellpos (aNr,menge,bNr,posNr) " +
"VALUES (?,?,?,?)";
}
prepStat = conn.prepareStatement(sql);
prepStat.setInt(1,pos.getANr());
prepStat.setDouble(2,pos.getMenge());
prepStat.setInt(3,bNr.intValue());
prepStat.setInt(4,posnr);
prepStat.executeUpdate();
}
} catch (SQLException e) {
e.printStackTrace();
}
}

Client für den Zugriff auf eine BMP Entity Bean


Für einen Client ist es grundsätzlich unerheblich, ob dieser auf eine CMP oder BMP Entity
Bean zugreift. Die Methoden, die die Entity Bean beinhalten und dem Client über die ent-
sprechenden Interfaces zur Verfügung gestellt werden, sind von ihrer Deklaration her iden-
tisch aufgebaut.
Soll beispielsweise eine Entity Bean über ihren Schlüsselwert gesucht werden, so ruft der
Client die Methode findByPrimaryKey auf. Die zugehörige ejbFindByPrimaryKey-
Methode ist bei CMP vom EJB-Container und bei BMP vom Entwickler implementiert
worden, was für den Aufruf durch den Client keinen Unterschied macht.
Aus diesem Grund wollen wir den Zugriff auf eine Entity Bean nicht noch einmal komplett
wiederholen, sondern auf den Abschnitt 7.11.3 und auf das Beispiel auf der beiliegenden
CD1, insbesondere die Klasse DatenbankCtrl aus dem Package com.entwickler.
eclipsebuch.onlineshopWebClient und das jsp-Dokument bestellung.jsp, verweisen.
An dieser Stelle soll lediglich die Methode insertBestellung dargestellt werden. Hier
werden die Daten nicht direkt in die Datenbank geschrieben, sondern es wird die entspre-

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel07/Kap71104OnlineshopWebClient

378
eclipse_v01.book Seite 379 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

chende ejbCreate-Methode der BestellungBean-Klasse ausgelöst, woraufhin eine


neue Entity Bean erzeugt und die Daten in die Datenbank übernommen werden:

public void insertBestellung(Vector artikel, int knr) {


Calendar cal = Calendar.getInstance();
String datum = cal.get(Calendar.DAY_OF_MONTH) + "."
+ (cal.get(Calendar.MONTH) + 1) + "."
+ cal.get(Calendar.YEAR);
Hashtable p = new Hashtable();
if (loginAccepted) {
int j = 0;
/* Aus dem Vector-Objekt, das die Artikelnummern enthält
* wird ein Hashtable-Objekt mit den einzelnen
* Bestellposition-Objekten erstellt.
*/
for (int i = 0; i < artikel.size(); i++) {
try {
String tmp1 = (String) artikel.elementAt(i);
Integer aNr = new Integer(Integer.parseInt(tmp1));
// artBean ist vom Typ ArtikelLocal
artBean = art.findByPrimaryKey(aNr);
p.put(new Integer(j), new Bestellposition(
artBean.getANr().intValue(), 1.0));
j++;
} catch (FinderException e) {
}
}
try {
bsBean = bs.create(new Integer(knr), datum, p);
} catch (CreateException e) {
e.printStackTrace();
}
}
}

7.12 Java Messaging Service und Message Driven Beans


7.12.1 Grundlagen
Java Messaging Service ist eine Java-API, die Interfaces bereitstellt, um Nachrichten zu er-
zeugen, versenden, empfangen und lesen. Diese Nachrichten werden asynchron zwischen
Anwendungen oder Komponenten ausgetauscht. Ähnlich wie bei der JNDI- oder JDBC-
API stellt die JMS-API ausschließlich Interfaces zur Verfügung, deren Implementierung
Drittanbietern überlassen ist.
Für das Java Messaging gibt es zwei Varianten:
1. Point-to-Point
2. Publish/Subscribe

379
eclipse_v01.book Seite 380 Montag, 30. Januar 2006 12:02 12

Java Messaging Service und Message Driven Beans

Point-to-Point-Kommunikation
Die Point-to-Point-Kommunikation wird über eine so genannte Queue gehandhabt. Ein
Producer sendet eine Nachricht an die Queue. Dort bleibt sie so lange erhalten, bis ein Con-
sumer diese abholt. Die Vorgehensweise ist in Abb. 7.34 dargestellt.

Abb. 7.34: Java Messaging Point-to-Point

Bei der Point-to-Point-Kommunikation sind im Wesentlichen folgende Punkte zu berück-


sichtigen:
쐌 Ein Consumer muss bei der Queue registriert sein, bevor Nachrichten abgeholt werden
können.
쐌 Jede Nachricht wird von einem einzigen Consumer abgeholt. Eine Nachricht kann also
nicht an verschiedene Consumer adressiert werden. Es können aber verschiedene Con-
sumer bei der Queue registriert sein, die dann unterschiedliche Nachrichten erhalten.
Außerdem können auch mehrere Producer Nachrichten an die Queue senden und ein
Consumer bei mehreren Queues registriert sein.
쐌 Die Kommunikation verläuft asynchron und zeitunabhängig. Wenn eine Nachricht an
die Queue gesendet wird, muss der Consumer diese nicht sofort abholen. Sie wird in der
Queue gespeichert. Der Consumer muss zu diesem Zeitpunkt noch nicht einmal bei der
Queue registriert sein. Der Service für die Message Queue (MQ) muss aber laufen. Bei-
spielsweise bietet der JBoss Server eine Message Queue, die abhängig von der Start-
konfiguration mit den anderen Diensten gestartet wird. Wir werden in den folgenden
Beispielen diesen MQ Service nutzen.
쐌 Der Empfang einer Nachricht muss vom Consumer bestätigt werden. Erst dann wird die
Nachricht aus der Queue entfernt.
Die Besonderheit dieser Variante des JMS ist, dass garantiert nur ein Consumer eine Nach-
richt abholt. Wenn bestimmte Aufgaben einer Anwendung, z.B. Ausführen einer Bestel-
lung oder die Buchung einer Finanztransaktion, nur einmal ausgeführt werden sollen, so
kann die Point-to-Point-Kommunikation ideal eingesetzt werden.

Publish/Subscribe-Kommunikation
Bei der Publish/Subscribe-Kommunikation kann eine Nachricht von mehreren Consumern
empfangen werden. Die Consumer abonnieren Nachrichten beim so genannten Topic, wel-
ches an die Stelle der Queue tritt. Nachrichten werden vom Producer veröffentlicht und an

380
eclipse_v01.book Seite 381 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

das Topic gesendet. Das Topic verschickt die Nachrichten an die Consumer weiter, die die-
se abonniert haben. Im Gegensatz zur Queue holen die Consumer nicht die Nachrichten ab,
sondern sie bekommen sie zugeschickt. Folglich müssen die Consumer als Abonnenten zu
dem Zeitpunkt, zu dem die Nachricht versendet wird, beim Topic registriert sein. Die Kom-
munikationswege sind in Abb. 7.35 dargestellt.

Abb. 7.35: Java Messaging Publish/Subscribe

Bei der Publish/Subscribe-Kommunikation gelten im Wesentlichen folgende Punkte:


쐌 Eine Nachricht kann von beliebig vielen (null, ein oder mehreren) Consumern empfan-
gen werden.
쐌 Es besteht im Gegensatz zur Point-to-Point-Kommunikation eine zeitliche Abhängig-
keit zwischen dem Versenden und dem Empfangen einer Nachricht. Wenn ein Consu-
mer sich erst nach dem Versenden einer Nachricht für diese registriert, so erhält er diese
Nachricht nicht mehr.
쐌 In der Regel muss ein Consumer ständig aktiv sein, damit er die Nachricht empfangen
kann.
Bei dieser Art der Kommunikation können die Abonnenten entscheiden, welche Nachrich-
ten sie empfangen möchten. Dem Publisher ist es im Allgemeinen nicht bekannt, wer seine
Nachrichten empfängt.

7.12.2 Message Driven Beans


Message Driven Beans (MDBs) sind mit der EJB 2.0-Spezifikation in den Kreis der EJBs
aufgenommen worden. Sie basieren auf einem so genannten Message Listener, der Nach-
richten über eine Queue oder ein Topic erhält und verarbeitet. Damit sind die MDBs eng mit
dem JMS verbunden.
Eine MDB implementiert die beiden Interfaces MessageListener und Message-
DrivenBean aus dem Package javax.ejb. Das Interface MessageDrivenBean beinhal-
tet analog zu Entity und Session Beans die Methoden setMessageDrivenContext,
ejbCreate und ejbRemove. Im MessageListener ist die onMessage-Methode defi-
niert.

381
eclipse_v01.book Seite 382 Montag, 30. Januar 2006 12:02 12

Java Messaging Service und Message Driven Beans

Damit sieht eine Klasse1 für eine entsprechende EJB wie folgt aus:

public class MeldungBean implements


MessageDrivenBean, MessageListener {

private javax.ejb.MessageDrivenContext messageContext = null;

public void setMessageDrivenContext(


javax.ejb.MessageDrivenContext messageContext)
throws javax.ejb.EJBException {
this.messageContext = messageContext;
}

public void ejbCreate() { }


public void ejbRemove() {
messageContext = null;
}

public void onMessage(javax.jms.Message message) { }


}

Die onMessage-Methode wird ausgeführt, wenn die MDB eine Nachricht empfängt. In
dem Message-Objekt werden die Informationen zu der Nachricht übergeben. Die MDB hat
keine weiteren Methoden, die direkt oder indirekt von außen aufgerufen werden können.
Die gesamte Business-Logik wird über die onMessage-Methode angestoßen. Der Sender
einer Nachricht erhält auch keine Ergebnisse als Rückgabewert, da die onMessage-Metho-
de vom Typ void ist.
Im Gegensatz zu den anderen EJBs gibt es bei den MDBs keine Home- oder Remote Inter-
faces. Die MDBs registrieren sich bei der Queue oder beim Topic und können dann von ei-
nem Client Nachrichten empfangen. Die ejbCreate- und ejbRemove-Methode werden
vom EJB-Container aufgerufen.
Es sind aber einige Einstellungen im Deployment-Deskriptor ejb-jar.xml vorzunehmen.
Hierfür gibt es das Element message-driven, in dem der EJB-Name, die EJB-Klasse und
der Typ (Queue oder Topic) definiert werden. Für eine MDB vom Typ Queue kann das Ele-
ment folgendermaßen definiert werden.

<!-- Message Driven Beans -->


<message-driven>
<ejb-name>Meldung</ejb-name>
<ejb-class>nachrichten.MeldungBean</ejb-class>
<transaction-type>Bean</transaction-type>
<acknowledge-mode>Auto-acknowledge</acknowledge-mode>

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel07/Kap71201MDB

382
eclipse_v01.book Seite 383 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

<message-driven-destination>
<destination-type>javax.jms.Queue</destination-type>
<subscription-durability>NonDurable</subscription-durability>
</message-driven-destination>
</message-driven>

Der einzige Unterschied zu einer MDB vom Typ Topic besteht an dieser Stelle in dem Ein-
trag des Elements destination-type:

<message-driven >
<ejb-name>Warnung</ejb-name>
<ejb-class>nachrichten.WarnungBean</ejb-class>
<transaction-type>Bean</transaction-type>
<acknowledge-mode>Auto-acknowledge</acknowledge-mode>
<message-driven-destination>
<destination-type>javax.jms.Topic</destination-type>
<subscription-durability>NonDurable</subscription-durability>
</message-driven-destination>
</message-driven>

Neben der Datei ejb-jar.xml muss außerdem die Datei jboss.xml konfiguriert werden. Hier
werden wie bei den anderen Beans unter anderem die JNDI-Namen vergeben. In der ein-
fachsten Form ist folgender Eintrag für eine MDB vom Typ Queue bzw. Topic einzufügen:

<message-driven>
<ejb-name>Meldung</ejb-name>
<destination-jndi-name>queue/MdbQueue</destination-jndi-name>
</message-driven>

<message-driven>
<ejb-name>Warnung</ejb-name>
<destination-jndi-name>topic/MdbTopic</destination-jndi-name>
</message-driven>

7.12.3 Senden einer Nachricht an eine Message Driven Bean

Senden an eine Queue


Um eine Nachricht an eine Queue zu versenden, müssen mehrere Schritte durchgeführt
werden:
쐌 Zunächst erzeugen wir ein Eclipse-Projekt1, in dessen Classpath wir die für die Kom-
munikation mit dem JBoss Server notwendigen Bibliotheken (vgl. Abschnitt 7.8) ein-
binden.

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel07/Kap71202MDBSender

383
eclipse_v01.book Seite 384 Montag, 30. Januar 2006 12:02 12

Java Messaging Service und Message Driven Beans

쐌 Beim Zugriff muss ein Objekt für den Kontext erzeugt werden, in dem die Queue sich
befindet. In unserem Beispiel beinhaltet der Applikationsserver JBoss die Queue bzw.
die Message Driven Bean. Es wird also zunächst ein Objekt vom Typ der Klasse Ini-
tialContext erzeugt. Beim Remote-Zugriff müssen einige Eigenschaften an den
Konstruktor übergeben werden, die im Detail im Kapitel 7.8 erläutert werden.
쐌 Ein Objekt der Klasse QueueConnectionFactory muss erzeugt werden, mit der an-
schließend die Queue identifiziert werden kann. Der Name der QueueConnection-
Factory lautet ConnectionFactory.
쐌 Ein Objekt der Klasse Queue wird über die QueueConnectionFactory erzeugt. Hierbei
handelt es sich um eine Referenz auf die Queue, deren JNDI-Namen wir an die ent-
sprechende lookup-Methode übergeben. Den JNDI-Namen haben wir in der Datei
jboss.xml mit queue/MdbQueue festgelegt.
쐌 Das QueueConnection-Objekt stellt die Verbindung zur Queue dar. Dieses wird eben-
falls von der QueueConnectionFactory erzeugt.
쐌 Es wird außerdem ein QueueSession-Objekt benötigt, welches sich auf eine Queue-
Connection bezieht.
쐌 Das QueueSender-Objekt dient dazu, eine Nachricht an die Queue zu senden. Dieses
Objekt kann während einer QueueSession für eine Queue verwendet werden.
쐌 Schließlich ist noch ein Objekt der Klasse Message oder einer Subklasse zu erzeugen
und mit Daten zu füllen. Dann wird die Nachricht mithilfe der send-Methode des
QueueSender-Objekts an die Queue versendet.
Ein Beispiel für einen Sender wird in der Klasse Sender dargestellt. Der Einfachheit halber
wird lediglich ein Text an die Nachricht übergeben:

package com.entwickler.eclipsebuch.queue;

import java.util.Hashtable;
import javax.jms.JMSException;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class Sender {


private TextMessage message;
private QueueSender queueSender;
private QueueSession queueSession;
private QueueConnection queueConnection;

384
eclipse_v01.book Seite 385 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

private QueueConnectionFactory qcf;


private Queue queue;
private InitialContext context;

public Sender(){
try {
Hashtable props = new Hashtable();
props.put(InitialContext.INITIAL_CONTEXT_FACTORY,
"org.jnp.interfaces.NamingContextFactory");
props.put(InitialContext.PROVIDER_URL,
"jnp://127.0.0.1:1099");
props.put(InitialContext.URL_PKG_PREFIXES,
"org.jboss.naming:org.jnp.interfaces");
InitialContext context = new InitialContext(props);
qcf = (QueueConnectionFactory) context.lookup
("ConnectionFactory");
queue = (Queue) context.lookup("queue/MdbQueue");
queueConnection = qcf.createQueueConnection();
queueSession = queueConnection.createQueueSession(
false,Session.AUTO_ACKNOWLEDGE);
queueSender = queueSession.createSender(queue);
message = queueSession.createTextMessage();
message.setText("Das ist eine Testmessage");
queueSender.send(message);
queueSender.close();
queueSession.close();
queueConnection.close();
} catch (JMSException e1) {
e1.printStackTrace();
} catch (NamingException e) {
e.printStackTrace();
}
}
public static void main(String[] args){
Sender s = new Sender();
}
}

Wenn die MDB im EJB-Container liegt, erscheint nach Ablauf des Konstruktors der Klasse
Sender eine Meldung in der Konsole des Applikationsservers JBoss, die in Abb. 7.36 dar-
gestellt ist.

385
eclipse_v01.book Seite 386 Montag, 30. Januar 2006 12:02 12

Java Messaging Service und Message Driven Beans

Abb. 7.36: Ausgabe einer Message Driven Bean vom Typ Queue

Senden an ein Topic


Analog zum Senden einer Nachricht an eine Queue veröffentlichen wir eine Nachricht an
einem Topic wie folgt:
쐌 Erzeugen eines Eclipse-Projekts1, in dessen Classpath die für die Kommunikation mit
dem JBoss Server notwendigen Bibliotheken (vgl. Abschnitt 7.8) enthalten sind.
쐌 Erzeugen eines InitialContext-Objekts für den JBoss Server
쐌 Erzeugen einer TopicConnectionFactory
쐌 Erzeugen eines Topic-Objekts
쐌 Erzeugen eines TopicConnection-Objekts für die Verbindung zum Topic
쐌 Erzeugen eines TopicSession-Objekts, um das Veröffentlichen von Nachrichten in
einer Sitzung zusammenfassen zu können
쐌 Erzeugen eines TopicPublisher-Objekts, über das eine Nachricht veröffentlicht wer-
den kann
쐌 Erzeugen einer Nachricht bzw. eines Message-Objekts, in dem beispielsweise Texte
gespeichert werden können
쐌 Veröffentlichen der Nachricht
Der Programmcode für das Veröffentlichen ist im Konstruktor einer Klasse mit dem Namen
Publisher zusammengefasst. Im Gegensatz zum Beispiel mit der Queue sei hier unter-
stellt, dass der Zugriff lokal erfolgt, so dass keine Informationen bei der Erzeugung des
InitialContext-Objekts benötigt werden:

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel07/Kap71202MDBSender

386
eclipse_v01.book Seite 387 Montag, 30. Januar 2006 12:02 12

7 – Eclipse und J2EE

package com.entwickler.eclipsebuch.topic;

import javax.jms.JMSException;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicPublisher;
import javax.jms.TopicSession;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class Publisher {


private TextMessage message;
private TopicPublisher topicPublisher;
private TopicSession topicSession;
private TopicConnection topicConnection;
private TopicConnectionFactory tcf;
private Topic topic;
private InitialContext context;
public Publisher(){
try {
InitialContext context = new InitialContext();
tcf = (TopicConnectionFactory) context.lookup
("ConnectionFactory");
topic = (Topic) context.lookup("topic/MdbTopic");
topicConnection = tcf.createTopicConnection();
topicSession = topicConnection.createTopicSession(
false,Session.AUTO_ACKNOWLEDGE);
topicPublisher = topicSession.createPublisher(topic);
message = topicSession.createTextMessage();
message.setText("Das ist eine Testmessage");
topicPublisher.publish(message);
topicPublisher.close();
topicSession.close();
topicConnection.close();
} catch (JMSException e1) {
e1.printStackTrace();
} catch (NamingException e) {
e.printStackTrace();
}
}
}

387
eclipse_v01.book Seite 388 Montag, 30. Januar 2006 12:02 12

Java Messaging Service und Message Driven Beans

Die Ausgabe in der Konsole des JBoss Servers ist in Abb. 7.37 dargestellt. Der Unterschied
in der Ausgabe besteht vor allem darin, dass als jmsDestination hier der Wert
TOPIC.MdbTopic angegeben ist.

Abb. 7.37: Ausgabe einer Message Driven Bean vom Typ Topic

388
eclipse_v01.book Seite 389 Montag, 30. Januar 2006 12:02 12

8 Web Services

von Dr. Kai Brüssau

8.1 Grundlagen
Nachdem wir im vorherigen Kapitel untersucht haben, wie EJB-Komponenten auf einem
Applikationsserver installiert und von einem Client angesprochen werden, widmen wir uns
in diesem Kapitel Komponenten, die im Prinzip an beliebiger Stelle bereitgestellt und über
das Internet ausgeführt werden.
Die Web Services sind also Komponenten, deren Funktionalitäten und Daten von Clients
oder anderen Anwendungen über Protokolle der Internettechnologie angesprochen werden.
Sie werden daher auch als webbasierte Komponenten bezeichnet. Web Services können in
einer beliebigen Programmiersprache entwickelt werden und die Kommunikation erfolgt
über wohldefinierte Schnittstellen.
Die verwendeten Protokolle sind XML (Extensible Markup Language, siehe Kapitel 8.1)
und SOAP (Simple Object Access Protocol) für das Übermitteln von Daten und Funktions-
aufrufen sowie WSDL (Web Service Description Language) für das Beschreiben eines Web
Service.
Damit ein Web Service gesucht und gefunden werden kann, existiert UDDI (Universal De-
scription, Discovery and Integration). Dort registriert sich ein Web Service und der Client
sucht in einer UDDI-Registrierungsdatenbank nach bestimmten Funktionalitäten oder
Namen. Wird ein entsprechender Web Service gefunden, erhält der Client die Informatio-
nen, die er benötigt, um die Funktionalität aufzurufen. Das Vorgehen ist in Abb. 8.1 skiz-
ziert.

Abb. 8.1: Zugriff auf Web Services

UDDI ist ein Projekt, das von verschiedenen namhaften Unternehmen gegründet worden
ist, um Web Services von Clients aus lokalisieren zu können. Die Idee ist, dass bestimmte
Web Services von anonymen Clients aus ohne aufwändige Registrierung bei einem be-
stimmten Unternehmen verwendet werden.

389
eclipse_v01.book Seite 390 Montag, 30. Januar 2006 12:02 12

Begriffe

In eine UDDI-Registrierungsdatenbank werden die Beschreibung und die URL eines Web
Service hineingeschrieben. In dieser Datenbank wird beispielsweise nach Web Services mit
bestimmten Funktionalitäten gesucht. Die technische Beschreibung in Form eines WSDL-
Dokuments, das es einem Client ermöglicht, auf den Web Service zuzugreifen, ist in der
Regel ebenfalls vorhanden.
Eine UDDI-Registrierungsdatenbank ist auf der Seite www.uddi.org für alle öffentlich zu-
gänglich. Es können aber auch eigene, private Registrierungsdatenbanken entwickelt wer-
den, für die beispielsweise nur die Mitarbeiter eines Unternehmens eine Zugangsberechti-
gung haben.

8.2 Begriffe

8.2.1 SOAP
Simple Object Access Protocol (SOAP) ist ein Protokoll, das die Kommunikation zwischen
Objekten auf Basis der XML-Technologie ermöglicht. In einer verteilten, dezentralisierten
und webbasierten Umgebung können Objekte, die auf unterschiedlichen Plattformen lau-
fen und in unterschiedlichen Programmiersprachen geschrieben sind, über SOAP miteinan-
der kommunizieren.
Ein SOAP Client ist ein Objekt, das für den Aufruf einer Methode eines anderen Objekts
eine so genante SOAP Message als Request sendet. Die SOAP Message ist ein XML-Do-
kument, das nach dem SOAP-Standard aufgebaut ist. Der SOAP-Standard umfasst ver-
schiedene Styles, die die Struktur der SOAP Message bestimmen.
Die SOAP Message beinhaltet die Informationen, die zum Ausführen einer Methode eines
Remote-Objekts, z.B. eines Web Service, benötigt werden. Anschließend wird das Ergeb-
nis, also der Rückgabewert der Methode, in einer weiteren SOAP-Message als Response an
den Client zurückgesendet.
Der Datenaustausch mithilfe der SOAP Messages ist in Abb. 8.2 dargestellt.

Abb. 8.2: Austausch von SOAP Messages

390
eclipse_v01.book Seite 391 Montag, 30. Januar 2006 12:02 12

8 – Web Services

8.2.2 WSDL
Ein WSDL-Dokument ist ein XML-Dokument, das ein Web Service beschreibt. Es bein-
haltet den Namen des Web Service, die aufrufbaren Methoden mit den Parameterlisten und
Rückgabewerten und die URL, wo der Web Service liegt.
Über ein WSDL-Dokument wird sozusagen die Schnittstelle eines Web Service nach außen
gegeben. Aus einem WSDL-Dokument lässt sich demnach der Zugriff vollständig generie-
ren und der Client benötigt keine weiteren Informationen. Der Aufbau eines WSDL-Doku-
ments wird in Kapitel 8.3.1 detailliert beschrieben. In Kapitel 8.3.2 und 8.3.3 wird anschlie-
ßend gezeigt, wie aus einem WSDL-Dokument ein Web Service und ein Java-Client für den
Zugriff erzeugt werden können.
Es ist unerheblich, ob die Client-Anwendung und der Web Service beide in Java oder in un-
terschiedlichen Sprachen programmiert sind. Wir beziehen uns aber in diesem Kapitel aus-
schließlich auf die Programmiersprache Java.

8.2.3 JAX
Die Java API for XML (JAX) gliedert sich in mehrere Bereiche, die nur kurz vorgestellt
werden. Eine tiefer gehende Untersuchung der Technologien ist für die Entwicklung von
Web Services in diesem Zusammenhang nicht notwendig, da wir für die technische Seite
der Verarbeitung und Kommunikation Tools verwenden, deren Funktionsweise wir nicht
betrachten. Zu JAX gehören:
쐌 Java API’ for XML Processing (JAXP):
Mit JAXP werden XML-Dokumente verarbeitet, wobei verschiedene Parser, insbe-
sondere SAX- und DOM-Parser, verwendet werden. Bei SAX (Simple API for XML)
und DOM (Document Object Model) handelt es sich um zwei Konzepte für das Parsen
von XML-Dokumenten. An dieser Stelle wollen wir hierauf nicht näher eingehen und
auf Dokumentationen im Internet verweisen, wie z.B. http://www.saxproject.org/ und
http://www.w3c.org/DOM/.
쐌 Java Architecture for XML Binding (JAXB)
JAXB stellt eine effiziente Möglichkeit bereit, um Daten aus XML-Dokumenten in Java-
Objekte zu speichern. Für die Verarbeitung werden XML Schema und Java-Klassen mit-
einander verknüpft. Das XML Schema definiert die Struktur der XML-Dokumenten,
deren Daten in den Objekten der entsprechenden Java-Klassen gespeichert werden.
쐌 SOAP with Attachements API for Java (SAAJ)
SAAJ stellt eine gängige Möglichkeit bereit, XML-Dokumente über das Internet zu
versenden. Ein Client, der einen Web Service aufruft, sendet seinen Request als XML-
Dokument und erhält die Response ebenfalls als XML-Dokument. Dabei wird in Java-
Anwendungen in der Regel SAAJ eingesetzt.
쐌 Java API for XML-based Remote Procedure Calls (JAX-RPC)
JAX-RPC ist die Basis für die Entwicklung und Verwendung von Web Services in Java.
Ein solcher Web Service ist eine Ansammlung von Prozeduren die von einem Client
über ein Netzwerk aufgerufen werden. Der Web Service liegt auf einem Server, wie z.B.
dem Tomcat Server oder einem anderen J2EE Web Server.

391
eclipse_v01.book Seite 392 Montag, 30. Januar 2006 12:02 12

Entwicklung eines Web Service

쐌 Java API for XML Registries (JAXR)


JAXR wird für den Zugriff auf so genannte Register (Business Registries) verwendet, in
denen Unternehmen ihre Dienste und Produkte anbieten. Sie werden auch als elektro-
nische Gelbe Seiten bezeichnet. JAXR stellt ein Standardvorgehen bereit, um aus Java-
Programmen auf solche Register, wie z.B. eine UDDI-Registrierungsdatenbank, zuzu-
greifen.

8.3 Entwicklung eines Web Service


Ein Web Service basiert auf einer Klasse, die Methoden implementiert hat, die von außen
durch einen Client aufgerufen werden können. Für diese Methoden wird ein Interface er-
stellt. Des Weiteren wird das WSDL-Dokument mit der Beschreibung des Web Service be-
nötigt.
Es gibt bei der Entwicklung von Web Service zwei Vorgehensweisen.
1. Das WSDL-Dokument wird vorgegeben und ausgehend von dieser Beschreibung wer-
den alle Klassen und Interfaces generiert. Anschließend müssen die Funktionalitäten in
die dafür vorgesehenen Methoden implementiert werden.
2. Eine Java-Klasse wird vorgegeben und aus dieser wird ein WSDL-Dokument generiert.
Es werden beispielsweise alle öffentlichen Methoden in das WSDL-Dokument aufge-
nommen und sie können von einem Client aufgerufen werden.
Wir werden die beiden einzelnen Varianten detailliert betrachten. Zunächst wollen wir ein
WSDL-Dokument entwickeln. Dieses ist nicht nur für die Generierung des Web Service,
sondern auch für den Zugriff eines Client erforderlich. In dem WSDL-Dokument werden
alle notwendigen Informationen gespeichert.

8.3.1 Aufbau eines WSDL-Dokuments


Ein WSDL-Dokument ist ein XML-Dokument, in dem der Web Service in entsprechenden
Elementen beschrieben wird. In ihm müssen unter anderem folgende Angaben gemacht
werden:
쐌 Methodennamen der von außen aufrufbaren Methoden
쐌 Parameter bzw. Rückgabewerte der Methoden
쐌 Adresse des Servers, der den Web Service ausführt
Die einzelnen Elemente werden wir im Folgenden detailliert betrachten. Anhand eines
Beispiels für ein WSDL-Dokument1 wird jeweils gezeigt, wie die Umsetzung erfolgt. Der
Web Service, den wir entwickeln, dient zur Prüfung von Kreditkartennummern. Um die
Gültigkeit einer Karte zu überprüfen, müssen dazu die Kreditkartennummer und der Name
des Karteninhabers übertragen werden. Als Antwort wird zurückgegeben, ob die Karte gül-
tig ist oder nicht. Diese Informationen für den Request und die Response werden jeweils in
SOAP Messages übertragen.

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel8/Kap831kkpWS

392
eclipse_v01.book Seite 393 Montag, 30. Januar 2006 12:02 12

8 – Web Services

Element definitions
Das Root-Element des WSDL-Dokuments ist definitions. Mithilfe dieses Elements
werden verschiedene Namespaces festgelegt. Die Namespaces sind wichtig, um die Ele-
mente im WSDL-Dokument zu unterscheiden und um externe Spezifikationen, wie z.B. die
WSDL-, die SOAP- oder die XML-Schema-Spezifikation, zu referenzieren. Außerdem
wird der so genannte targetNamespace definiert. Dieser ermöglicht es dem WSDL-Do-
kument, sich selbst zu referenzieren. Es ist nicht notwendig, dass das WSDL-Dokument an
der Stelle schon existiert. Es muss lediglich eine eindeutige URL angegeben werden.
Das definitions-Element für einen Web Service, den wir mit KKPService bezeichnen,
sieht dann beispielsweise wie folgt aus:

<wsdl:definitions name="KKPService"
targetNamespace="http://kkpws.eclipsebuch.entwickler.com"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:tns="http://kkpws.eclipsebuch.entwickler.com"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">

Im Element definitions werden der Name des Web Service und die Namespaces
(Namensräume) festgelegt. Im Einzelnen verwenden wir die folgenden Attribute für das
definitions-Tag:
쐌 name: Das Attribut ist optional und legt den Namen des Web Service fest.
쐌 targetNamespace: Dieses Attribut legt den logischen Namensraum für das eigene
WSDL-Dokument fest.
쐌 xmlns:wsdl Dem Namensraum xmlns:wsdl wird das Schema der WSDL-Doku-
mente zugeordnet. Die Angabe der Elemente erfolgt dann beispielsweise in der Form
wsdl:definitions. Häufig wird das Schema auch dem Default Namespace zuge-
wiesen:
xmlns ="http://schemas.xmlsoap.org/wsdl/"
In diesem Fall ist dann keine Angabe eines Namespace für die Elemente notwendig.
쐌 xmlns:tns: Mit dem Präfix tns (this namespace) wird nach der Konvention auf das
verwendete WSDL-Dokument referenziert. Der Inhalt entspricht dem des Attributs
targetNamespace.
쐌 xmlns:xsd und xmlns:soap: Die Präfixes xsd und soap werden nach der Konven-
tion für die Namespaces der XML-Schema-Spezifikation und der WSDL-SOAP-Ver-
bindung verwendet.

Element types
Das Element types beinhaltet komplexe Datentypdefinitionen, die vom Web Service ver-
wendet werden. Diese werden als XML Schemas festgelegt oder importiert.

<wsdl:types>
<!-- XML Schema -->
</wsdl:types>

393
eclipse_v01.book Seite 394 Montag, 30. Januar 2006 12:02 12

Entwicklung eines Web Service

Wenn in einem Web Service ausschließlich einfache Datentypen benutzt werden, kann die-
ser Teil weggelassen werden.

Element message
Das Element message beinhaltet die Beschreibung der Nachrichten, die übertragen wer-
den. Eine Nachricht/Message kann dabei aus mehreren Teilen bestehen, die alle einen ei-
genen Datentyp besitzen. In diesem Element wird lediglich festgelegt, welche Informatio-
nen in der jeweiligen Nachricht versendet werden. Dabei wird nicht festgelegt, ob die Daten
als Parameter oder als Rückgabewerte zu interpretieren sind.
In unserem Beispiel soll der Web Service die Kreditkartennummer und den Namen des
Kreditkarteninhabers empfangen und einen boolean-Wert zurückgeben, der angibt, ob die
Karte gültig ist oder nicht. Es werden also zwei Nachrichten versendet. Als Erstes werden
die Kreditkartennummer und der Kundenname jeweils als String-Ausdruck übertragen. Die
zweite Nachricht enthält den Wert true oder false. Die Nachrichten werden mithilfe des
message-Elements wie folgt definiert:

<wsdl:message name="msg_check">
<wsdl:part name="kknr" type="xsd:string"/>
<wsdl:part name="kname" type="xsd:string"/>
</wsdl:message>

<wsdl:message name="msg_checkResponse">
<wsdl:part name="result" type="xsd:boolean"/>
</wsdl:message>

Die beiden Messages erhalten die Namen msg_check und msg_checkResponse. Auf
diese beiden Namen werden wir im Folgenden bei der Definition des Zugriffs auf die Me-
thoden des Web Service zurückgreifen.

Element portType
Im Element portType werden die Methoden des Web Service abstrakt definiert. Innerhalb
des operation-Elements wird für jede Methode festgelegt, welche Nachrichten zwischen
Client und Server ausgetauscht werden. Das portType-Element kann beliebig viele Me-
thoden bzw. operation-Elemente beinhalten. Eine Methode besitzt eine Input Message
und eine Output Message. Die Input Message wird vom Client zum Server gesendet, wor-
aufhin dort eine bestimmte Funktionalität ausgeführt und die Output Message zurück-
geschickt wird. Außerdem kann eine so genannte Fault Message definiert werden, die bei
Auftreten eines Fehlers übermittelt wird.
Wir geben dem portType-Element den Namen CheckerIF. Das Suffix IF soll andeuten,
dass wir hier das Interface des Web Service festlegen. Wir definieren eine Methode mit dem
Namen check. Diese erhält als Input Message die oben beschriebene Message msg_check
und versendet als Output Message die Message msg_checkResponse. Vor den Messages
geben wir noch den eigenen Namespace tns an, in dem sie vorhanden sind. Damit emp-

394
eclipse_v01.book Seite 395 Montag, 30. Januar 2006 12:02 12

8 – Web Services

fängt die Methode die beiden String-Werte für die Kreditkartennummer und den Kunden-
namen und gibt einen Boolean-Wert zurück:

<wsdl:portType name="CheckerIF">
<wsdl:operation name="check">
<wsdl:input message="tns:msg_check" name="msg_input"/>
<wsdl:output message="tns:msg_checkResponse"
name="msg_output"/>
</wsdl:operation>
</wsdl:portType>

Element binding
Das binding-Element bezieht sich auf ein portType-Element und beschreibt das Kom-
munikationsprotokoll, das für das portType-Element aus dem WSDL-Dokument verwen-
det wird. Das binding-Element legt das Protokoll für jede Nachricht in jeder Methode des
Port Type fest. Daneben können weitere Elemente vorkommen, die zusätzliche spezielle In-
formationen enthalten.
Das binding-Element besitzt das Attribut name und das Attribut type. Wir definieren das
Element wie folgt:

<wsdl:binding name="CheckerIFBinding" type="tns:CheckerIF">

Der Name CheckerIFBinding wird später als Grundlage für die vom Web Service benö-
tigten Klassen dienen.
Das Attribut type erhält als Wert den Namen des Port Type CheckerIF, der im selben
Namespace tns definiert ist. Damit wird bestimmt, welche Methoden bzw. welchen Port
Type das binding-Element beinhaltet.
Innerhalb des binding-Elements legen wir im soap:binding-Element das Transport-
protokoll mit http://schemas.xmlsoap.org/soap/http und den Style mit docu-
ment fest.

<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http"/>

Der Style gibt an, wie die SOAP Messages kodiert werden. Wenn wir beispielsweise aus
Java heraus eine Methode eines Web Service aufrufen wollen, müssen die Methode, die
Parameter und nach dem Aufruf der Rückgabewert in eine SOAP Message übertragen wer-
den. Bei Einstellung des Style document erfolgt diese Kodierung nach SOAP Document.
Andere Varianten der Kodierung sind RPC, Wrapped und Message, bei denen die SOAP
Messages unterschiedlich kodiert bzw. verarbeitet werden.
Weiterhin legen wir für alle Methoden des Port Type CheckerIF – in diesem Fall für die
Methode check – sowohl für die Input als auch für die Output Message das Protokoll fest.

395
eclipse_v01.book Seite 396 Montag, 30. Januar 2006 12:02 12

Entwicklung eines Web Service

Wir benötigen dafür verschiedene soap-Elemente, die Informationen über die Art der
Kommunikation und den Aufbau der Nachrichten zur Verfügung stellen.
쐌 soap:operation: Dieses Element gibt an, dass für den Aufruf (über einen HTTP Re-
quest) der Methode check im HTTP Header das Element soapAction mit dem Inhalt
check vorkommen muss.
쐌 soap:body: Dieses Element legt den Inhalt der Input und Output Message fest. Dem
zugehörigen Attribut namespace wird der Inhalt des Targetnamespace (http://kkp-
ws.eclipsebuch.entwickler.com) zugeordnet.
Insgesamt sieht der Bereich des WSDL-Dokuments wie folgt aus:

<wsdl:binding name="CheckerIFBinding" type="tns:CheckerIF">


<soap:binding transport="http://schemas.xmlsoap.org/soap/http"
style="document"/>
<wsdl:operation name="check">
<soap:operation soapAction="check"/>
<wsdl:input>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="http://kkpws.eclipsebuch.entwickler.com"
use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="http://kkpws.eclipsebuch.entwickler.com"
use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>

Element service
Innerhalb des serviee-Elements werden port-Elemente zusammengefasst. Für das ser-
vice-Element geben wir lediglich im Attribut name den Namen des Web Service an. Ein
Web Service bzw. ein WSDL-Dokument kann mehrere binding-Elemente beinhalten, die
jeweils eine Funktionalität über einen Port Type bzw. ein Interface anbieten. Unter welchen
Adressen diese Funktionalitäten liegen, wird jeweils in einem port-Element angegeben.
Das port-Element beinhaltet die so genannten Kommunikationsendpunkte (endpoints).
Sie werden als URL angegeben. Wie schon in anderen Beispielen lassen wir auch hier den
Web Service und den Client auf demselben Rechner ablaufen, so dass wir als Server-Adres-
se localhost benutzen. An dieser Stelle steht normalerweise eine IP-Adresse oder ein Do-
main Name. Den Web Service werden wir im Tomcat Web Server installieren, so dass wir
den Port 8080 verwenden. Weiterhin benutzen wir das Tool AXIS zur Installation des Web
Service (siehe Kapitel 8.3.2). Demnach lautet die URL: http://localhost:8080/KKPWS.

396
eclipse_v01.book Seite 397 Montag, 30. Januar 2006 12:02 12

8 – Web Services

<wsdl:service name="KKPService">
<wsdl:port binding="tns:CheckerIFBinding" name="CheckerIFPort">
<soap:address location="http://localhost:8080/KKPWS"/>
</wsdl:port>
</wsdl:service>

Damit ist das Schreiben des WSDL-Dokuments1 abgeschlossen und als nächsten Schritt er-
zeugen wir die Klassen und Interfaces für den Web Service und für einen Client, der darauf
zugreift.

8.3.2 Erzeugen eines Web Service aus einem WSDL-Dokument


In dem WSDL-Dokument stehen die gesamten Informationen, die für den Aufbau der
Struktur des Web Service notwendig sind. In dem Web Service soll eine Methode vom Typ
boolean mit dem Namen check existieren. Diese bekommt zwei Parameter vom Typ
String übergeben. Diese Methode und der Web Service insgesamt sollen in unserem Bei-
spiel in Java implementiert werden.

Axis
Als Ablaufumgebung für Web Services wählen wir den Tomcat Web Server, für den wir
zusätzlich die Erweiterung Axis2 benötigen, die von der Apache Group kostenlos zur Ver-
fügung gestellt wird. Axis umfasst Bibliotheken, die vom Tomcat Web Server benötigt wer-
den, um Web Services ausführen zu können.
Axis bietet außerdem die Möglichkeit, aus einem WSDL-Dokument die benötigten Klas-
sen und Interfaces zu generieren. Dazu erzeugen wir in Eclipse ein Projekt mit dem Name
KKP_WS3 und binden die folgenden Bibliotheken aus dem lib-Verzeichnis von AXIS in
den Classpath ein:
쐌 ../lib.wsdl4j.jar
쐌 ../lib.axis.jar
쐌 ../lib.axis-ant.jar
쐌 ../lib.commons-discovery.jar
쐌 ../lib. commons-loggingj.jar
쐌 ../lib.jaxrpc.jar
쐌 ../lib.log4j-1.2.8.jar
쐌 ../lib.saaj.jar
In dem Root-Verzeichnis des Projekts erzeugen wir das WSDL-Dokument aus Kapitel
8.3.1. In der jar-Datei axis.jar gibt es das Package org.apache.axis.wsdl und darin die
Klasse WSDL2Java. Diese Klasse beinhaltet eine main-Methode und erzeugt aus dem
WSDL-Dokument die Java-Klassen und Interfaces.

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel08/Kap831kkpWS


2. Der Download des Tomcat Web Servers und von Apache Axis ist unter http://ws.apache.org/ möglich.
3. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel08/Kap832kkpWS

397
eclipse_v01.book Seite 398 Montag, 30. Januar 2006 12:02 12

Entwicklung eines Web Service

Wir wählen also die Klasse WSDL2Java und wählen im Menü von Eclipse den Menüpunkt
RUN | RUN… Im RUN-Dialogfenster (siehe Abb. 8.3) geben wir als MAIN CLASS die Klasse
org.apache.axis.wsdl.WSDL2Java mit folgenden Argumenten ( Registerkarte AR-
GUMENTS) an:

--server-side KKPService.wsdl

Das erste Argument (--server-side) bewirkt die Generierung der Klassen und Inter-
faces des Web Service, die vom Server benötigt werden.
Das zweite Argument gibt den Namen des WSDL-Dokuments (KKPService.wsdl) an, das
in unserem Beispiel direkt im Projekt-Verzeichnis liegt.

Abb. 8.3: Argumente für die main-Methode der Klasse WSDL2Java

Web Tools Platform


Axis ist ebenfalls in der Web Tools Platform1 von Eclipse integriert.
Um die Funktionalitäten anwenden zu können, erzeugen wir in Eclipse ein Dynamic Web
Project (siehe Kapitel 7.3.5) mit dem Namen KKPWS. Als Ziel-Server (target runtime) ge-
ben wir den bereits konfigurierten Tomcat Web Server 5.5 an.
Im Projektverzeichnis definieren wir anschließend das WSDL-Dokument (FILE | NEW |
OTHER  WEB SERVICE  WSDL).
Es erscheint ein Grundgerüst eines WSDL-Dokuments, welches in der Graph-Ansicht
visualisiert wird. In dieser Ansicht erkennen wir die Abhängigkeiten des Web Service zwi-
schen Ports, Bindings, Operations und Messages (siehe Abb. 8.4).

1. Siehe Kapitel 7.3; Download unter www.eclipse.org

398
eclipse_v01.book Seite 399 Montag, 30. Januar 2006 12:02 12

8 – Web Services

Abb. 8.4: Übersicht über ein WSDL-Dokument in der Graph View der WTP

In der zugehörigen Properties View werden die Einstellungen für die einzelnen Elemente
gemacht. Es besteht aber auch die Möglichkeit, in der Source-Ansicht das WSDL-Doku-
ment textbasiert zu bearbeiten. Das Dokument wird ebenfalls auf Fehler und Inkonsisten-
zen geprüft. Nach der Fertigstellung des WSDL-Dokuments können Web Service oder
auch der Client über das Kontextmenü der Datei (WEB SERVICE) automatisch erzeugt wer-
den.

Eclipse-Projekt für den Web Service


In dem zugrunde liegenden Projekt existieren daraufhin unter anderem folgende neue Da-
teien:
쐌 com/entwickler/eclipsebuch/kkpws/CheckerIF.class (Interface)
쐌 com/entwickler/eclipsebuch/kkpws/CheckerIFBindingImpl.class (Klasse)
쐌 com/entwickler/eclipsebuch/kkpws/deploy.wsdd (Deployment-Deskriptor)
쐌 com/entwickler/eclipsebuch/kkpws/undeploy.wsdd
Der Web Service besteht aus CheckerIF und CheckerIFBindingImpl. In der Klasse
CheckerIFBindingImpl kann die Methode check implementiert werden und das Inter-
face CheckerIF enthält die entsprechende Deklaration der Methode. Selbstverständlich
könnten hier mehrere Methoden definiert sein, wenn wir das im WSDL-Dokument ange-
geben hätten.
Das Interface CheckerIF sieht folgendermaßen aus:

package com.entwickler.eclipsebuch.kkpws;

public interface CheckerIF extends java.rmi.Remote {


public boolean check(java.lang.String kknr,
java.lang.String kname)
throws java.rmi.RemoteException;
}

399
eclipse_v01.book Seite 400 Montag, 30. Januar 2006 12:02 12

Entwicklung eines Web Service

Die Klasse CheckerIFBindingImpl implementiert das Interface CheckerIF. In die Me-


thode check programmieren wir die Funktionalität zur Überprüfung der Gültigkeit der
Kreditkarte. Dazu fügen wir zunächst einen Konstruktor in die Klasse ein. Im Konstruktor
wird die Datenbankverbindung zur MySQL-Datenbank kkp mit dem JDBC-Treiber My-
SQL Connector/J1 aufgebaut und ein Objekt vom Typ PreparedStatement definiert. Die
Datenbank kkp enthält im Wesentlichen die Tabelle Kunden mit den Feldern kkNummer,
kName vom Typ VarChar und aktiv vom Typ Bit.
Die Klasse ist demnach wie folgt aufgebaut:

package com.entwickler.eclipsebuch.kkpws;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class CheckerIFBindingImpl implements CheckerIF{


Connection conn;
PreparedStatement ps;
boolean dbOK;

public CheckerIFBindingImpl(){
try {
String sql;
Class.forName("com.mysql.jdbc.Driver").newInstance();
conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/kkp");
sql ="SELECT kknummer, kname, aktiv FROM kunden ";
sql += "WHERE kknummer = ? AND kname = ?";
ps = conn.prepareStatement(sql);
dbOK = true;
} catch (Exception e) {
dbOK = false;
}
}

In der Methode check überprüfen wir, ob ein Datensatz zu der als Parameter übergebenen
Kreditkartennummer und zu dem Kundennamen passt. Wenn das der Fall ist, wird zusätz-
lich überprüft, ob das Feld aktiv auf true / 1 steht. Je nach Erfolg der Überprüfung wird
true oder false zurückgegeben:

1. Sowohl das Datenbanksystem MySQL als auch der JDBC-Treiber MySQL Connector/J stehen unter
http://dev.mysql.com/downloads/ kostenlos zur Verfügung.

400
eclipse_v01.book Seite 401 Montag, 30. Januar 2006 12:02 12

8 – Web Services


public boolean check(String kknr, String kname)
throws java.rmi.RemoteException {
try {
if (dbOK){
ResultSet rs;
ps.setString(1,kknr);
ps.setString(2,kname);
rs = ps.executeQuery();
if (rs.next() && rs.getBoolean("aktiv")){
return true;
}
}
return false;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}

Deployment des Web Service auf den Tomcat Web Server


Um einen Web Service auf einem Server zu installieren, benötigen wir einen Deployment-
Deskriptor. Dieser beinhaltet Informationen zum Web Service, wie z.B. den Namen des
Interface und der Klasse, in der es implementiert ist, sowie die Namen der Methoden, Para-
meter etc. Die Informationen sind in der Datei deploy.wsdd, die ebenfalls vom Programm
WSDL2Java erzeugt worden ist (siehe vorherigen Abschnitt), enthalten:

<deployment
xmlns=http://xml.apache.org/axis/wsdd/
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">

<!-- Services from KKPService WSDL service -->


<service name="CheckerIFPort" provider="java:RPC"
style="document" use="literal">
<parameter name="wsdlTargetNamespace"
value="http://kkpws.eclipsebuch.entwickler.com"/>
<parameter name="wsdlServiceElement" value="KKPService"/>
<parameter name="wsdlServicePort" value="CheckerIFPort"/>
<parameter name="className"
value="com.entwickler.eclipsebuch.kkpws.CheckerIFBindingImpl"/>
<parameter name="wsdlPortType" value="CheckerIF"/>
<parameter name="allowedMethods" value="check"/>

401
eclipse_v01.book Seite 402 Montag, 30. Januar 2006 12:02 12

Entwicklung eines Web Service

<parameter name="wsdlPortType" value="CheckerIF"/>


<parameter name="allowedMethods" value="check"/>

<operation name="check" qname="operNS:check"


xmlns:operNS="http://kkpws.eclipsebuch.entwickler.com"
returnQName="result" returnType="rtns:boolean"
xmlns:rtns="http://www.w3.org/2001/XMLSchema" >
<parameter name="kknr" type="tns:string"
xmlns:tns="http://www.w3.org/2001/XMLSchema"/>
<parameter name="kname" type="tns:string"
xmlns:tns="http://www.w3.org/2001/XMLSchema"/>
</operation>
</service>
</deployment>

Vor dem Deployment kopieren wir aus dem webapps-Verzeichnis von AXIS das Unter-
verzeichnis axis in das webapps-Verzeichnis des Tomcat Servers. Da wir mit dem MySQL-
Datenbanksystem arbeiten, muss zusätzlich der entsprechende JDBC-Treiber MySQL
Connector/J 3.0.91 (mysql-connector-java-3.0.9-stable-bin.jar) in das Verzeichnis
webapps/axis/lib des Tomcat Servers kopiert werden.
Anschließend starten wir den Tomcat Web Server und rufen in Eclipse aus der jar-Datei
axis.jar die main-Methode der Klasse org.apache.axis.client.AdminClient mit
folgenden Argumenten auf:

-p 8080 com/entwickler/eclipsebuch/kkpws/deploy.wsdd

Die Angabe von -p 8080 bewirkt, dass das Deployment an den Port 8080, also an den des
Tomcat Servers, erfolgt. Das zweite Argument beschreibt den Ort des Deployment-De-
skriptors innerhalb des Projekts. In der CONSOLE VIEW von Eclipse erscheint dann folgender
Text:

Processing file com/entwickler/eclipsebuch/kkpws/deploy.wsdd


<Admin>Done processing</Admin>

Das Programm AdminClient hat im Verzeichnis webapps/axis/WEB-INF des Tomcat Ser-


vers die Datei server-config.wsdd generiert oder verändert, falls diese schon vorhanden ge-
wesen ist. In der Datei server-config.wsdd stehen unter anderem die Informationen aus der
Datei deploy.wsdd von dem Web Service, dessen Deployment wir vorgenommen haben.
Abschließend müssen wir noch aus dem Verzeichnis com/entwickler/eclipsebuch/kkpws/
die beiden Dateien CheckerIF.class und CheckerIFBindingImpl.class in das Verzeichnis

1. Falls eine andere Version von MySQL Connector/J oder ein anderer JDBC-Treiber verwendet werden soll,
muss die entsprechende jar-Datei in das Verzeichnis kopiert werden.

402
eclipse_v01.book Seite 403 Montag, 30. Januar 2006 12:02 12

8 – Web Services

webapps/axis/WEB-INF/classes des Tomcat Servers kopieren. Die gesamte Dateistruktur


des axis-Verzeichnisses des Tomcat Servers ist in Abb. 8.5 dargestellt.

Abb. 8.5: Dateistruktur eines Web Service auf dem Tomcat Server

Ob AXIS richtig konfiguriert und der Web Service ansprechbar ist, können wir zunächst
durch Aufruf von http://localhost:8080/axis/ testen. Im Browser erscheint das Welcome-
Dokument von AXIS. Hier klicken wir auf den Link VIEW und sehen die installierten Web
Services. Die Ausgabe ist in Abb. 8.6 dargestellt.

Abb. 8.6: Ausgabe der installierten Web Services

Anhand der Abb. 8.6 ist zu erkennen, dass der von uns erzeugte Web Service vorhanden und
ansprechbar ist. Wir können uns an dieser Stelle beispielsweise das zugehörige WSDL-
Dokument ansehen.

403
eclipse_v01.book Seite 404 Montag, 30. Januar 2006 12:02 12

Entwicklung eines Web Service

Deployment bei Verwendung der WTP


Beim Einsatz der WTP ist für das Deployment lediglich die Servers View aufzurufen, der
entsprechende Server zu erstellen und dabei der Web Service diesem zuzuordnen.

8.3.3 Entwicklung eines Clients für den Web Service


Die Informationen über einen Web Service, die ein Client benötigt, sind wiederum im
WSDL-Dokument vorhanden. Daher werden wir das gleiche WSDL-Dokument ver-
wenden, das schon bei der Generierung der Klassen für den Server zum Einsatz kam. Wir
erstellen in Eclipse ein neues Projekt mit dem Namen KKPWSClient1, binden die jar-
Dateien von AXIS aus dem Verzeichnis axis/lib ein und kopieren das WSDL-Dokument
KKPService.wsdl in das Projektverzeichnis. Wie bei der Erstellung der server-seitigen
Klassen etc. rufen wir wiederum die main-Methode aus der Klasse org.apache.axis.
wsdl.WSDL2Java auf, die in der Datei axis.jar liegt. Dieses Mal verwenden wir aber le-
diglich das Argument KKPService.wsdl, womit wir das WSDL-Dokument übergeben.
Das Programm WSDL2Java erzeugt ein Package mit dem im WSDL-Dokument festgeleg-
ten Namen com.entwickler.eclipsebuch.kkpws. Darin sind folgende Klassen und
Interfaces enthalten:
쐌 CheckerIF (Interface)
쐌 CheckerIFBindingStub (Klasse)
쐌 KKPService (Interface)
쐌 KKPServiceLocator (Klasse)
Der Client muss den Web Service lokalisieren, bevor er auf die Methoden zugreifen kann.
Zum Lokalisieren ist im WSDL-Dokument der so genannte Kommunikationsendpunkt
(endpoint) definiert worden. Bei dieser URL wird der Web Service gesucht. Das Auffinden
des Web Service übernimmt die Klasse KKPServiceLocator.
Um eine Methode des Web Service aus einem Java-Client heraus aufrufen zu können, be-
nötigen wir eine Stub-Klasse, über die der Zugriff erfolgt. Die entsprechende Klasse heißt
in diesem Beispiel CheckerIFBindingStub. Sie implementiert das Interface Checker-
IF, in dem alle Methoden des Web Service vorkommen. Wir gehen also wie folgt vor:
쐌 Lokalisieren des Web Service
Zum Lokalisieren des Web Service wird in der Klasse KKPServiceLocator der Name
des Kommunikationsendpunkts bzw. die URL des Web Service mit http://local-
host:8080/axis/services/CheckerIFPort festgelegt.
쐌 Erzeugen des Objekts der Stub-Klasse
Beim Erzeugen eines Objekts der Klasse CheckerIFBindingStub wird für jede auf-
rufbare Methode des Web Service ein Objekt der Klasse OperationDesc aus dem
Package org.apache.axis.description erzeugt und in einem Objekt-Array ge-
speichert. In diesen Objekten werden unter anderem jeweils der Name der Methode so-
wie die Datentypen der Übergabeparameter und des Rückgabewerts gespeichert.

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel8/Kap833kkpWSClient

404
eclipse_v01.book Seite 405 Montag, 30. Januar 2006 12:02 12

8 – Web Services

쐌 Aufruf einer Methode des Web Service durch den Client


Die Methode check aus dem Interface CheckerIF wird in der Klasse CheckerIF-
BindingStub so implementiert, dass diese auf die korrespondierende Methode im
Web Service zugreift und dabei die Übergabeparameter weiterleitet und den Rück-
gabewert empfängt. Hierfür wird ein Objekt der Klasse Call aus dem Package
org.apache.axis.client. verwendet. Bei dem Zugriff verarbeitet dieses Objekt
das entsprechende org.apache.axis.description.OperationDesc-Objekt,
welches beim Erzeugen der Stub-Klasse angelegt worden ist.
Wir verwenden in unserem Java-Client die Klassen, die von AXIS zur Verfügung gestellt
bzw. generiert werden. Es gibt selbstverständlich auch andere Möglichkeiten, auf einen
Web Service zuzugreifen, was wir aber an dieser Stelle nicht untersuchen. Um die Funktio-
nalität des Web Service testen zu können, benötigen wir noch eine main-Methode, die wie
folgt aussieht:

public static void main(String[] args) {


try {
KKPService sl = new KKPServiceLocator();
CheckerIFBindingStub kkpws =
(CheckerIFBindingStub) sl.getCheckerIFPort();
if (kkpws.check("1000 2000 30","Karl Adam")){
System.out.println("Karte ist gültig");
}else{
System.out.println("Karte ist nicht gültig");
}
} catch (RemoteException e) {
e.printStackTrace();
} catch (ServiceException e) {
e.printStackTrace();
}
}

Bei Verwendung der WTP gibt es einen Assistenten zur Erzeugung eines Clients zu einem
Web Service. Dieser kann für ein WSDL-Dokument über dessen Kontextmenü aufgerufen
werden.

8.3.4 Erzeugen eines WSDL-Dokuments aus einer Java-Klasse


In Kapitel 8.3.2 haben wir untersucht, wie ein Web Service auf Basis eines WSDL-Doku-
ments entwickelt werden kann. An dieser Stelle wollen wir den umgekehrten Fall betrach-
ten. Wir gehen davon aus, dass wir eine Klasse besitzen, deren Funktionalität wir als Web
Service bereitstellen möchten.

405
eclipse_v01.book Seite 406 Montag, 30. Januar 2006 12:02 12

Entwicklung eines Web Service

Beispielprojekt
Als Beispiel diene ein Buchungssystem, bei dem Personen anfragen können, ob ein be-
stimmter Programmiersprachenkurs noch frei ist, oder aber auch einen Kurs für eine be-
stimmte Person buchen können.
In der Klasse Buchung1, die Ausgangspunkt für den Web Service ist, gibt es zwei Metho-
den: anfragen und buchen, die beide im Interface BuchungIF definiert sind und in den
Web Service aufgenommen werden sollen.
Der Methode anfragen werden zwei Parameter übergeben. Der erste ist ein Objekt der
Klasse Person und der zweite ist die Kursnummer vom Typ String. Die Klasse Person
besitzt die beiden Instanzvariablen nachname und vorname vom Typ String und zugehö-
rige Getter- und Setter-Methoden. Die Methode hat einen Rückgabewert vom Typ boole-
an, der angibt, ob der Kurs für die Person noch frei ist oder nicht.
Der Methode buchen hat dieselben Übergabeparameter wie die Methode anfragen. Der
Rückgabewert ist ein Objekt der Klasse Rechnung. Diese Klasse besitzt die beiden In-
stanzvariablen kosten vom Typ double und kursID vom Typ String und zugehörige
Getter- und Setter-Methoden.
Im Package com.entwickler.eclipsebuch.buchungssystem werden alle verwen-
deten Klassen programmiert. Die Klasse Buchung besitzt folgenden Programmcode:

package com.entwickler.eclipsebuch.buchungssystem;

public class Buchung implements BuchungIF{


public Rechnung buchen(Person person, String kursID) {
return null;
}
public boolean anfragen(Person person, String kursID) {
return false;
}
}

Ausgehend von dieser Klasse wird ein WSDL-Dokument generiert. Dazu erzeugen wir ein
Java-Projekt, in dem wir in den Klassenpfad die jar-Dateien von AXIS aus dem Verzeichnis
../axis-1_1 /lib aufnehmen (siehe Kapitel 8.3.2) und das Interface BuchungIF sowie die
drei Klassen Buchung, Person und Rechnung programmieren. Die Klassen Person und
Rechnung bestehen im Wesentlichen nur aus den beschriebenen Instanzvariablen und den
zugehörigen Getter- und Setter-Methoden.

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel8/Kap834RegistrierungsWSOrig

406
eclipse_v01.book Seite 407 Montag, 30. Januar 2006 12:02 12

8 – Web Services

JAVA2WSDL
In der Classpath-Datei axis.jar gibt es im Package org.apache.axis.wsdl die Klas-
se Java2WSDL. In dieser Klasse existiert eine main-Methode, die wir mit folgenden Argu-
menten starten:

--output BuchungsWS.wsdl
--portTypeName BuchungIF
--bindingName Buchung
--location http://localhost:8080/axis/services/Buchung
com.entwickler.eclipsebuch.buchungssystem.Buchung

Die Parameter haben folgende Bedeutung:


쐌 --output: Angabe des Namens des WSDL-Dokuments, welches generiert wird
쐌 --portTypeName: Name des portType-Elements im WSDL-Dokument (siehe Ab-
schnitt „Element portType“ in Kapitel 8.3.1)
쐌 --bindingName: Name des binding-Elements im WSDL-Dokument (siehe Abschnitt
„Element binding“ in Kapitel 8.3.1)
쐌 --location: URL, unter der der Web Service angeboten werden soll
쐌 Das letzte Argument gibt die Klasse an, auf deren Grundlage das WSDL-Dokument ge-
neriert werden soll.
Nach dem Ausführen von Java2WSDL erscheint in dem Projektverzeichnis die Datei
BuchungsWS.wsdl. Unter Umständen ist noch REFRESH aus dem Kontextmenü des Projekts
auszuwählen, damit das Projekt aktualisiert wird und wir die Datei sehen können.

WSDL-Dokument BuchungsWS.wsdl
Das WSDL-Dokument BuchungsWS.wsdl verfügt über die Elemente, wie sie in Kapitel
8.3.1 beschrieben wurden. Es beginnt mit dem definitions-Element:

<wsdl:definitions
targetNamespace="http://
buchungssystem.eclipsebuch.entwickler.com"
xmlns:impl="http://buchungssystem.eclipsebuch.entwickler.com"
xmlns:intf="http://buchungssystem.eclipsebuch.entwickler.com"
xmlns:apachesoap="http://xml.apache.org/xml-soap"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
mlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns="http://schemas.xmlsoap.org/wsdl/">

In diesem Bereich werden die Namespaces festgelegt. Der Targetnamespace ergibt sich aus
dem Namen des Package der zugrunde liegenden Klasse. An die Stelle des Namespace tns
(this namespace) tritt hier der Namespace impl, wobei die Namen der Namespaces grund-
sätzlich frei wählbar sind. Sie müssen aber innerhalb eines Dokuments konsistent sein.

407
eclipse_v01.book Seite 408 Montag, 30. Januar 2006 12:02 12

Entwicklung eines Web Service

Im Gegensatz zu dem Beispiel der Kreditkartenprüfung aus Kapitel 8.3.2 verwenden wir
hier als Übergabeparameter und Rückgabewerte Objekte von selbst erstellten Klassen. Die
zu übertragenden Daten der Objekte werden im types-Element als complexType–Ele-
mente angegeben:

<wsdl:types>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace=
"http://buchungssystem.eclipsebuch.entwickler.com">
<import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>

<complexType name="Person">
<sequence>
<element name="nachname" nillable="true" type="xsd:string"/>
<element name="vorname" nillable="true" type="xsd:string"/>
</sequence>
</complexType>

<complexType name="Rechnung">
<sequence>
<element name="kosten" type="xsd:double"/>
<element name="kursID" nillable="true" type="xsd:string"/>
</sequence>
</complexType>
</schema>
</wsdl:types>

Es werden die beiden complexType-Elemente Person und Rechnung definiert, die die
Instanzvariablen, zu denen es Getter- und Setter-Methoden gibt, der entsprechenden
Klassen beinhalten.
Im Anschluss werden die Nachrichten in message-Elementen festgelegt. Für die beiden
Methoden anfragen und buchen werden jeweils eine Nachricht für den Request und eine
für die Response definiert, so dass wir insgesamt vier Nachrichten erhalten:

<wsdl:message name="anfragenResponse">
<wsdl:part name="anfragenReturn" type="xsd:boolean"/>
</wsdl:message>

<wsdl:message name="buchenResponse">
<wsdl:part name="buchenReturn" type="impl:Rechnung"/>
</wsdl:message>

<wsdl:message name="buchenRequest">
<wsdl:part name="person" type="impl:Person"/>
<wsdl:part name="kursID" type="xsd:string"/>
</wsdl:message>

408
eclipse_v01.book Seite 409 Montag, 30. Januar 2006 12:02 12

8 – Web Services

<wsdl:message name="anfragenRequest">
<wsdl:part name="person" type="impl:Person"/>
<wsdl:part name="kursID" type="xsd:string"/>
</wsdl:message>

In diesem Bereich wird als Datentyp (type) für einige Teile der Nachrichten impl:Person
und impl:Rechnung angegeben. Da die entsprechenden complexType-Elemente inner-
halb des types-Elements desselben WSDL-Dokuments definiert worden sind, wird der
Namespace impl verwendet.
Im darauf folgenden Teil des WSDL-Dokuments wird der Port Type, die Schnittstelle des
Web Service, beschrieben. Für das portType-Element haben wir bei der Generierung mit
dem Programm Java2WSDL den Namen BuchungIF bestimmt. Hier werden die Methoden
in den beiden operation-Elementen abgebildet:

<wsdl:portType name="BuchungIF">
<wsdl:operation name="buchen" parameterOrder="person kursID">
<wsdl:input name="buchenRequest"
message="impl:buchenRequest"/>
<wsdl:output name="buchenResponse"
message="impl:buchenResponse"/>
</wsdl:operation>

<wsdl:operation name="anfragen" parameterOrder="person kursID">


<wsdl:input name="anfragenRequest"
message="impl:anfragenRequest"/>
<wsdl:output name="anfragenResponse"
message="impl:anfragenResponse"/>
</wsdl:operation>
</wsdl:portType>

Die beiden hier definierten Methoden heißen buchen und anfragen. Diese haben wir in
der Klasse Buchung so bezeichnet. Die Methoden verwenden die Messages, die innerhalb
des message-Elements desselben WSDL-Dokuments definiert worden sind.
Anschließend wird das binding-Element mit dem Namen Buchung festgelegt. Auch die-
sen Namen haben wir beim Start von Java2WSDL vorgegeben. Als Transportprotokoll wird
standardmäßig http und als Style für die SOAP-Messages document verwendet. Der Be-
reich enthält weiterhin die im Abschnitt „Element binding“ aus Kapitel 8.3.1 beschriebenen
Elemente:

<wsdl:binding name="Buchung" type="impl:BuchungIF">

<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http"/>

409
eclipse_v01.book Seite 410 Montag, 30. Januar 2006 12:02 12

Entwicklung eines Web Service

<wsdl:operation name="buchen">
<soap:operation soapAction=""/>
<wsdl:input name="buchenRequest">
<soap:body use="literal"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="http://buchungssystem.eclipsebuch.entwickler.com"/>
</wsdl:input>
<wsdl:output name="buchenResponse">
<soap:body use="literal"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="http://buchungssystem.eclipsebuch.entwickler.com"/>
</wsdl:output>
</wsdl:operation>

<wsdl:operation name="anfragen">
<soap:operation soapAction=""/>
<wsdl:input name="anfragenRequest">
<soap:body use="literal"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="http://buchungssystem.eclipsebuch.entwickler.com"/>
</wsdl:input>
<wsdl:output name="anfragenResponse">
<soap:body use="literal"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="http://buchungssystem.eclipsebuch.entwickler.com"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>

Zum Abschluss wird noch im service-Element für das eben definierte binding-Element
Buchung die URL angegeben, über die diese Funktionalität aufgerufen werden kann:

<wsdl:service name="BuchungIFService">
<wsdl:port name="Buchung" binding="impl:Buchung">
<soap:address
location="http://localhost:8080/axis/services/Buchung"/>
</wsdl:port>
</wsdl:service>

Aus dem auf diese Weise generierten WSDL-Dokument BuchungsWS.wsdl können wir
mithilfe des Programms WSDL2Java (siehe Kapitel 8.3.2) einen Web Service generieren.
Dabei werden neben den Klassen und Interfaces des Web Service auch die Klassen Person
und Rechnung mit den entsprechenden Instanzvariablen sowie Getter- und Setter-Metho-
den erzeugt.1 Die Implementierung der Methoden anfragen und buchen muss allerdings
aus der Originalklasse Buchung übernommen werden.

1. Siehe auf der beiliegenden CD im Verzeichnis ../Kapitel8/Kap834RegistrierungsWS

410
eclipse_v01.book Seite 411 Montag, 30. Januar 2006 12:02 12

9 Plug-In-Entwicklung für die


Eclipse Workbench

von Oliver Widder

9.1 Everything is a plug-in


Das wichtigste Element in der Architektur von Eclipse ist das Plug-In. Eclipse besteht aus-
schließlich aus Plug-Ins, abgesehen von einem kleinen Rest, der für die Steuerung des
Plug-In-Mechanismus zuständig ist. Eclipse ist somit nichts anderes als eine Ablaufumge-
bung für Plug-Ins. Erst die entsprechenden Plug-Ins (z.B. org.eclipse.jdt.*) machen Eclipse
zu einer Java-Entwicklungsumgebung.
Hat man sich Eclipse frisch heruntergeladen, finden sich jede Menge Plug-Ins in dem Ver-
zeichnis plugins. Sie liegen dort zum einen Teil als Verzeichnisse und zum anderen Teil als
gepackte Verzeichnisse in einer Jar-Datei vor. Die Plug-Ins lassen sich in verschiedene
Komponenten einteilen, z.B.:
쐌 Die Komponente Runtime-Core (Plug-Ins org.eclipse.core.runtime.*, org.eclipse.os-
gi.*) enthält grundlegende Mechanismen, wie z.B. den Mechanismus zum Laden und
Verwalten der Plug-Ins.
쐌 Die Komponente Workspace-Core (Plug-In org.eclipse.core.resources) ist für Projek-
te, Dateien und Verzeichnisse verantwortlich.
쐌 Die Komponente SWT (Simple Widget Toolkit, Plug-Ins org.eclipse.swt.*) enthält alle
grundlegenden Oberflächenelemente wie Tabellen und Eingabefelder.
쐌 Die Komponente JFace (Plug-Ins org.eclipse.jface.*) setzt auf SWT auf und bietet
komfortable Oberflächenelemente wie Tree Viewer und Table Viewer an.
쐌 Die Komponente PDE (Plug-In Development Environment, Plug-Ins org.eclipse.pde.*)
bietet Werkzeuge zum Erstellen und Testen von eigenen Plug-Ins.
쐌 Die Komponente Workbench (Plug-Ins org.eclipse.ui.*) setzt auf JFace und SWT auf
und enthält die Implementierung der Eclipse-Oberfläche.
쐌 Die Komponente JDT (Java Development Toolkit, Plug-Ins org.eclipse.jdt.*) macht
Eclipse zu einer Java-Entwicklungsumgebung. Sie enthält alles Java-spezifische wie
Übersetzen, Debuggen, Editieren von Java-Klassen etc.
쐌 Die Komponente CVS enthält die Anbindung an das Konfigurationsmanagement-
Werkzeug CVS (Concurrent Versioning System, Plug-Ins org.eclipse.team.*).
쐌 Die Komponente Ant enthält die Ant-Einbindung in Eclipse (Plug-Ins org.eclipse.
ant.*).

9.2 In die Tiefen der Oberfläche


Dieses Kapitel ist ein Praktikum für die Entwicklung eigener Plug-Ins mit dem Plug-In
Development Environment (PDE) zur Erweiterung der Eclipse-Oberfläche. Wir werden in
das Innere der Eclipse Workbench vordringen. Dabei werden wir viele – jedoch bei weitem

411
eclipse_v01.book Seite 412 Montag, 30. Januar 2006 12:02 12

Eine Outline View für den Texteditor

nicht alle – Techniken zur Implementierung von Erweiterungen kennen lernen1. Nachdem
wir dieses Kapitel durchgearbeitet haben, werden wir so weit mit Eclipse vertraut sein, dass
wir uns die hier nicht behandelten Techniken aneignen können.
Im Verlauf dieses Praktikums werden wir folgende Plug-Ins erstellen und nach und nach
ausbauen:
쐌 Ein Plug-In, das eine Hello World Outline View für den Texteditor implementiert
(com.entwickler.eclipsebuch.texteditoroutline).
쐌 Ein Plug-In, das einen Java Properties Editor implementiert (com.entwickler.eclipse-
buch.javapropertieseditor). Der Java Properties Editor verhält sich wie der Texteditor.
Er bietet jedoch eine Outline View an, die Java Properties in einer Baumstruktur dar-
stellt.
쐌 Ein Plug-In, mit dem die Struktur der aktuell offenen Views und Editors betrachtet wer-
den kann (com.entwickler.eclipsebuch.workbenchview).
쐌 Ein Plug-In, das in einer View alle Marker des gerade aktiven Editors anzeigt (com.ent-
wickler.eclipsebuch.markerview).
쐌 Zum Schluss werden wir noch Plug-Ins erstellen, über die wir OSGi2 Bundles und Ser-
vices betrachten können.

9.3 Eine Outline View für den Texteditor


Nun wollen wir schnell in medias res gehen und eigene Erweiterungen für die Eclipse
Workbench produzieren. Dabei werden wir auch im Code der oben genannten Plug-Ins stö-
bern. Wir können uns jedes Plug-In in den Workspace holen. Dazu markieren wir z.B. das
Plug-In org.eclipse.core.runtime in der Plug-Ins View (WINDOW | SHOW VIEW | OTHER... |
PDE | PLUG-INS) und wählen im Kontextmenü IMPORT | AS BINARY PROJECT (siehe Abb. 9.1).
Danach dürfen wir das Plug-In als ein neues Projekt im Workspace begrüßen. Da es als
Binary Project importiert wurde, steckt der Code in einem Jar File (org.eclipse.core.run-
time_3.1.0.jar). Der Source Code steckt in dem Zip-File org.eclipse.core.runtime_
3.1.0src.zip und ist dem Jar File zugeordnet. So kann man sich alle Klassen per Doppelklick
im Source Code ansehen3.

1. Wir werden jedoch nicht alle Eclipse-Techniken kennen lernen können. Nachdem wir dieses Kapitel durchge-
arbeitet haben, werden wir aber so weit mit Eclipse vertraut sein, dass wir uns die hier nicht behandelten Tech-
niken schnell aneignen können.
Der Source Code aller in diesem Kapitel erstellten Plug-Ins liegt auf der CD. Dem Leser wird empfohlen,
damit zu arbeiten, da der abgedruckte Code bezüglich Import-Anweisungen und verwendeter Icons nicht voll-
ständig ist.
2. Was OSGi Bundles und Servcies sind, wird in Abschnitt 9.10 erklärt.
3. Die meisten Plug-Ins benötigen andere Plug-Ins, aus denen sie Klassen benutzen. Gegebenenfalls will man
diese Plug-Ins gleich mit in den Workspace holen. Man kann alle Plug-Ins, die voneinander abhängen, im Kon-
textmenü eines Plug-Ins in der Plug-Ins View über SELECT | REQUIRED PLUG-INS selektieren, um sie dann alle
über Import in den Workspace zu importieren.

412
eclipse_v01.book Seite 413 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

Abb. 9.1: Importieren eines Plug-In in den Workspace

9.3.1 Adapter: Dynamische Erweiterung von Klassen


Bevor wir uns an die Erstellung einer ersten Erweiterung machen, wollen wir eine wichtige
Technik von Eclipse kennen lernen: die dynamische Erweiterung von Klassen. Realisiert
wird sie durch das Interface IAdaptable. Wir suchen es über NAVIGATE | OPEN TYPE ...
(oder ŸÁT), finden es im Package org.eclipse.core.runtime, öffnen es und
wollen dort kurz verweilen:

/** * An interface for an adaptable object. * <p> * Adaptable


objects can be dynamically extended to provide different *
interfaces (or "adapters"). Adapters are created by adapter *
factories, which are in turn managed by type by adapter managers.
* </p> * For example,
* <pre>
* IAdaptable a = [some adaptable];
* IFoo x = (IFoo)a.getAdapter(IFoo.class);
* if (x != null)
* [do IFoo things with x]
* </pre>
* <p>
* Clients may implement this interface, or obtain a default
implementation
* of this interface by subclassing <code>PlatformObject</code>.
* </p>
* @see IAdapterFactory
* @see IAdapterManager
* @see PlatformObject
*/

413
eclipse_v01.book Seite 414 Montag, 30. Januar 2006 12:02 12

Eine Outline View für den Texteditor

public interface IAdaptable {


/**
* Returns an object which is an instance of the given class
* associated with this object. Returns <code>null</code> if
* no such object can be found.
*
* @param adapter the adapter class to look up
* @return a object castable to the given class,
* or <code>null</code> if this object does not
* have an adapter for the given class
*/
public Object getAdapter(Class adapter);
}

Jedes Projekt, jeder Ordner, jedes File, jede Java-Klasse, jede Methode usw. wird in Eclipse
durch ein Objekt repräsentiert1. Dies gilt ebenso für die Elemente der Eclipse-Oberfläche
wie Views, Editors usw. Diese Objekte kommunizieren – wie in Java nicht unüblich – über
Interfaces. Über Interfaces werden auch die Rollen von Objekten bestimmt, d.h.: Ist ein Ob-
jekt vom Typ eines bestimmten Interface (im Sinne von instanceof), so nimmt es eine
bestimmte Rolle ein. So fragt z.B. die Outline View (org.eclipse.ui.views.con-
tentoutline.ContentOutline) den gerade aktiven Editor, ob er vom Typ org.
eclipse.ui.views.contentoutline.IContentOutlinePage ist. Wenn ja, dann
fordert die View den Editor auf, den Inhalt der Outline View zu füllen.
Dies wollen wir uns etwas genauer ansehen. Dazu holen wir uns das Plug-In org.
eclipse.ui.views, das auch die Outline View enthält, in den Workspace. Mit ŸÁT
suchen wir die Klasse ContentOutline, holen sie in den Class File Editor und navigieren
zu der Methode doCreatePage:

protected PageRec doCreatePage(IWorkbenchPart part) { // Try to


get an outline page. Object obj =
part.getAdapter(IContentOutlinePage.class); if (obj instanceof
IContentOutlinePage) { IContentOutlinePage page =
(IContentOutlinePage) obj;
if (page instanceof IPageBookViewPage)
initPage((IPageBookViewPage) page);
page.createControl(getPageBook());
return new PageRec(part, page);
}
// There is no content outline
return null;
}

1. Dieses Objekt wird jedoch so spät wie möglich instanziiert. So werden z.B. erst dann, wenn man die Files eines
Projekts abfragt, auch die sie repräsentierenden Objekte erzeugt.

414
eclipse_v01.book Seite 415 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

Der Parameter part vom Typ IWorkbenchPart ist ein Workbench Part. Er repräsentiert
einen Editor oder eine View. Die Methode doCreatePage möchte nun von diesem Work-
bench Part wissen, ob er als Quelle für eine Outline View dienen kann. Eine Quelle einer
Outline View muss vom Typ IContentOutlinePage sein. Doch muss der Workbench
Part nicht höchstpersönlich von diesem Typ sein. Vielmehr genügt es, wenn er dazu einen
Adapter zur Verfügung stellt.
Dazu wird der Workbench Part über die Methode getAdapter (aus dem Interface IAdapt-
able) aufgefordert, ein Objekt zurückzuliefern, das dieses Interface implementiert. Wenn
der Workbench Part das nicht kann, dann gibt es eben keine Outline View für ihn, d.h. für
diese View oder für diesen Editor. In der Outline View steht dann „An outline is not avail-
able“. Wenn er aber eine IContentOutlinePage zurückliefern kann, so wird diese auf-
gefordert, die benötigten Oberflächen-Controls zu erzeugen1.
Dieser Adapter-Mechanismus hat mindestens zwei Vorteile:
쐌 Eine Klasse muss die benötigten Interfaces nicht selbst implementieren. Sie kann be-
stimmte Funktionalitäten an andere Klassen (Adapter) delegieren. So kann man den
Funktionsumfang der einzelnen Klassen klein halten.
쐌 Eine Klasse kann um weitere Funktionalitäten erweitert werden, ohne dass sie ein neues
Interface implementieren muss. So bleiben Klassen nach außen stabil.

9.3.2 Ein eigener Adapter


Wir wollen nun dem Texteditor eine Outline View spendieren. Wir verfassen einen ICon-
tentOutlinePage-Adapter für den Texteditor2. Dazu müssen wir ein eigenes Plug-In er-
stellen. Eclipse bietet hierfür Plug-In-Projekte an. Plug-In-Projekte sind spezielle Java-Pro-
jekte, die ein Plug-In implementieren. Jedes Plug-In-Projekt enthält eine Datei mit Namen
plugin.xml, das so genannte Plug-In Manifest. Wir werden es später genauer kennen lernen.
Zum Anlegen eines Plug-In-Projekts3 wählen wir im New Project Wizard (FILE | NEW | PRO-
JECT...) den Punkt PLUG-IN PROJECT. Das Projekt nennen wir com.entwickler.eclipsebuch.text-
editoroutline und entfernen den Haken bei der Checkbox CREATE AN OSGI BUNDLE MANIFEST4.
Auf der nächsten Seite entfernen wir den Haken bei der Checkbox GENERATE THE JAVA CLASS
THAT... und quittieren die Seite mit FINISH5.

1. Zuvor wird noch abgefragt, ob dieser Adapter darüber hinaus auch eine Seite einer Page Book View ist
(IPageBookViewPage). Wenn ja, darf sie sich noch gesondert initialisieren (innerhalb der Methode init-
Page). Eine Page Book ist eine View, die mehrere Seiten besitzt, von denen aber immer nur eine sichtbar ist.
Welche Seite sichtbar ist, steuert alleine die Page Book View. Die Outline View ist z.B. eine Page Book View.
Welche Seite angezeigt wird, hängt von dem Editor ab, der gerade den Fokus besitzt.
2. Die beiden Plug-Ins org.eclipse.core.runtime und org.eclipse.ui.views, die wir uns in den Workspace geholt
haben, benötigen wir nicht mehr und löschen sie über DELETE | ALSO DELETE CONTENTS... im Kontextmenü.
3. Das Plug-In befindet sich auf der CD im Verzeichnis Kap_9/texteditoroutline.
4. Am Ende dieses Kapitels befindet sich ein Abschnitt über OSGi, in dem erklärt wird, was es mit einem OSGi
Bundle Manifest auf sich hat.
5. Das Plug-In-Projekt befindet sich auf der CD im Verzeichnis Kap_9/texteditoroutline

415
eclipse_v01.book Seite 416 Montag, 30. Januar 2006 12:02 12

Eine Outline View für den Texteditor

Eclipse fragt uns nun, ob wir in die Plug-In-Development-Perpektive wechseln wollen. Ja,
wir wollen. Denn die Plug-In-Development-Perspektive bietet alle wichtigen Views des
Plug-In Development Environment.
Nun erzeugen wir im Source Folder des neu angelegten Projekts das Package com.ent-
wickler.eclipsebuch.texteditoroutline (im Kontextmenü des Source-Folder
NEW | PACKAGE wählen). In diesem Package werden wir nun die Klasse erzeugen, die für
den Inhalt der Outline View des Texteditors zuständig ist. Dazu muss sie das Interface
IContentOutlinePage implementieren. Eine Standardimplementierung, von der man
ableiten kann, existiert bereits: ContentOutlinePage. Dazu müssen wir diese Klasse in
den Klassenpfad bringen. Das Interface und die Klasse befinden sich in einem mit Eclipse
ausgelieferten Plug-In. Aber in welchem?
Um das herauszufinden, bieten sich zwei Alternativen an:
쐌 Wir erstellen einen extra Workspace, der alle Plug-Ins als Binary Projects enthält. Die-
ser Workspace dient nur dem Zweck, nach Herzenslust in den Plug-Ins nach Klassen
und Interfaces suchen zu können.
Dazu erstellen wir uns ein leeres Verzeichnis im Filesystem des Betriebssystems. Dann
starten wir eine zweite Instanz von Eclipse über
eclipse -data <vollständiger Pfad des neu angelegten Verzeichnisses>.
In dem Verzeichnis legt Eclipse nun einen neuen Workspace an. Sobald Eclipse gestar-
tet ist, öffnen wir die Plug-Ins View, wählen alle Plug-Ins an und importieren sie als
Binary Projects.
쐌 Wir legen ein leeres Plug-In-Projekt mit Namen dummy an, das sämtliche Plug-Ins im
Classpath enthält: FILE | NEW | PROJECT... | PLUG-IN-PROJECT | NEXT | PROJECT NAME: dum-
my. Wir entfernen den Haken aus der Checkbox CREATE AN OSGI BUNDLE MANIFEST|
NEXT. Wir entfernen den Haken aus der Checkbox GENERATE THE JAVA CLASS... | FINISH.
Im nun entstandenen Projekt öffnen wir die Datei plugin.xml (das Plug-In Manifest1)
durch Doppelklick2 und gehen auf die Seite DEPENDENCIES. Dort fügen wir über den
Button ADD im Bereich Required Plug-Ins sämtlich Plug-Ins hinzu und speichern das
Plug-In Manifest ab.
Nun können wir über ŸÁT nach dem Interface IContentOutlinePage suchen.
Wir finden es im Package org.eclipse.ui.views.contentoutline und laden es in
den Class File Editor. Nun können wir im Package Explorer über den Button LINK WITH
EDITOR sehen, in welchem Plug-In sich dieses Interface befindet: org.eclipse.ui.views.
Im Package Explorer sehen wir, dass sich im gleichen Plug-In auch ContentOutline-
Page befindet.
Doch wir können das Jar File aus diesem Plug-In nicht einfach in den Klassenpfad unseres
Plug-In-Projekts com.entwickler.eclipsebuch.texteditoroutline nehmen. Denn Eclipse be-
nutzt für das Laden von Klassen aus Plug-Ins seinen eigenen Classloader. Dieser kümmert

1. Wir werden im weiteren Verlauf dieses Kapitels Plug-In Manifests genauer kennen lernen.
2. Falls sich nicht der Plug-In Manifest Editor öffnet (sondern ein anderer Editor), so öffnen wir plugin.xml durch
OPEN WITH | PLUG-IN MANIFEST EDITOR im Kontextmenü von plugin.xml.

416
eclipse_v01.book Seite 417 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

sich wenig um den Klassenpfad, den wir im Plug-In-Projekt einstellen. Was zählt, ist nur
das Plug-In Manifest.
Ein Plug-In Manifest ist eine XML-Datei, die alle Konfigurationen eines Plug-Ins enthält.
Das Plug-In Manifest trägt stets den Namen plugin.xml und befindet sich auf der obersten
Ebene im Plug-In-Verzeichnis (bzw. im Plug-In-Projekt). Das nackte Plug-In Manifest un-
seres Plug-In sieht folgendermaßen aus:

<?xml version="1.0" encoding="UTF-8"?>


<?eclipse version="3.0"?>
<plugin
id="com.entwickler.eclipsebuch.texteditoroutline"
name="Texteditoroutline Plug-In"
version="1.0.0" provider-name="ENTWICKLER">
<runtime>
<library name="texteditoroutline.jar">
<export name="*"/>
</library>
</runtime>
</plugin>

Es enthält Namen und ID des Plug-Ins sowie den Namen des Jar Files, das den Code des
Plug-Ins enthält. Das Plug-In Manifest enthält auch die Information, welche anderen Plug-
Ins benötigt werden, z.B. weil aus diesen Klassen benutzt werden. Zum Glück muss man
zum Editieren des Plug-In Manifests nicht dessen XML-Format kennen. Denn Eclipse bie-
tet innerhalb des Plug-In Development Environment (PDE) den Plug-In Manifest Editor an.
Wir starten ihn mit einem Doppelklick auf das Plug-In Manifest plugin.xml.
Auf der Seite DEPENDENCIES können wir über den Button ADD angeben, welche Plug-Ins be-
nötigt werden. Wir wählen org.eclipse.ui.views und org.eclipse.ui (siehe Abb. 9.2).

Abb. 9.2: Hinzufügen von org.eclipse.ui.views zu den benötigten Plug-Ins

417
eclipse_v01.book Seite 418 Montag, 30. Januar 2006 12:02 12

Eine Outline View für den Texteditor

Nachdem wir das Plug-In Manifest abgespeichert haben, befinden sich im Container Plug-
In Dependencies im Package Explorer die Jar Files aus allen benötigten Plug-Ins.
Wir öffnen nun die Klasse ContentOutlinePage. Sie stammt aus dem Plug-In org.
eclipse.ui.views und implementiert die Outline View. In der Methode createControl er-
zeugt sie den Seiteninhalt:

/**
* The ContentOutlinePage implementation of this
* IContentOutlinePage method creates a tree viewer.
* Subclasses must extend this method configure the tree viewer
* with a proper content
* provider, label provider, and input element.
*/
public void createControl(Composite parent) {
treeViewer = new TreeViewer(parent,
SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
treeViewer.addSelectionChangedListener(this);
}

Gleich zu Beginn wird ein Tree Viewer (TreeViewer) erzeugt. Der Tree Viewer stammt
aus der Eclipse-Komponente JFace (Plug-Ins org.eclipse.jface.*) und bietet eine baumarti-
ge Darstellung von Informationen. Er basiert auf dem SWT Control Tree und reichert es
um weitere Funktionalitäten an. So kann man einem Tree Viewer ähnlich dem aus Swing
bekannten JTree ein Modell zuordnen, das den Inhalt definiert. Die Verbindung zwischen
dem Tree Viewer und dem Modell stellt ein Content Provider her. Der Content Provider für
einen Tree Viewer muss das Interface org.eclipse.jface.viewers.ITreeContent-
Provider implementieren1:

/**
* An interface to content providers for tree-structure-oriented
* viewers.
*/
public interface ITreeContentProvider
extends IStructuredContentProvider {
/**
* Returns the child elements of the given parent element.
*/
public Object[] getChildren(Object parentElement);

/**
* Returns the parent for the given element, or null
* indicating that the parent can‘t be computed.

1. In diesem und den meisten der noch folgenden Code-Auszüge aus Eclipse-Plug-Ins werden die Kommentare
aus Platzgründen verkürzt oder ausgelassen.

418
eclipse_v01.book Seite 419 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

* In this case the tree-structured viewer can‘t expand


* a given node correctly if requested.
*/
public Object getParent(Object element);

/**
* Returns whether the given element has children.
*/
public boolean hasChildren(Object element);
}

ITreeContentProvider ist wiederum von IStructuredContentProvider abgelei-


tet:

/**
* An interface to content providers for structured viewers.
*/
public interface IStructuredContentProvider
extends IContentProvider {
/**
* Returns the elements to display in the viewer
* when its input is set to the given element.
* These elements can be presented as rows in a table, items
* in a list, etc.
* The result is not modified by the viewer.
*/
public Object[] getElements(Object inputElement);
}

Die Methode getElements bekommt das Modell übergeben und gibt ein Array der Ele-
mente der obersten Ebene des Baums zurück. Klappt der Benutzer ein Element auf, so wird
es an die Methode getChildren übergeben, die ein Array der Kindelemente zurückgibt.
Wie ein Element im Baum dargestellt wird, bestimmt der Label Provider (ILabelPro-
vider), der dem Tree Viewer zugeordnet ist:

/**
* Extends IbaseLabelProvider with the methods
* to provide the text and/or image for the label
* of a given element.
* Used by most structured viewers, except table viewers.
*/
public interface ILabelProvider extends IBaseLabelProvider {
/**
* Returns the image for the label of the given element.
* The image is owned by the label provider
* and must not be disposed directly.

419
eclipse_v01.book Seite 420 Montag, 30. Januar 2006 12:02 12

Eine Outline View für den Texteditor

* Instead, dispose the label provider when no longer needed.


*/
public Image getImage(Object element);

/**
* Returns the text for the label of the given element.
*/
public String getText(Object element);
}

Die Methode getText bekommt jedes Element, das getElements und getChildren
des Content Providers zurückgeben, übergeben. getText gibt einen String zurück, wel-
cher dann im Baum angezeigt wird. Darüber hinaus kann die Methode getImage ein Bild
zurückgeben, das neben dem String angezeigt wird. Existiert zu einem Tree Viewer kein
Label Provider, so werden die Objekte des Baums über die toString-Methode in einen
String umgewandelt und angezeigt.
Wir wollen uns einen einfachen, aber sehr flexiblen Content Provider (Klasse TreeCon-
tentProvider) schreiben. Als Modell erwartet er eine Klasse vom Typ TreeNode (die
wir auch noch erstellen müssen). TreeNode besitzt einen Wert (Attribut value), der im
Tree Viewer angezeigt wird, und gegebenenfalls noch Kinder (Attribut children)1:

package com.entwickler.eclipsebuch.texteditoroutline;

public class TreeNode {


private Object value;
private Object children;

public TreeNode(Object value) {


this.value = value; children = null;
}

public TreeNode(Object value, Object children) {


this.value = value;
this.children = children;
}

public Object getChildren() {


return children;
}

1. Die Tatsache, dass die Kinder als java.lang.Object abgelegt werden, dient der späteren Erweiterung.
Dazu gleich mehr.

420
eclipse_v01.book Seite 421 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

public void setChildren(Object children) {


this.children = children;
}

public Object getValue() {


return value;
}

public void setValue(Object value) {


this.value = value;
}

public String toString() {


return String.valueOf(value);
}
}

Der Tree Content Provider ist schnell geschrieben. Für unseren ersten Einsatz von Tree-
ContentProvider sollen die Kinder eines TreeNode als Array abgelegt werden1 (siehe
Methoden getElements und getChildren). Die Methode hasChildren entscheidet,
ob ein TreeNode Kinder besitzt.

package com.entwickler.eclipsebuch.texteditoroutline;

import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.Viewer;

public class TreeContentProvider implements ITreeContentProvider {

public Object[] getChildren(Object parentElement) {


TreeNode node = (TreeNode) parentElement;
Object children = node.getChildren();
if (children instanceof Object[]) {
return (Object[]) children;
}
return null;
}

public Object getParent(Object element) {


return null;
}

1. Später sollen sie z.B. auch als java.util.Map abgelegt werden.

421
eclipse_v01.book Seite 422 Montag, 30. Januar 2006 12:02 12

Eine Outline View für den Texteditor

public boolean hasChildren(Object element) {


TreeNode node = (TreeNode) element;
Object children = node.getChildren();
return (children != null);
}

public Object[] getElements(Object inputElement) {


if (inputElement instanceof Object[]) {
return (Object[]) inputElement;
}
return null;
}

public void dispose() {


}

public void inputChanged(Viewer viewer,


Object oldInput, Object newInput) {
}
}

Da TreeNode eine Implementierung der toString-Methode besitzt, können wir auf den
Label Provider verzichten.
Der IContentOutlinePage-Adapter soll nun einen Tree Viewer mit einem TreeCon-
tentProvider erzeugen. Als Modell wird zunächst eine feste Baumstruktur von Tree-
Nodes direkt ausprogrammiert. Als Basisklasse nehmen wir ContentOutlinePage. Man
muss nur noch die createControl-Methode überschreiben, dabei jedoch die Implemen-
tierung der Vaterklasse aufrufen. In ihr wird ein Tree Viewer erzeugt, dem man nun einen
Content Provider und ein Modell zuweisen muss:

package com.entwickler.eclipsebuch.texteditoroutline;

import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.views.contentoutline.ContentOutlinePage;

public class TextEditorContentOutlinePage


extends ContentOutlinePage {

public void createControl(Composite parent) {


TreeNode[] level1 = new TreeNode[] {
new TreeNode("Hallo")
};
TreeNode[] level2 = new TreeNode[] {
new TreeNode("Das"),
new TreeNode("ist"),
new TreeNode("eine"),

422
eclipse_v01.book Seite 423 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

new TreeNode("Outline-View"),
};
TreeNode[] level3 = new TreeNode[] {
new TreeNode("für"),
new TreeNode("den"),
new TreeNode("Text-Editor"),
};
level1[0].setChildren(level2);
level2[3].setChildren(level3);
super.createControl(parent);
getTreeViewer().setContentProvider(new TreeContentProvider());
getTreeViewer().setInput(level1);
}
}

Damit wäre der Adapter für die Outline View des Texteditors fertig, hätten wir nicht noch
ein kleines Problem: Wie bringt man nun den Texteditor dazu, diesen Adapter bei einer
getAdapter-Anfrage zurückzuliefern? Es gibt mindestens zwei Alternativen.

9.3.3 Alternative 1: Überschreiben der Methode getAdapter


Der Texteditor wird durch die Klasse org.eclipse.ui.editors.text.TextEditor
im Plug-In org.eclipse.ui.editors implementiert. Die vorhandene Implementierung der Me-
thode getAdapter der Klassse TextEditor kann nur einen Adapter für das Interface
IEncodingSupport liefern:

public Object getAdapter(Class adapter) {


if (IEncodingSupport.class.equals(adapter))
return fEncodingSupport;
return super.getAdapter(adapter);
}

Durch eine kleine Erweiterung könnte sie auch unseren Adapter TextEditorContent-
OutlinePage (für IContentOutlinePage) zurückliefern1:

public Object getAdapter(Class adapter) {


/*
* Patch zur Unterstützung einer Outline View für
* den Text Editor
*/
if (IContentOutlinePage.class.equals(adapter))
return new TextEditorContentOutlinePage();

1. Der nachfolgende Code befindet sich auf der CD in der Datei Kap_9/ui.editors/TextEditor_getAdapter.txt.

423
eclipse_v01.book Seite 424 Montag, 30. Januar 2006 12:02 12

Eine Outline View für den Texteditor

if (IEncodingSupport.class.equals(adapter))
return fEncodingSupport;
return super.getAdapter(adapter);
}

Das ist mit Eclipse-Mitteln kein Problem. Doch bevor wir nun den Code eines Plug-In än-
dern, wollen wir warnend sämtliche uns zur Verfügung stehenden Zeigefinger heben:
Eclipse bietet viele Möglichkeiten, vorhandene Plug-Ins zu erweitern. Bevor man den Code
eines fremden Plug-Ins ändert, sollte man sich dies gut überlegen. Denn bei jedem Update
des Plug-Ins muss man den Code wieder von Neuem ändern. Wir tun dies in diesem Prak-
tikum ganz bewusst. An dieser Stelle tun wir dies, um den Code eines vorhandenen Plug-
Ins besser verstehen zu können. Später werden wir es tun, weil es keine anderen Erweite-
rungsmöglichkeiten gibt.
Dafür holen wir uns das Plug-In org.eclipse.ui.editors als Source Project in den Workspace
(im Kontextmenü des Plug-Ins innerhalb der Plug-Ins View: IMPORT | AS SOURCE PROJECT).
Im Projekt org.eclipse.ui.editors im Package Explorer können wir nun im Source Folder
(src) im Package org.eclipse.ui.editors.text die Klasse TextEditor öffnen und den Code
der Methode getAdapter wie oben ergänzen.
Damit sich das fehlerfrei übersetzen lässt, muss man im Plug-In Manifest von org.eclipse.
ui.editors unser Plug-In com.entwickler.eclipsebuch.texteditoroutline sowie das Plug-In
org.eclipse.ui.views als benötigte Plug-Ins hinzufügen (Button ADD auf der Seite DEPENDEN-
CIES im Plug-In Manifest Editor) und in der Klasse TextEditor die benötigten Import-An-
weisungen erzeugen (SOURCE | ORGANIZE IMPORTS). Das müsste es gewesen sein.
Um die neue Outline View für den Texteditor auszuprobieren, können wir aus Eclipse her-
aus eine weitere Eclipse-Instanz (eine so genannte Eclipse Application) starten. Diese ent-
hält alle installierten Plug-Ins aus dem Verzeichns plugins des Eclipse-Installationsver-
zeichnisses sowie alle Plug-Ins aus den Plug-In-Projekten des aktuellen Workspace. Man
kann eine Eclipse Application auch im Debug-Modus starten und Breakpoints innerhalb
der Plug-In-Projekte setzen.
Zum Starten einer Eclipse Application erzeugen wir im Dialog für Run Configurations
(RUN | RUN...) eine neue Eclipse Application Configuration (ECLIPSE APPLICATION | NEW).
Wir nennen sie eclipse-application-01. Im Tab-Folder MAIN muss nun im Feld LOCATION
ein leeres Verzeichnis für den Workspace gewählt werden. Auf der Tab-Seite PLUG-INS
wählen wir die erste Alternative (LAUNCH WITH ALL WORKSPACE AND ENABLED EXTERNAL PLUG-
INS), d.h., Eclipse startet mit allen installierten Plug-Ins und den Plug-In-Projekten aus dem
Workspace. Und es kann losgehen (RUN).
In dem zunächst noch leeren Workspace erzeugen wir über FILE | NEW | PROJECT... | JAVA PRO-
JECT | NEXT | test | FINISH zunächst ein Projekt mit Namen test. Im Kontextmenü des neuen
Projekts erzeugen wir über NEW | FILE | test.txt | FINISH eine Textdatei. Kaum ist diese im
Texteditor geöffnet, so sehen wir schon unsere neue Outline View (siehe Abb. 9.3).
Doch nun werden wir eine Alternative kennen lernen, wie man ohne das Ändern von beste-
hendem Code auskommt.

424
eclipse_v01.book Seite 425 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

Abb. 9.3: Die neue Outline View des Texteditors

9.3.4 Alternative 2: der Adapter Manager


Zur Demonstration dieser Alternative wollen wir eine sinnvollere Outline View für den
Texteditor erstellen. Diese soll mehr tun, als nur freundlich zu grüßen. Vielmehr wollen wir
die View speziell für Java-Property-Dateien benutzen. Java Properties haben meist hierar-
chisch aufgebaute Namen, wie z.B.:

x.y.z = Wert

Die Namen aller Properties sollen nun baumartig in der Outline View angezeigt werden.
Dabei wollen wir den für die Outline View des Texteditors erstellten TreeContentProvi-
der nutzen. Wir werden deshalb ein Plug-In anlegen, das allgemein nützliche Klassen ent-
hält. Wir erzeugen wie oben ein Plug-In mit Namen com.entwickler.eclipsebuch.controls und
legen dort im Source Folder ein Package mit Namen com.entwickler.eclipsebuch.controls
an1. Mit den Refactoring-Mitteln von Eclipse kopieren wir zuerst TreeNode.java und dann
TreeContentProvider.java aus dem Plug-In com.entwickler.eclipsebuch.texteditoroutline in
dieses neue Package (im Kontextmenü von TreeNode.java bzw. TreeContentProvider.java
im Package Explorer den Menüpunkt COPY wählen und dann im Kontextmenü des Package
com.entwickler.eclipsebuch.controls den Menüpunkt PASTE wählen). Im Plug-In Manifest
von com.entwickler.eclipsebuch.controls müssen wir noch org.eclipse.jface als benötigtes
Plug-In angeben (Button ADD auf der Seite DEPENDENCIES im Plug-In Manifest Editor)2.
Um den Baum der Properties schneller aufbauen und sortiert anzeigen zu können, sollen die
Knoten (TreeNode) in einer java.util.TreeMap abgelegt werden. Dafür muss der
TreeContentProvider entsprechend angepasst werden:

1. Dies wird die erste Version dieses Plug-Ins. Weitere Versionen mit Erweiterungen werden folgen. Diese Ver-
sion befindet sich auf der CD im Verzeichnis Kap_9/controls/v1.
2. Die Projekte com.entwickler.eclipsebuch.texteditoroutline und org.eclipse.ui.editors sollten wir jetzt wieder
schließen (CLOSE PROJECT) oder löschen (DELETE). Sie haben ihre Schuldigkeit getan und werden nicht mehr
benötigt.

425
eclipse_v01.book Seite 426 Montag, 30. Januar 2006 12:02 12

Eine Outline View für den Texteditor

package com.entwickler.eclipsebuch.controls;

import java.util.Map;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.Viewer;

public class TreeContentProvider implements ITreeContentProvider {

public Object[] getChildren(Object parentElement) {


TreeNode node = (TreeNode) parentElement;
Object children = node.getChildren();
if (children instanceof Object[]) {
return (Object[]) children;
}
else if(children instanceof Map) {
return ((Map) children).values().toArray();
}
return null;
} ...

public boolean hasChildren(Object element) {


TreeNode node = (TreeNode) element;
Object children = node.getChildren();
if(children == null) {
return false;
}
else {
if(children instanceof Map) {
return ((Map) children).size() > 0;
}
}
return false;
}

public Object[] getElements(Object inputElement) {


if (inputElement instanceof Object[]) {
return (Object[]) inputElement;
}
else if(inputElement instanceof Map) {
return ((Map) inputElement).values().toArray();
}
return null;
}

(...)

426
eclipse_v01.book Seite 427 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

Da wir gerade zum zweiten Mal eine View für einen Editor erstellen, sei die Frage erlaubt:
Was ist eigentlich der Unterschied zwischen einer View und einem Editor?
쐌 Ein Editor bearbeitet den Inhalt eines Dokuments. Änderungen im Editor wirken sich
nicht sofort auf den Inhalt dieses Dokuments aus. Erst beim Abspeichern werden die
Änderungen in das Dokument geschrieben.
쐌 Eine View zeigt Daten an, die aus einer beliebigen Quelle kommen können. Änderun-
gen in einer View wirken sich sofort auf diese Daten aus.
Hinter jedem Editor steht also ein Dokument, das über den Editor bearbeitet wird. Über fol-
gende Zeilen erhält man dieses Dokument:

ITextEditor editor;
IEditorInput input = editor.getEditorInput();
IDocumentProvider provider = editor.getDocumentProvider();
IDocument document = provider.getDocument(input);

Nun wollen wir den Adapter für unsere Outline View für den Texteditor implementieren.
Wir werden ihn in ein eigenes Plug-In stecken, wozu wir jetzt ein neues Plug-In-Projekt mit
Namen com.entwickler.eclipsebuch.javapropertiesoutline erzeugen1. Anders als bei den
bisher erzeugten Plug-Ins setzen wir aber diesmal den Haken bei GENERATE THE JAVA CLASS
THAT CONTROLS THE PLUG-IN‘S LIFE CYCLE nicht:

FILE | NEW | PROJECT... | PLUG-IN PROJECT | NEXT | Project Name: com.entwickler.eclipse-


buch.javapropertiesoutline. Wir entfernen den Haken bei CREATE AN OSGI BUNDLE MANIFEST
| NEXT | FINISH.
Im Source Folder des neuen Plug-In-Projekts ist nun ein gleichnamiges Package entstan-
den. In diesem erzeugen wir die Klasse JPOAdapter2. Sie soll Zeile für Zeile der Java-
Property-Datei auslesen. Falls eine Zeile den Aufbau einer Property besitzt, wird über einen
regulären Ausdruck der Name der Property ausgelesen. Es wird der reguläre Ausdruck

*((?:\\w|\\.)+) *=.*

benutzt, der auf alle Strings passt, die folgenden Aufbau haben:
쐌 Teilausdruck: * Zuerst dürfen beliebig viele (oder keine) Leerzeichen stehen.
쐌 Teilausdruck: ((?:\\w|\\.)+) Dann müssen beliebig viele (aber mindestens einer)
Buchstaben, Zahlen, Unterstriche (\w) oder Punkte (\.) stehen. Diese Zeichen können
(wegen der äußeren Klammer im Teilausdruck) danach ausgelesen werden3.

1. Dies ist die erste Version dieses Plug-Ins. Eine weitere wird folgen. Das Plug-In-Projekt befindet sich auf der
CD im Verzeichnis Kap_9/javapropertiesoutline/v1.
2. JPO steht für Java Properties Outline.
3. Die äußere Klammer definiert eine so genannte Capturing Group, d.h., der Teilausdruck kann ausgelesen wer-
den. Die innere Klammer definiert (wegen '?:') eine so genannte Non Capturing Group. Der Teilausdruck
kann nicht ausgelesen werden. Die Klammer dient hier nur zur Gruppierung, so dass sich das '+' auf den Aus-
druck in der inneren Klammer bezieht.

427
eclipse_v01.book Seite 428 Montag, 30. Januar 2006 12:02 12

Eine Outline View für den Texteditor

쐌 Teilausdruck: * Dann dürfen beliebig viele (oder keine) Leerzeichen stehen.


쐌 Teilausdruck: = Dann muss ein Gleichheitszeichen stehen.
쐌 Teilausdruck: .* Dann dürfen beliebig viele (oder keine) beliebigen Zeichen stehen.
Folgende Zeile würde z.B. als Property erkannt werden: dies.ist.eine.property = dies ist
eine Property. Falls der reguläre Ausdruck passt, kann der Namen der Property ausgelesen
werden:

String pattern = " *((?:\\w|\\.)+) *=.*";


Pattern p = Pattern.compile(pattern);
Matcher m =
p.matcher("dies.ist.eine.property = dies ist eine Property");
String property = m.group(1);

Nachdem die obige Zeile ausgeführt worden ist, hat die Variable property den Wert
dies.ist.eine.property. Über die Methode split von java.lang.String zerlegen wir die
Property in ihre Bestandteile. Diese hängen wir dann in das Modell des Tree Viewer ein.
Dazu dient die Methode insertInLayer. Sie hängt jeweils einen Namensbestandteil
(partOfName) in eine Ebene des Baums ein und gibt einen Referenz auf die jeweils nächs-
te Ebene (subLayer) zurück. Da der Adapter Zugriff auf den Editor benötigt, bekommt er
bei der Instanziierung eine Referenz auf den Editor übergeben1:

package com.entwickler.eclipsebuch.javapropertiesoutline;

import java.util.Map;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.views.contentoutline.ContentOutlinePage;
import com.entwickler.controls.TreeContentProvider;
import com.entwickler.controls.TreeNode;

1. Damit die weiteren abgedruckten Listings fehlerfrei kompilieren, müssen dem Plug-In noch einige Plug-In-
Abhängigkeiten hinzugefügt werden. Siehe hierzu das Plug-In-Projekt auf der CD unter Kap_9/javaproper-
tiesoutline/v1. Um den Leser im Fließtext nicht zu sehr zu langweilen, werden auch bei den weiteren Plug-Ins
dieses Kapitels die Plug-In-Abhängigkeiten nicht explizit beschrieben.

428
eclipse_v01.book Seite 429 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

public class JPOAdapter extends ContentOutlinePage {


private ITextEditor editor;
private TreeMap model;

public JPOAdapter(ITextEditor editor) {


this.editor = editor;
}

/**
* Fuege einen Teil einer Property (partOfName) in
* den Baum ein. * @param partOfName Teil der Property
* @param layer Ebene, auf der eingefuegt werden soll
* @return
*/
private Map insertInLayer(String partOfName, Map layer) {
Map subLayer;
if (layer.containsKey(partOfName)) {
TreeNode node = (TreeNode) layer.get(partOfName);
subLayer = (Map) node.getChildren();
}
else {
subLayer = new TreeMap();
TreeNode node = new TreeNode(partOfName, subLayer);
layer.put(partOfName, node);
}
return subLayer;
}

/**
* Lese alle Zeilen aus, zerteile die Properties in ihre
* Bestandteile und haenge sie in den Baum ein
*/
public void createControl(Composite parent) {
try {
super.createControl(parent);
IEditorInput input = editor.getEditorInput();
IDocumentProvider provider = editor.getDocumentProvider();
IDocument document = provider.getDocument(input);
int numOfLines = document.getNumberOfLines();
String pattern = " *((?:\\w|\\.)+) *=.*";
Pattern p = Pattern.compile(pattern);
model = new TreeMap();
// iteriere ueber alle Zeilen
for (int l = 0; l < numOfLines; l++) {
IRegion region = document.getLineInformation(l);
String line =
document.get(region.getOffset(), region.getLength());
Matcher m = p.matcher(line);

429
eclipse_v01.book Seite 430 Montag, 30. Januar 2006 12:02 12

Eine Outline View für den Texteditor

if (m.matches()) {
// die Zeile stellt eine Property dar
String property = m.group(1);
// zerlege die Property in ihre Bestandteile
String[] propertyParts = property.split("\\.");
Map layer = model;
for(int i = 0; i < propertyParts.length; i++) {
layer = insertInLayer(propertyParts[i], layer);
}
}
}
getTreeViewer().
setContentProvider(new TreeContentProvider());
getTreeViewer().setInput(model);
} catch (BadLocationException e) {
JavapropertiesoutlinePlugin.getDefault().log(e);a
}
}
}
a. Die Methode log der Klasse JavapropertiesoutlinePlugin erstellen wir weiter unten.

Wir wollen nun den Adapter JPOAdapter dem Texteditor zuordnen, ohne die Klasse
org.eclipse.ui.editors.text.TextEditor ändern zu müssen. Dafür schauen wir
uns einmal an, was passiert, wenn der Texteditor aufgefordert wird, einen Adapter für die
Outline View zu liefern, das heißt, wenn die Methode getAdapter von TextEditor mit
dem Argument IContentOutlinePage aufgerufen wird. Wir wollen in der ersten Zeile
der Methode doCreatePage von org.eclipse.ui.views.contentoutline.Con-
tentOutline einen Breakpoint setzen. Doch um den Breakpoint setzen zu können, müs-
sen wir erst einmal das Plug-In org.eclipse.ui.views als Binary Project in den Workspace
holen. Dann setzen wir den Breakpoint und starten die oben konfigurierte Eclipse Applica-
tion eclipse-application-01 im Debug-Modus (RUN | DEBUG HISTORY | eclipse-application-
01). Falls der Texteditor noch geöffnet ist, schlägt der Breakpoint zu, bevor Eclipse ganz
gestartet ist. In der Variables View sehen wir, dass das Objekt, auf das die Variable part
referenziert, der Texteditor ist (siehe Abb. 9.4).

Abb. 9.4: Variable part beim Halt am Breakpoint

430
eclipse_v01.book Seite 431 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

Wir machen einen STEP INTO (Í) , um zu sehen, wie der Texteditor auf die Anfrage nach
einem Adapter für das IContentOutlinePage-Interface antwortet. Der Texteditor kann
nur einen Adapter für das Interface IEncodingSupport zurückliefern:

public Object getAdapter(Class adapter) {


if (IEncodingSupport.class.equals(adapter))
return fEncodingSupport;
return super.getAdapter(adapter);
}

Er gibt aber die Anfrage an seinen Vater (org.eclipse.ui.texteditor.Extended-


TextEditor) weiter. Wir gehen mit Î bis zur letzten Zeile und gehen dann mit Í in
die Methode getAdapter von ExtendedTextEditor:

public Object getAdapter(Class adapter) {


if (IGotoMarker.class.equals(adapter))
return fGotoMarkerAdapter;
if (IAnnotationAccess.class.equals(adapter))
return getAnnotationAccess();
if (adapter == IShowInSource.class) {
(...)
}
return super.getAdapter(adapter);
}

Auch ExtendedTextEditor kann keinen Adapter für IContentOutlinePage liefern


und verweist wiederum auf seinen Vater (org.eclipse.ui.texteditor.Abstract-
TextEditor). Wir gehen wieder mit Î bis zur letzten Zeile und dort mit Í hinein:

public Object getAdapter(Class required) {


if (IEditorStatusLine.class.equals(required)) {
(...)
}
if (IVerticalRulerInfo.class.equals(required)) {
(...)
}

(...)

return super.getAdapter(required);
}

Doch auch AbstractTextEditor kann keinen Adapter für das Interface IContentOut-
linePage liefern. Wir gehen wieder mit Î bis zur letzten Zeile, dort mit Í hinein und
landen in der Methode getAdapter der Klasse org.eclipse.ui.part.Workbench-
Part:

431
eclipse_v01.book Seite 432 Montag, 30. Januar 2006 12:02 12

Eine Outline View für den Texteditor

public Object getAdapter(Class adapter) {


/**
* This implementation of the method declared by IAdaptable
* passes the request along to the platform‘s adapter manager
*/
return Platform.getAdapterManager().getAdapter(this, adapter);
}

Hier wird es interessant. Die Methode getAdapter der Klasse WorkbenchPart leitet die
Anfrage an einen zentralen Adapter Manager weiter. Über den Adapter Manager können
Adapter für andere Klassen angemeldet werden, die dann vom Adapter Manager aufge-
rufen werden. Vielleicht hat ja doch irgendjemand einen IContentOutlinePage-Adap-
ter für die Klasse TextEditor geschrieben. Und genau das ist die Stelle, wo wir unseren
Adapter einhängen können. Dazu müssen wir Folgendes tun:
쐌 Wir müssen eine Adapter Factory (JPOAdapterFactory) schreiben, die einen Adap-
ter für das Interface IContentOutlinePage erzeugt. Dabei bekommt sie auch eine
Referenz für das konkrete Objekt (hier der Text Editor), an dem die getAdapter-Me-
thode aufgerufen wurde.
쐌 Wir müssen diese Adapter Factory beim Adapter Manager für die Klasse TextEditor
anmelden. Der obige Aufruf (Platform.getAdapterManager().getAdapter
(this, adapter)) wird dann einen Adapter zurückliefern, der über diese Adapter
Factory erzeugt worden ist.
JPOAdapterFactory muss außerdem noch die Methode getAdapterList anbieten, die
ein Array aller Interfaces zurückliefert, für die diese Factory einen Adapter erzeugen kann.
In der Methode getAdapter prüft JPOAdapterFactory ab, ob das Interface, für das sie
einen Adapter liefern soll, IContentOutlinePage ist. Und sie prüft ab, ob das Objekt, an
dem ursprünglich getAdapter aufgerufen wurde, ein Texteditor ist. Wenn dem so ist, er-
zeugt sie einen Adapter für die Outline View des Texteditors:

package com.entwickler.eclipsebuch.javapropertiesoutline;

import org.eclipse.core.runtime.IAdapterFactory;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;

public class JPOAdapterFactory implements IAdapterFactory {

public Object getAdapter(Object adaptableObject,


Class adapterType) {
if (adapterType.equals(IContentOutlinePage.class)) {
if (adaptableObject instanceof ITextEditor) {
return new JPOAdapter((ITextEditor) adaptableObject);
}
}
return null;

432
eclipse_v01.book Seite 433 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

public Class[] getAdapterList() {


return new Class[] { IContentOutlinePage.class };
}
}

Anmelden kann man die Adapter Factory beim Adapter Manager mit der folgenden Anwei-
sung:

Platform.getAdapterManager().registerAdapters(
new JPOAdapterFactory(), TextEditor.class);

Doch an welcher Stelle kann diese Anweisung ausgeführt werden? Man benötigt eine Life-
cycle-Klasse für ein Plug-In, die über den Start des Plug-Ins informiert wird. Genau das ist
die so genannte Plug-In-Klasse:
Jedem Plug-In kann eine Plug-In-Klasse zugewiesen werden. Wenn das Plug-In zum ersten
Mal aufgerufen wird, wird ein Objekt dieser Klasse erzeugt1 und die Methode start an
dieser Klasse aufgerufen. Beim Erzeugen des Plug-Ins hat Eclipse für uns eine Plug-
In-Klasse erzeugt2. In der Methode start fügen wir die Anweisung zum Anmelden der
Adapter Factory beim Adapter Manager ein. Außerdem fügen wir noch die Methode log
ein. Über sie kann man Exceptions in der Log View ausgeben3:

package com.entwickler.eclipsebuch.javapropertiesoutline; import


org.eclipse.ui.plugin.*; import
org.eclipse.jface.resource.ImageDescriptor; import
org.osgi.framework.BundleContext; /**
* The main plugin class to be used in the desktop.
*/
public class JavapropertiesoutlinePlugin extends AbstractUIPlugin
{

//The shared instance.


private static JavapropertiesoutlinePlugin plugin;

/**
* The constructor.
*/
public JavapropertiesoutlinePlugin() {
plugin = this;

1. Da die Plug-In-Klasse als Singleton implementiert wird, ist es auch das einzige Objekt.
2. Weil wir den Haken bei GENERATE THE JAVA CLASS THAT CONTROLS THE PLUG-IN´S LIFE CYCLE nicht entfernt haben.
3. Sie wird z.B. in der Klasse JPOAdapter (s.o.) bei der Behandlung von Exceptions benutzt.

433
eclipse_v01.book Seite 434 Montag, 30. Januar 2006 12:02 12

Eine Outline View für den Texteditor

/**
* This method is called upon plug-in activation
*/
public void start(BundleContext context) throws Exception {
super.start(context);
Platform.getAdapterManager().registerAdapters(
new JPOAdapterFactory(), TextEditor.class);
}

/**
* This method is called when the plug-in is stopped
*/
public void stop(BundleContext context) throws Exception {
super.stop(context);
plugin = null;
}

/**
* Returns the shared instance.
*/
public static JavapropertiesoutlinePlugin getDefault() {
return plugin;
}

(...)

public void log(Throwable e) {


IStatus status = new Status(IStatus.ERROR, PLUGIN_ID,
-1, "Exception", e);
getLog().log(status);
}
}

Im Plug-In Manifest hat Eclipse beim Erzeugen des Plug-In-Projekts die Klasse Java-
propertiesoutlinePlugin als Plug-In-Klasse eingetragen (siehe Abb. 9.5).

Abb. 9.5: Konfiguration der Klasse JavapropertiesoutlinePlugin als Plug-In-Klasse

434
eclipse_v01.book Seite 435 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

Wir setzen an der ersten Anweisung der Methode start einen Breakpoint und starten die
Eclipse Application im Debug-Modus erneut1. Doch wir müssen enttäuscht feststellen, dass
unser kluger Plan nicht aufgeht. Denn es wird zuerst der Breakpoint in der Methode do-
CreatePage der Klasse ContentOutline erreicht2, d.h., dass die Anmeldung unserer
Adapter Factory nicht rechtzeitig erfolgt. Und damit kann unser Adapter leider nicht zum
Zug kommen. Um genau zu sein, wird die Methode start in der Klasse JPOPlugin zu-
nächst überhaupt nicht aufgerufen. Sie wird erst aufgerufen, wenn das Plug-In gestartet
wird.
Es stellt sich die Frage: Wie und wann wird ein Plug-In gestartet? Ein Plug-In wird dann ge-
startet, wenn es eine Extension in einen Extension Point eines anderen Plug-Ins einhängt
und von diesem anderen Plug-In aufgerufen wird. Nächste Frage: Was ist ein Extension
Point? Ein Extension Point ist ein Einstiegspunkt, an dem sich ein Plug-In in ein anderes
Plug-In einhängen kann. Wenn ein Plug-In – nennen wir es Plug-In A – sich in ein anderes
Plug-In – nennen wir es Plug-In B – einhängen will, muss B einen Extension Point bereit-
stellen und A für diesen Extension Point eine Extension implementieren. Extension Point
und Extension werden jeweils im Manifest des Plug-Ins deklariert. Ein Extension Point gibt
in der Regel über eine XML-Schema3-Definition bekannt, in welchem Format die Extensi-
ons formuliert werden müssen.
Wir suchen nun nach einem Extension Point, in den wir uns mit einer Extension einhängen
können, so dass unser Plug-In rechtzeitig gestartet wird. Über die Plug-In-Seite im Search-
Dialog (SEARCH | SEARCH...) suchen wir nach den Deklarationen von allen Extension Points
(siehe Abb. 9.6).

Abb. 9.6: Suche nach den Deklarationen aller Extension Points

1. Gegebenenfalls müssen wir die noch laufende Run-time Workbench vorher stoppen (RUN | TERMINATE) in der
Debug-Perspektive.
2. Wo getAdapter am Texteditor aufgerufen wird.
3. XML Schema ist eine XML-basierte Sprache zur Beschreibung von XML-Formaten. Sie ist zu vergleichen mit
der Document Type Definition (DTD). Sie kann jedoch deutlich präziser beschreiben. Eclipse verwendet ein
Derivat der Sprache XML Schema.

435
eclipse_v01.book Seite 436 Montag, 30. Januar 2006 12:02 12

Eine Outline View für den Texteditor

Der Extension Point org.eclipse.ui.startup sieht so aus, als könnte er das liefern, was wir
suchen. Über SHOW DESCRIPTION im Kontextmenü sehen wir uns die Beschreibung des
Extension Point an:
„This extension point is used to register plugins that want to be activated on startup.“ Das
klingt doch vielversprechend. Mit einem Doppelklick öffnet sich das Plug-In Manifest des
Plug-Ins org.eclipse.ui, in dem der Extension Point deklariert ist. In dem Plug-In Manifest
findet man folgende Zeile:

<Extension Point
id="startup"
name="%ExtPoint.startup"
schema="schema/startup.exsd"/>

Die XML-Schema-Datei heißt startup.exsd und liegt in einem Verzeichnis namens schema.
Dieses Verzeichnis befindet sich aber – unverständlicherweise – nicht innerhalb des Plug-
Ins org.eclipse.ui. Man findet diese Datei im Verzeichnis src/org.eclipse.ui_3.1.0 des Plug-
Ins org.eclipse.rcp.source. Öffnen wir diese Datei (in der Plug-Ins View) im PDE Schema
Editor und sehen uns dort auf der Seite startup.exsd1 den Source Code an, so finden wir dort
folgenden Ausschnitt2:

<element name="extension"> <complexType> <sequence>


<element ref="startup" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="point" type="string" use="required">
<annotation>
<documentation>
a fully qualified identifier of the target extension
point
</documentation>
</annotation>
</attribute>
<attribute name="id" type="string">
<annotation>
<documentation>
an optional identifier of the extension instance
</documentation>
</annotation>
</attribute>
<attribute name="name" type="string">
<annotation>
<documentation>

1. Die Fähigkeiten des PDE Schema Editors werden wir weiter unten kennen lernen.
2. Die Backslashes (\) sollen anzeigen, dass es in der nächsten Zeile weitergeht. Sie dürfen nicht eingegeben
werden.

436
eclipse_v01.book Seite 437 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

an optional name of the extension instance


</documentation>
<appInfo>
<meta.attribute translatable="true"/>
</appInfo>
</annotation>
</attribute>
</complexType>
</element>

<element name="startup">
<complexType>
<attribute name="class" type="string">
<annotation>
<documentation>
a fully qualified name of the class that implements
&lt;samp&gt;org.eclipse.ui.IStartup&lt;/samp&gt;.If not
specified, the plug-in class is used. Do not specify the plug-in
class as an explicit value, or it will be instantiated twice (once
by regular plug-in activation, and once by this mechanism).
Since release 3.0.
</documentation>
<appInfo>
<meta.attribute kind="java"
basedOn="org.eclipse.ui.IStartup"/>
</appInfo>
</annotation>
</attribute>
</complexType>
</element>

Wir sehen, dass der Extension Point zumindest das Element extension mit dem Attribut
point erwartet (use="required"). Die Attribute name und id sind optional. Des Weite-
ren können innerhalb des Elements extension noch beliebig viele Elemente startup mit
einem Attribut class auftreten. Das class-Attribut verweist auf eine Klasse, die das In-
terface org.eclipse.ui.IStartup implementieren muss.
Dieses Interface enthält die Methode earlyStartup, die vom Plug-In org.eclipse.ui beim
Starten von Eclipse aufgerufen wird.

/**
* Plug-Ins that register a startup extension
*will be activated after
* the Workbench initializes and have an opportunity to run
* code that can‘t be implemented using the normal contribution
* mechanisms.
*/

437
eclipse_v01.book Seite 438 Montag, 30. Januar 2006 12:02 12

Eine Outline View für den Texteditor

public interface IStartup {


/**
* Will be called in a separate thread after
* the workbench initializes.
* (...)
*/
public void earlyStartup();
}

Wir erzeugen eine Klasse mit Namen JPOEarlyStartup, die das Interface IStartup im-
plementiert. Die Methode earlyStartup lassen wir allerdings leer:

package com.entwickler.eclipsebuch.javapropertiesoutline;
import org.eclipse.ui.IStartup;
public class JPOEarlyStartup implements IStartup {
public void earlyStartup() { }
}

Nun erzeugen wir eine Extension für den Extension Point org.eclipse.ui.startup und tragen
im Element startup die obige Klasse ein. Dank PDE steht uns dafür ein komfortabler Edi-
tor zur Verfügung und wir müssen uns um die Interpretation der XML-Schema-Beschrei-
bung eines Extension Point nicht kümmern.
Dazu öffnen wir das Plug-In Manifest plugin.xml des Plug-Ins com.entwickler.eclipse-
buch.javapropertiesoutline im Plug-In Manifest Editor (im Kontextmenü: OPEN WITH |
PLUG-IN MANIFEST EDITOR). Auf der Seite EXTENSION erzeugen wir zunächst eine neue Ex-
tension über den Button ADD.... Nun erscheint eine Liste aller Extension Points. Wir wählen
org.eclipse.ui.startup und FINISH.
Auf der Seite EXTENSIONS erscheint nun die neue Extension. Um das Element startup zu
erzeugen, wählen wir im Kontextmenü der neuen Extension NEW | STARTUP. Auf der rechten
Seite tragen wir die Klasse JPOEarlyStartup ein (siehe Abb. 9.7):

Abb. 9.7: Erzeugen des Elements startup

438
eclipse_v01.book Seite 439 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

Auf der Seite plugin.xml sehen wir nun die Definition der neuen Extension1:

<extension
point="org.eclipse.ui.startup">
<startup
class="com.entwickler.eclipsebuch.javapropertiesoutline.\
JPOEarlyStartup"/>
</extension>

Falls die Eclipse Application noch läuft, brechen wir sie ab (RUN | TERMINATE). Dann setzen
wir einen Method Breakpoint auf den Methodeneingang von JPOEarlyStartup (RUN |
TOGGLE METHOD BREAKPOINT). Nun starten wir erneut die Eclipse Application im Debug-
Modus. Falls der Texteditor noch offen ist, wird als Erstes der Breakpoint in der Methode
doCreatePage der Klasse ContentOutline erreicht2. Wir überspringen ihn mit Ð. Als
Nächstes hält die Eclipse Application am Breakpoint in der Methode start der Plug-In-
Klasse JavapropertiesoutlinePlugin. Erst nachdem wir mit Ð diesen Breakpoint
übersprungen haben, landen wir beim Breakpoint beim Methodeneingang von early-
Startup. Was ist passiert?
Durch die Extension für den Extension Point org.eclipse.ui.startup will das Plug-In org.
eclipse.ui die Methode earlyStartup der Klasse JPOEarlyStartup aufrufen. Dadurch
wird unser Plug-In com.entwickler.eclipsebuch.javapropertieseditor gestartet. Und wenn
ein Plug-In gestartet wird, wird als Erstes die Methode start in der Plug-In-Klasse aufge-
rufen.
In der Eclipse Application legen wir im Projekt test eine Textdatei mit Namen test.proper-
ties an. Sofort stehen wir am Breakpoint in der Methode doCreatePage von Conten-
tOutline an der Anweisung3:

Object obj = part.getAdapter(IContentOutlinePage.class);

Die Variable part zeigt auf ein Objekt vom Typ TextEditor. Wir führen die aktuelle An-
weisung aus (Î). Und siehe da: obj zeigt auf ein Objekt vom Typ JPOAdapter (siehe
Abb. 9.8).

Abb. 9.8: Die Variable obj enthält einen JPOAdapter

1. Die Backslashes (\) sollen anzeigen, dass es in der nächsten Zeile weitergeht. Sie dürfen nicht eingegeben werden.
2. Das ist weniger schön. Warum werden wir bald sehen.
3. Falls sich der Texteditor sofort öffnet. Sonst müssen wir ihn erst für die Datei test.properties öffnen.

439
eclipse_v01.book Seite 440 Montag, 30. Januar 2006 12:02 12

Eine Outline View für den Texteditor

Es hat also funktioniert. Obwohl wir an der Klasse TextEditor nichts geändert haben, lie-
fert ein Aufruf ihrer Methode getAdapter nun einen JPOAdapter zurück und nicht, wie
vorher, null. Mit Ð setzen wir die Eclipse Application fort. Oh, welch Freude! Wir se-
hen eine Outline View für den Texteditor (siehe Abb. 9.9), allerdings noch leer. Aber auch
die Datei test.properties ist ja noch leer.

Abb. 9.9: Die Outline View für den Texteditor – leider noch leer.

Doch unsere unbändige Freude wird schnell ein jähes Ende finden. Denn wir tippen nun
folgenden Text in die Datei ein:

dies.ist.eine.property = Dies ist eine Property

Oh, welch Enttäuschung! Die Outline View ist immer noch so leer wie in Abb. 9.9. Wenn
wir aber die Datei speichern, schließen und wieder öffnen, so bleiben wir erst einmal wie-
der am Breakpoint in der doCreatePage-Methode von ContentOutline hängen. Wir
fahren mit Ð fort und – unser Herz springt vor Freude – die Outline View ist gefüllt (sie-
he Abb. 9.10).

Abb. 9.10: Die Outline View für den Texteditor – endlich gefüllt.

440
eclipse_v01.book Seite 441 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

Wir möchten „verweile doch!“ rufen. Doch auch diesen Glücksmoment können wir nicht
lange festhalten, denn unser unbändiger Forscherdrang lässt uns zum nächsten Problem ei-
len. Dazu schließen wir die Eclipse Application und starten sie erneut im Debug-Modus.
Wieder landen wir zuerst beim Breakpoint in der doCreatePage-Methode von Content-
Outline und erst nach einem Ð in der start-Methode von Javapropertiesout-
linePlugin. Wie es die Unken oben schon gerufen haben: Das ist ein Problem. Denn
wenn ContentOutline zum ersten Mal die Outline View aufbaut, ist die Factory für den
JPOAdapter noch gar nicht beim Adapter Manager angemeldet. Das geschieht erst in der
Methode start. Die fatale Folge dieses Timing-Problems ist, dass die Outline View für
den Texteditor erst einmal wieder verschwunden ist (siehe Abb. 9.11).

Abb. 9.11: Die Outline View ist wieder verschwunden

Erst wenn man kurzzeitig einen anderen Editor in den Vordergrund holt, kommt die Outline
View wieder (wie in Abb. 9.10) zurück. Wir haben also noch zwei Probleme zu lösen:
쐌 Änderungen im Texteditor werden erst nach Schließen und wieder Öffnen in der Out-
line View angezeigt.
쐌 Nach dem Starten von Eclipse wird die Outline View eines Texteditors nicht angezeigt,
wenn der Texteditor beim Starten im Vordergrund liegt.

9.3.5 Ein Extension Point wird geboren


Wir wollen zunächst das zweite Problem lösen. Der Extension Point org.eclipse.ui.startup
ist also doch nicht ganz der richtige für uns. Er wird zu spät aufgerufen. Wir brauchen einen
Extension Point, der aufgerufen wird, noch bevor die Klasse ContentOutline die Outline
View aufbaut und getAdapter an der Klasse TextEditor aufruft.
Dazu wollen wir einen neuen Extension Point erzeugen. Wir werden dafür Änderungen an
dem Plug-In org.eclipse.ui.views vornehmen und ihm einen neuen Extension Point spen-
dieren. Auch wenn das nicht die Mutter aller guten Lösungen für unser Problem ist, werden
wir dabei doch viel über die Erstellung eines Extension Point lernen.
Die Klasse ContentOutline muss – noch bevor sie zum ersten Mal getAdapter aufruft
– allen Plug-Ins die Möglichkeit geben, sich zu initialisieren, z.B. um eine eigene Adapter

441
eclipse_v01.book Seite 442 Montag, 30. Januar 2006 12:02 12

Eine Outline View für den Texteditor

Factory beim Adapter Manager anzumelden. Doch woher weiß die Klasse ContentOut-
line, welches Plug-In aufgerufen werden möchte? Hier bietet sich ein Extension Point an:
Alle Plug-Ins, die aufgerufen werden wollen, deklarieren eine Extension für den Extension
Point und ContentOutline kann diese Extensions abfragen. Wir wollen Schritt für
Schritt vorgehen:
Zunächst importieren wir das Plug-In org.eclipse.ui.views als Source Project1 (im Kontext-
menü des Plug-Ins in der Plug-Ins View: IMPORT | AS SOURCE PROJECT)2.

9.3.6 Schritt 1: Definition des Interface für den Extension Point


Über einen Extension Point und eine in ihn eingehängte Extension kann ein Plug-In A, das
den Extension Point definiert, mit einem Plug-In B, das eine Extension für den Extension
Point deklariert, kommunizieren. Dazu übergibt Plug-In B in der Extension eine Klasse, die
ein bestimmtes Interface implementiert. Plug-In A kann nun diese Klasse instanziieren und
an ihr Methoden des Interface aufrufen.
Für diesen Kommunikationsmechanismus muss zunächst das Interface definiert werden. In
unserem Fall soll über Methoden des Interface die neue Outline View initialisiert werden,
d.h. die Adapter Factory beim Adapter-Manager angemeldet werden. Wir definieren das In-
terface org.eclipse.ui.views.contentoutline.IOutlineView wie folgt3:

package org.eclipse.ui.views.contentoutline;
public interface IOutlineView {
public void initOutlineView();
}

Dazu legen wir das Interface IOutlineView über NEW | INTERFACE im Package org.eclipse.
ui.views.contentoutline des Projekts org.eclipse.ui.views an.

9.3.7 Schritt 2: Definition des Extension Point


Nun müssen wir uns überlegen, wie eine Extension für unseren neuen Extension Point aus-
sehen soll. Als einzige Information, die eine Extension tragen muss, genügt die Klasse, die
das Interface IOutlineView implementiert. Wenn der neue Extension Point org.eclipse.
ui.views.outlineview heißt, könnte eine Extension etwas so aussehen4:

1. Falls das Binary Project org.eclipse.ui.views noch vorhanden sein sollte, so löschen wir es (im Kontextmenü
des Projekts im Package Explorer: DELETE | ALSO DELETE CONTENTS UNDER...).
2. Hin und wieder kommt Eclipse bei dieser Löschen-und-dann-gleich-wieder-importieren-Aktion durcheinan-
der. Nach dem Löschen des Plug-In-Projekts org.eclipse.ui.views kann das Plug-In in der Plug-Ins View als out
of sync gekennzeichnet sein. Gegebenenfalls kann es dann nicht importiert werden. Dann sollte man Eclipse
einmal schließen und wieder öffnen.
3. Das Interface befindet sich auf der CD im Verzeichnis Kap_9/ui.views/src-views/org/eclipse/ui/views/content-
outline.
4. Die Backslashes (\) sollen anzeigen, dass es in der nächsten Zeile weitergeht. Sie dürfen nicht eingegeben wer-
den.

442
eclipse_v01.book Seite 443 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

<extension

id="com.entwickler.eclipsebuch.javapropertiesoutline.outlineview"
name="outlineview"
point="org.eclipse.ui.views.outlineview">
<outlineview
class="com.entwickler.eclipsebuch.\
javapropertiesoutline.JPOOutlineView"/>
</extension>

Der Extension Point definiert das Format der Extensions über eine XML-Schema-Defini-
tion. In unserem Fall sieht sie ausschnittsweise so aus1:

<element name="extension">
<complexType>
<sequence>
<element
ref="outlineview"
minOccurs="1"
maxOccurs="unbounded"/>
</sequence>
<attribute
name="point"
type="string"
use="required">
</attribute>
<attribute
name="id"
type="string">
</attribute>
<attribute
name="name"
type="string">
</attribute>
</complexType>
</element>

<element
name="outlineview">
<complexType>
<attribute
name="class"
type="string"
use="required">

1. Dies ist ein Ausschnitt aus der XML-Schema-Datei outlineview.exsd, der mit der im Folgenden beschriebenen
Erstellung des neuen Extension Point entstehen wird.

443
eclipse_v01.book Seite 444 Montag, 30. Januar 2006 12:02 12

Eine Outline View für den Texteditor

<annotation>
<appInfo>
<meta.attribute
kind="java"/>
</appInfo>
</annotation>
</attribute>
..</complexType>
</element>

Doch all das müssen wir nicht per Hand eingeben. Vielmehr stellt uns das PDE hier einmal
mehr mächtige Hilfsmittel zur Verfügung. Zunächst öffnen wir das Plug-In Manifest von
org.eclipse.ui.views durch einen Doppelklick auf die Datei plugin.xml1. Auf der Seite EX-
TENSION POINTS betätigen wir den Button ADD.... Nun öffnet sich ein Dialog, in dem wir ID
und Namen des Extension Point eingeben (siehe Abb. 9.12).

Abb. 9.12: Dialog zur Anlage des neuen Extension Point

Nachdem wir den Button FINISH betätigt haben, öffnet sich der XML-Schema-Editor2, mit
dem wir die XML-Schema-Definition für den Extension Point einfach erstellen können.
Das oberste Element extension ist bereits definiert. Über NEW ELEMENT erzeugen wir ein
neues Element und benennen es in der Properties View in outlineview um (siehe Abb.
9.13).

1. Falls sich nicht der Plug-In Manifest Editor öffnet, müssen wir plugin.xml per OPEN WITH | PLUG-IN MANIFEST
EDITOR öffnen.
2. Die XML-Schema-Datei outlineview.exsd zu dem Extension Point org.eclipse.ui.views.outlineview befindet
sich auf der CD im Verzeichnis Kap_9/ui.views/org.eclipse.ui.views/schema.
Das veränderte Plug-In Manifest befindet sich auf der CD in der Datei Kap_9/ui.views/org.eclipse.ui.views/
plugin.xml.

444
eclipse_v01.book Seite 445 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

Abb. 9.13: Anlegen des Elements outlineview im XML-Schema-Editor

Über NEW ATTRIBUTE erzeugen wir ein neues Attribut für das Element outlineview. In der
Properties View benennen wir es in class um und wählen für die Property Kind den Wert
java aus1. Damit wird festgelegt, dass dieses Attribut eine Java-Klasse enthält. Bei der De-
klaration einer Extension für diesen Extension Point kann dann später dieses Attribut über
einen Class Browser gefüllt werden.
Nun sind die Elemente extension und outlineview definiert. Jetzt müssen wir noch
festlegen, dass das Element outlineview ein bis mehrmals innerhalb des Elements
extension auftreten darf. Dazu wählen wir zunächst das Element extension an. Auf
der rechten Seite (ELEMENT GRAMMAR) erscheint nun Sequence. Im Kontextmenü von
Sequence wählen wir NEW | REFERENCE | OUTLINEVIEW. Nun erscheint das Unterelement
outlineview von Sequence. Wir wählen es an. Über die Property maxOccurs können
wir festlegen, dass das Element outlineview mehrfach auftreten darf. Wir tragen un-
bounded ein (siehe Abb. 9.14).

1. Das PDE bietet hier eine Combo Box mit den möglichen Werten string, java und resource an.

445
eclipse_v01.book Seite 446 Montag, 30. Januar 2006 12:02 12

Eine Outline View für den Texteditor

Abb. 9.14: Festlegen der Sequence im XML-Schema-Editor

Möchte man, dass der Extension Point auch von anderen benutzt wird, so sollte man ihn
dokumentieren. Auf der Seite DOCUMENTATION ist dafür ausreichend Gelegenheit. Hat man
dies getan, so entsteht der folgende Code in der XML-Schema-Datei outlineview.exsd:

<?xml version='1.0' encoding='UTF-8'?>


<!-- Schema file written by PDE -->
<schema targetNamespace="org.eclipse.ui.views">
<annotation>
<appInfo>
<meta.schema
plugin="org.eclipse.ui.views"
id="outlineview"
name="Outline View"/>
</appInfo>
<documentation>
A plug-in implementing an extension for this extension point
can provide a class implementing the interface IOutlineView. The
method initOutlineView is called before the outline view is built.
So the plug-in can register adapter factories through the adapter
manager.
</documentation>
</annotation>

446
eclipse_v01.book Seite 447 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

<element name="extension">
(...)
</element>

<element name="outlineview">
(...)
</element>

<annotation>
<appInfo>
<meta.section type="since"/>
</appInfo>
<documentation>
This Extension Point is only in a patch version of
org.eclipse.ui.views
</documentation>
</annotation>

<annotation>
<appInfo>
<meta.section type="examples"/>
</appInfo>
<documentation>
&lt;pre&gt;
&lt;extension
id=&quot;com.entwickler.javapropertiesoutline.startup&quot;
name=&quot;early startup of the plug-in&quot;
point=&quot;org.eclipse.ui.startup&quot;&gt;
&lt;startup

class=&quot;com.entwickler.javapropertiesoutline.JPOEarlyStartup&
quot;&gt;
&lt;/startup&gt;
&lt;/extension&gt;
&lt;/pre&gt;
</documentation>
</annotation>

<annotation>
<appInfo>
<meta.section type="apiInfo"/>
</appInfo>
<documentation>
&lt;br&gt;package org.eclipse.ui.views.contentoutline;
&lt;br&gt;

447
eclipse_v01.book Seite 448 Montag, 30. Januar 2006 12:02 12

Eine Outline View für den Texteditor

public interface IOutlineView { &lt;br&gt;


public void initOutlineView(); &lt;br&gt;
}
</documentation>
</annotation>
(...)
</schema>

Wählt man auf der Seite EXTENSION POINTS im Plug-In Manifest Editor im Kontextmenü des
Extension Point outlineview den Menüpunkt SHOW DESCRIPTION, so wird die Beschreibung
des Extension Point wie in Abb. 9.15 dargestellt.

Abb. 9.15: Beschreibung des Extension Point outlineview

9.3.8 Schritt 3: Implementierung des Extension Point


In der Implementierung des Extension Point werden zunächst alle Extensions für diesen
Extension Point ermittelt. Hierfür steht die Plug-In-Registry (org.eclipse.core.run-
time.IExtensionRegistry) zur Verfügung. An ihr kann man nun die Elemente aller
Extensions für diesen Extension Point erfragen (org.eclipse.core.runtime.ICon-
figurationElement). Aus diesen Elementen wird das Attribut class ausgelesen und
die in ihm enthaltene Klasse wird instanziiert (IConfigurationElement.createExe-
cutableExtension("class")). Falls das instanziierte Objekt eine Instanz von IOut-
lineView ist, wird an ihm die Methode initOutlineView aufgerufen. Falls das Objekt
keine Instanz von IOutlineView ist, wird eine Warnung ausgegeben, die in der Error Log
View erscheint. Ebenso werden Exceptions in der Error Log View ausgegeben. Wir fügen

448
eclipse_v01.book Seite 449 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

die Implementierung des Extension Point in den parameterlosen Konstruktor von Con-
tentOutline ein1:

/**
* Creates a content outline view with no content outline pages.
*/
public ContentOutline() {
super();
try {
IExtensionRegistry reg = Platform.getExtensionRegistry();
IConfigurationElement[] elems =
reg.getConfigurationElementsFor(
"org.eclipse.ui.views.outlineview");
for (int i = 0; i < elems.length; i++) {
IConfigurationElement elem = elems[i];
if (elem.getName().equals("outlineview")) {
Object outlineview =
elem.createExecutableExtension("class");
if (outlineview instanceof IOutlineView) {
((IOutlineView) outlineview)
.initOutlineView();
} else {
ViewsPlugin.getDefault().getLog().log(
new Status(
IStatus.WARNING,
ViewsPlugin.PLUGIN_ID,
-1,
"The class "
+ elem.getAttribute("class")
+ " does not implement IOutlineView",
null));
}
}
}
} catch (CoreException e) {
ViewsPlugin.getDefault().getLog().log(
new Status(
IStatus.ERROR,
ViewsPlugin.PLUGIN_ID,
-1,
"Exception while initialising outline views",
e));
}
}

1. Der nachfolgende Code befindet sich auf der CD in dem Verzeichnis Kap_9/ui.views/
ContentOutline_constructor.txt.

449
eclipse_v01.book Seite 450 Montag, 30. Januar 2006 12:02 12

Eine Outline View für den Texteditor

Nachem wir diesen Code eingefügt haben, müssen wir über SOURCE | ORGANIZE IMPORTS die
nötigen import-Anweiseungen generieren1.

9.3.9 Eine Extension für den neuen Extension Point


Für eine Extension für den Extension Point org.eclipse.ui.views.outlineview legen wir zu-
nächst im Package com.entwickler.eclipsebuch.javapropertiesoutline des
gleichnamigen Plug-Ins die Klasse JPOOutlineView an, die das Interface IOutline-
View implementiert2. In der Methode initOutlineView wird dann die JPOAdapter-
Factory beim Adapter Manager angemeldet3:

package com.entwickler.eclipsebuch.javapropertiesoutline;

import org.eclipse.core.runtime.Platform;
import org.eclipse.ui.editors.text.TextEditor;
import org.eclipse.ui.views.contentoutline.IOutlineView;

public class JPOOutlineView implements IOutlineView {


public void initOutlineView() {
Platform.getAdapterManager().registerAdapters(
new JPOAdapterFactory(),
TextEditor.class);
}
}

Für die Extension gehen wir zur Seite EXTENSIONS im Plug-In Manifest Editor von com.ent-
wickler.javapropertiesoutline. Über den Button ADD... erhalten wir eine Auswahl aller Ex-
tension Points. Hier sehen wir auch unseren eben erst erschaffenen Extension Point (siehe
Abb. 9.16)4.
Nach dem FINISH können wir im Kontextmenü der neuen Extension mit NEW | OUTLINEVIEW
das Element outlineview anlegen und im Eingabefeld class auf der rechten Seite die
Klasse JPOOutlineView eintragen (siehe Abb. 9.17)5.

1. Bei der Klasse Platform öffnet sich eine Auswahl. Wir benötigen die Klasse
org.eclipse.core.runtime.Platform.
2. Diese zweite Version des Plug-Ins com.entwickler.eclipsebuch.javapropertiesoutline findet man auf der CD im
Verzeichnis Kap_9/javapropertiesoutline/v2.
3. Das Anmelden der JPOAdapterFactory in der Methode start der Plug-In-Klasse Javaproperties-
outlinePlugin können wir wieder entfernen.
4. Vorher löschen wir die Extension für den Extension Point org.eclipse.ui.startup.
5. Die vorhandene Extension für den Extension Point org.eclipse.ui.startup können wir über DELETE im Kontext-
menü wieder löschen.

450
eclipse_v01.book Seite 451 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

Abb. 9.16: Der neue Extension Point org.eclipse.ui.views.outlineview

Abb. 9.17: Anlegen des Elements outlineview

Nun setzen wir einen Breakpoint in der Methode initOutlineView von JPOOutline-
View und starten die Ecipse Application im Debug-Modus. Und siehe da, nun wird zuerst
die JPOAdapterFactory beim Adapter Manager angemeldet (in JPOOutlineView.
initOutlineView) und dann erst wird getAdapter an der Klasse TextEditor aufge-
rufen (in ContentOutline.doCreatePage)1. Und so sehen wir auch die Outline View
für den Texteditor von Anfang an in ihrer vollen Pracht (wie in Abb. 9.10).

1. Falls der Breakpoint in der Methode start der Plug-In-Klasse JavapropertiesoutlinePlugin noch
existiert, so wird zunächt an diesem angehalten. Schließlich ist die Methode start der Plug-In-Klasse die
erste Methode, die in einem Plug-In aufgerufen wird. Wir hätten die Anmeldung der JPOAdapterFactory
also auch hier lassen können.

451
eclipse_v01.book Seite 452 Montag, 30. Januar 2006 12:02 12

Der eigene Editor

9.4 Der eigene Editor


Damit haben wir das zweite der oben genannten Probleme gelöst. Doch das erste der beiden
ist wichtiger: Die Outline View wird nach Änderungen im Texteditor erst aktualisiert, wenn
man den Text Editor öffnet und wieder schließt. Zur Lösung dieses Problems wollen wir ei-
nen anderen Weg beschreiten1.
Da der Aufbau der Outline View aufwändig ist, wollen wir sie nur dann neu aufbauen, wenn
die Änderungen im Texteditor abgespeichert werden. Doch wir werden nicht weiter an vor-
handenen Plug-Ins Änderungen vornehmen. Denn dies soll immer nur der letzte aller mög-
lichen Wege sein. Für die Erweiterung vorhandener Plug-Ins sind Extension Points und Ex-
tensions gedacht. Dabei sollte man auch so lange bleiben, wie es irgend geht. Nur wenn
man an einer Stelle erweitern will, wo es keinen oder keinen passenden Extension Point
gibt, kann man sich mit dem Gedanken befassen, ein vorhandenes Plug-In zu ändern. Dabei
muss man sich aber stets gewahr sein, auf was man sich einlässt:
쐌 Man muss die Änderungen bei jedem neuen Release von Eclipse neu durchführen.
쐌 Die Änderungen basieren in der Regel nicht auf einem veröffentlichten API, so dass
sich die Änderung vielleicht schon beim nächsten Release nicht mehr durchführen las-
sen.
Aus diesen Gründen sollte man erst dann ein vorhandenes Plug-In ändern, wenn der Welt-
frieden bedroht wäre oder ähnlich schlimme Dinge passierten, täte man es nicht.
Da der Weltfrieden ohne eine Outline View für den Texteditor nicht ernsthaft auf dem Spiel
steht, werden wir nun einen anderen Weg beschreiten: Wir werden unseren eigenen Java
Properties Editor erstellen und diesem eine Java Properties Outline View verpassen.

9.4.1 Nichts einfacher als ein eigener Texteditor


Ein Editor ist ein recht kompliziertes Gebilde. Er muss mit einem zugrunde liegenden Do-
kument kommunizieren und auf einige Benutzeraktionen wie SAVE und SAVE AS reagieren.
Auch kann jemand das zugrunde liegende Dokument außerhalb des Editors geändert haben.
Dies muss der Editor merken und entsprechend reagieren. Doch das wäre alles wesentlich
schlimmer, gäbe es nicht einen fix und fertigen Texteditor, den man per Ableitung für eige-
ne Zwecke anpassen kann: org.eclipse.ui.editors.text.TextEditor
Bevor wir uns an den eigentlichen Editor machen, wollen wir die oben erstellte Outline
View im Plug-In com.entwickler.eclipsebuch.javapropertiesoutline auslagern und anderen
Plug-Ins zur Verfügung stellen. Dazu erzeugen wir ein allgemeines Control, das man mit ei-
nem Tree Viewer (TreeViewer) und einem Dokument (IDocument) initialisiert. Der Tree
Viewer wird dann mit den Java Properties aus dem Dokument gefüllt. Wir nennen dieses
neue Control Java Properties Tree (JavaPropertiesTree). Über die Methode parse-
Document kann der Java Properties Tree stets mit einem Dokument neu gefüllt werden.

1. Wobei wir auch das erste Problem noch einmal lösen.

452
eclipse_v01.book Seite 453 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

Wir erzeugen im Package com.entwickler.eclipsebuch.controls des gleichnamigen Plug-Ins


eine Klasse mit Namen JavaPropertiesTree1:

package com.entwickler.eclipsebuch.controls;

import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.viewers.TreeViewer;

public class JavaPropertiesTree {


private TreeMap model;
private TreeViewer treeviewer;

public JavaPropertiesTree(
TreeViewer treeviewer,
IDocument document) {
this.treeviewer = treeviewer;
treeviewer.setContentProvider(new TreeContentProvider());
parseDocument(document);
}

private Map insertInLayer(String partOfName, Map layer) {


Map subLayer;
if (layer.containsKey(partOfName)) {
TreeNode node = (TreeNode) layer.get(partOfName);
subLayer = (Map) node.getChildren();
} else {
subLayer = new TreeMap();
TreeNode node = new TreeNode(partOfName, subLayer);
layer.put(partOfName, node);
}
return subLayer;
}

1. Die neue Version des Plug-Ins com.entwickler.eclipsebuch.controls findet man auf der CD im Verzeichnis
Kap_9/controls/v2. Die Projekte com.entwickler.eclipsebuch.javapropertiesoutline und org.eclipse.ui.views
können nun geschlossen oder gelöscht werden.

453
eclipse_v01.book Seite 454 Montag, 30. Januar 2006 12:02 12

Der eigene Editor

public void parseDocument(IDocument document) {


try {
int numOfLines = document.getNumberOfLines();
String pattern = " *((?:\\w|\\.)+) *=.*";
Pattern p = Pattern.compile(pattern);
model = new TreeMap();
for (int l = 0; l < numOfLines; l++) {
IRegion region = document.getLineInformation(l);
String line =
document.get(
region.getOffset(),
region.getLength());
Matcher m = p.matcher(line);
if (m.matches()) {
String property = m.group(1);
String[] propertyParts = property.split("\\.");
Map layer = model;
for(int i = 0; i < propertyParts.length; i++) {
layer = insertInLayer(propertyParts[i], layer);
}
}
}
treeviewer.setInput(model);
} catch (BadLocationException e) {
ControlsPlugin.getDefault().log(e);
}
}
}

Außerdem spendieren wir dem Plug-In com.entwickler.controls eine Plug-In-Klasse


(ControlsPlugin), die ein bequemes Logging von Exceptions zulässt (Methode log):

package com.entwickler.eclipsebuch.controls;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Plugin;
import org.eclipse.core.runtime.Status;

public class ControlsPlugin extends Plugin {


public final static String PLUGIN_ID =
"com.entwickler.eclipsebuch.controls";

private static ControlsPlugin plugin;

public ControlsPlugin() {
super();

454
eclipse_v01.book Seite 455 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

plugin = this;
}

public static ControlsPlugin getDefault() {


return plugin;
}

public void log(Throwable e) {


IStatus status =
new Status(IStatus.ERROR, PLUGIN_ID, -1, "Exception",e);
getLog().log(status);
}
}

Damit ist unser neuer Editor mitsamt Outline View ein Kinderspiel.
Wir legen über den Wizard ein leeres Plug-In-Projekt an, das wir com.entwickler.eclipse-
buch.javapropertieseditor nennen1. Dort legen wir im Source Folder ein Package mit Na-
men com.entwickler.eclipsebuch.javapropertieseditor an.
Als Erstes wollen mir mithilfe des eben erzeugten JavaPropertiesTree den Adapter für
IContentOutlinePage erstellen (JPOAdapter). Wir spendieren ihm eine Methode na-
mens update, die die Outline View komplett neu aufbaut. Damit auch andere die Möglich-
keit haben, ihn von außen zu erweitern, machen wir den JavaPropertiesTree über die
Methode getJavaPropertiesTree öffentlich zugänglich:

package com.entwickler.eclipsebuch.javapropertieseditor;

import org.eclipse.jface.text.IDocument;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.views.contentoutline.ContentOutlinePage;
import com.entwickler.eclipsebuch.controls.JavaPropertiesTree;

public class JPOAdapter extends ContentOutlinePage {


private ITextEditor editor;
private JavaPropertiesTree jpt;

public JPOAdapter(ITextEditor editor) {


this.editor = editor;
}

1. Dabei erzeugen wir kein OSGi Bundle Manifest (Haken bei CREATE AN OSGI BUNDLE MANIFEST entfernen).
Für dieses Plug-In wird es mehrere Versionen geben. Die erste Version findet man auf der CD im Verzeichnis
Kap_9/javapropertieseditor/v1.

455
eclipse_v01.book Seite 456 Montag, 30. Januar 2006 12:02 12

Der eigene Editor

public JavaPropertiesTree getJavaPropertiesTree() {


return jpt;
}

private IDocument getDocument() {


IEditorInput input = editor.getEditorInput();
IDocumentProvider provider = editor.getDocumentProvider();
IDocument document = provider.getDocument(input);
return document;
}

public void createControl(Composite parent) {


super.createControl(parent);
jpt = new JavaPropertiesTree(getTreeViewer(), getDocument());
}

public void update() {


jpt.parseDocument(getDocument());
}
}

Wie oben schon angekündigt leiten wir den Editor von TextEditor ab. Dabei passen wir
zunächst lediglich zwei Methoden an:
쐌 getAdapter, damit die Anfrage nach einer IContentOutlinePage den JPOAdap-
ter zurück gibt
쐌 doSave, damit nach dem Abspeichern die Outline View aktualisiert wird

package com.entwickler.eclipsebuch.javapropertieseditor;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.ui.editors.text.TextEditor;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;

public class JavaPropertiesEditor extends TextEditor {


private JPOAdapter jpoadapter;

public JavaPropertiesEditor() {
super();
jpoadapter = new JPOAdapter(this);
}

public Object getAdapter(Class adapter) {


if (IContentOutlinePage.class.equals(adapter)) {
return jpoadapter;
}

456
eclipse_v01.book Seite 457 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

return super.getAdapter(adapter);
}

public void doSave(IProgressMonitor monitor) {


super.doSave(monitor);
jpoadapter.update();
}
}

Nun müssen wir Eclipse beibringen, dass es einen neuen Editor gibt. Dafür gibt es den Ex-
tension Point org.eclipse.ui.editors. In einer Extension für diesen Extension Point muss
man die Klasse, die den neuen Editor implementiert und ein Icon für diesen Editor angeben.
Nun brauchen wir noch ein passendes Icon. Dafür suchen wir in allen Plug-Ins, die auf .ui
enden. Die besitzen in der Regel ein Unterverzeichnis mit Namen icon, in dem man jede
Menge GIF-Dateien findet1. Nach einer ersten Suche passt folgendes Icon:
Wir finden es unter dem Namen var_cntnt_prvdr.gif im Verzeichnis icon/full/elc16 des
Plug-In org.eclipse.debug.ui. Wir kopieren es in das Unterverzeichnis icon von com.ent-
wickler.eclipsebuch.javapropertieseditor2:

<extension
point="org.eclipse.ui.editors">
<editor
name="Java Properties Editor"
extensions="properties"
icon="icons/var_cntnt_prvdr.gif"
contributorClass="org.eclipse.ui.editors.text.\
TextEditorActionContributor"
class="com.entwickler.eclipsebuch.javapropertieseditor.\
JavaPropertiesEditor"
id="com.entwickler.eclipsebuch.javapropertieseditor.editor1">
</editor>
</extension>

Standardmäßig sind alle Dateien mit der Endung „properties“ mit dem neuen Editor ver-
knüpft (siehe Attribut extensions des Elements editor).
Ein paar Worte sollten wir noch zu dem Attribut contributorClass verlieren: Alle
Buttons aus der Toolbar, alle Menüpunkte aus Menü und Kontextmenü sowie die einzelnen
Anzeigen in der Status Line sind nicht unmittelbar in der Klasse TextEditor implemen-
tiert. Vielmehr stecken sie in so genannten Actions. Was Actions sind, werden wir in Kürze
erfahren. In der Extension des Extension Point org.eclipse.ui.editors kann im Attribut con-

1. Dazu holt man sich am besten das Plug-In über IMPORT | AS BINARY PROJECT in den Workspace.
2. Wie man sieht, war der Autor hier faul und hat sich die Eingabe von ID und Name der Extension gespart.
Die Backslashes (\) sollen anzeigen, dass es in der nächsten Zeile weitergeht. Sie dürfen nicht eingegeben
werden.

457
eclipse_v01.book Seite 458 Montag, 30. Januar 2006 12:02 12

Der eigene Editor

tributorClass eine Klasse angegeben werden, die wiederum Menü, Kontextmenü usw.
mit den benötigten Actions füllt. Wir nehmen hierfür dieselbe Klasse, die auch der Text-
editor verwendet. Dadurch erhalten wir alle Menüpunkte, die auch der Texteditor verwen-
det.
Nun starten wir die Eclipse Application1 und können die Datei test.properties mit dem neu-
en Editor öffnen (siehe Abb. 9.18)2.

Abb. 9.18: Öffnen von test.properties mit dem neuen Editor

Und wir sehen eine Outline View, die nach jedem Abspeichern des Dokuments aktualisiert
wird.

9.4.2 Wir erweitern den Editor


Wir sind unersättlich und spendieren unserem Edior mit zugeordneter Outline View noch
ein paar schmucke Funktionalitäten. So soll die entsprechende Zeile im Editor selektiert
werden, wenn man einen Zweig im Baum der Outline View selektiert und der Zweig nur
genau einer Property entspricht.
Der Java Properties Tree kann jedoch keine Zeile im Editor markieren. Er kennt keinen Edi-
tor. Aus diesem Grund werden wir ihm folgende Funktionalität spendieren: Ihm können
mehrere Objekte zugeordnet werden, die ein von uns noch zu entwerfendes Interface
(IJavaPropertiesSelector) implementieren. Sobald eine Zeile selektiert wird, ruft
der Java Properties Tree die Methode selectOneLine an diesen Objekten auf. So können
dann Zeilen im jeweiligen Editor selektiert werden.

1. Vorher löschen oder schließen wir das Plug-In-Projekt com.entwickler.eclipsebuch.javapropertiesoutline.


2. Es kann sein, dass neben der Datei test.properties nicht das Icon des Java Property Editor erscheint. Dann muss
man den Java Properties Editor über WINDOW | PREFERENCES... | GENERAL | EDITORS | FILE ASSOCIATIONS allen
Dateien mit der Endung .properties als Default zuordnen. Wenn man nun das Projekt schließt und wieder
öffnet, sollte das richtige Icon erscheinen.

458
eclipse_v01.book Seite 459 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

Wir erstellen das Interface IJavaPropertiesSelector im Plug-In com.entwickler.


eclipsebuch.controls1:

package com.entwickler.eclipsebuch.controls;

public interface IJavaPropertiesSelector {


public void selectOneLine(int linenumber);
}

Der Java Properties Tree muss sich nun zu allen Zweigen des Baums die zugehörigen Zei-
lennummern merken (in der Klasse LineNumbers). Markiert der Benutzer einen (und nur
einen) Zweig, dem wiederum genau eine Zeilennummer zugeordnet ist, so wird die Metho-
de selectOneLine aller zugeordneten Objekte vom Typ IJavaPropertiesSelector
mit dieser Zeilennummer als Parameter aufgerufen. Dazu wird dem Tree Viewer ein so ge-
nannter Selection Changed Listener (Interface: org.eclipse.jface.viewers.ISe-
lectionChangedListener) zugeordnet. Der Selection Changed Listener wird immer
dann aufgerufen, wenn der Benutzer eine oder mehrere Zeilen des Baums selektiert (Me-
thode selectionChanged):

package com.entwickler.eclipsebuch.controls;

import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TreeViewer;

public class JavaPropertiesTree {


private TreeMap model;
private TreeViewer treeviewer;
private Vector javaPropertiesSelectors;

private class LineNumbers {


private String name;
private Vector numbers;

1. Jetzt entsteht die dritte Version von com.entwickler.eclipsebuch.controls. Man findet sie auf der CD im Ver-
zeichnis Kap_9/controls/v3.

459
eclipse_v01.book Seite 460 Montag, 30. Januar 2006 12:02 12

Der eigene Editor

public LineNumbers(String name) {


this.name = name;
numbers = new Vector();
}

public void addNumber(int number) {


numbers.add(new Integer(number));
}

public Vector getNumbers() {


return numbers;
}

public String toString() {


return name;
}
}

private Map insertInLayer(String key, int line, Map layer) {


Map subLayer;
if (layer.containsKey(key)) {
TreeNode node = (TreeNode) layer.get(key);
LineNumbers ln = (LineNumbers) node.getValue();
ln.addNumber(line);
subLayer = (Map) node.getChildren();
} else {
subLayer = new TreeMap();
LineNumbers ln = new LineNumbers(key);
ln.addNumber(line);
TreeNode node = new TreeNode(ln, subLayer);
layer.put(key, node);
}
return subLayer;
}

public void parseDocument(IDocument document) {


try {
int numOfLines = document.getNumberOfLines();
String pattern = " *((?:\\w|\\.)+) *=.*";
Pattern p = Pattern.compile(pattern);
model = new TreeMap();
for (int l = 0; l < numOfLines; l++) {
IRegion region = document.getLineInformation(l);
String line =
document.get(region.getOffset(), region.getLength());
Matcher m = p.matcher(line);

460
eclipse_v01.book Seite 461 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

if (m.matches()) {
String property = m.group(1);
String[] propertyParts = property.split("\\.");
Map layer = model;
for(int i = 0; i < propertyParts.length; i++) {
layer = insertInLayer(propertyParts[i], l, layer);
}
}
}
treeviewer.setInput(model);
} catch (BadLocationException e) {
ControlsPlugin.getDefault().log(e);
}
}

private class TreeSelectionChangedListener


implements ISelectionChangedListener {
public void selectionChanged(SelectionChangedEvent event) {
if (javaPropertiesSelectors.size() == 0) {
return;
}
IStructuredSelection selection =
(IStructuredSelection) event.getSelection();
if (selection.size() != 1) {
return;
}
Iterator selIt = selection.iterator();
while (selIt.hasNext()) {
TreeNode node = (TreeNode) selIt.next();
LineNumbers ln = (LineNumbers) node.getValue();
Vector numbers = ln.getNumbers();
if (numbers.size() != 1) {
return;
}
int num = ((Integer) numbers.elementAt(0)).intValue();
Iterator jpsIt = javaPropertiesSelectors.iterator();
while (jpsIt.hasNext()) {
((IJavaPropertiesSelector) jpsIt.next()).
selectOneLine(num);
}
}
}
}

public void addJavaPropertiesSelector(


IJavaPropertiesSelector sel) {
javaPropertiesSelectors.add(sel);
}

461
eclipse_v01.book Seite 462 Montag, 30. Januar 2006 12:02 12

Der eigene Editor

public void removeJavaPropertiesSelector(


IJavaPropertiesSelector sel) {
javaPropertiesSelectors.remove(sel);
}

public JavaPropertiesTree(
TreeViewer treeviewer,
IDocument document) {
this.treeviewer = treeviewer;
javaPropertiesSelectors = new Vector();
treeviewer.addSelectionChangedListener(
new TreeSelectionChangedListener());
treeviewer.setContentProvider(new TreeContentProvider());
parseDocument(document);
}
}

Die Verbindung zwischen dem Java Properties Tree und dem Editor stellt die Klasse JPO-
Adapter her. Sie erzeugt einen IJavaPropertiesSelector (Klasse JPOSelector)
und ordnet ihn dem JavaPropertiesTree zu. Der IJavaPropertiesSelector mar-
kiert dann die entsprechende Zeile im Editor1:

package com.entwickler.eclipsebuch.javapropertieseditor;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.views.contentoutline.ContentOutlinePage;
import com.entwickler.controls.IJavaPropertiesSelector;
import com.entwickler.controls.JavaPropertiesTree;

public class JPOAdapter extends ContentOutlinePage {


private ITextEditor editor;
private JavaPropertiesTree jpt;

...

1. Ab hier beginnt die zweite Version des Plug-Ins com.entwickler.eclipsebuch.javapropertieseditor. Man findet
es auf der CD in dem Verzeichnis Kap_9/javapropertieseditor/v2.

462
eclipse_v01.book Seite 463 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

private class JPOSelector implements IJavaPropertiesSelector {


public void selectOneLine(int linenumber) {
try {
IDocument document = getDocument();
IRegion region = document.getLineInformation(linenumber);
editor.selectAndReveal(
region.getOffset(),
region.getLength());
editor.setHighlightRange(
region.getOffset(),
region.getLength(),
true);
} catch (BadLocationException e) {
JPEPlugin.getDefault().log(e);
}
}
}

public void createControl(Composite parent) {


super.createControl(parent);
jpt = new JavaPropertiesTree(getTreeViewer(), getDocument());
jpt.addJavaPropertiesSelector(new JPOSelector());
}

public void update() {


jpt.parseDocument(getDocument());
}
}

Um Fehler bequem ausgeben zu können, haben wir auch dem Plug-In com.entwickler.
eclipsebuch.javapropertieseditor eine Plug-In-Klasse spendiert (JPEPlugin)1:

package com.entwickler.eclipsebuch.javapropertieseditor;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.ui.plugin.AbstractUIPlugin;

public class JPEPlugin extends AbstractUIPlugin {


public static final String PLUGIN_ID =
"com.entwickler.eclipsebuch.javapropertieseditor";
private static JPEPlugin plugin;

1. Diese muss als Plug-In-Klasse im Plug-In Manifest eingetragen werden (Feld CLASS auf der Seite OVERVIEW im
Plug-In Manifest Editor).

463
eclipse_v01.book Seite 464 Montag, 30. Januar 2006 12:02 12

Und Action ...: Actions in der Eclipse Workbench (Teil 1)

public JPEPlugin() {
super();
plugin = this;
}

public static JPEPlugin getDefault() {


return plugin;
}

public void log(Throwable e) {


IStatus status =
new Status(IStatus.ERROR, PLUGIN_ID, -1, "Exception", e);
getLog().log(status);
}
}

Starten wir nun wieder die Eclipse Application, öffnen test.properties und markieren in der
Outline View einen Zweig, dem genau eine Zeile im Editor entspricht, so wird diese Zeile
markiert (siehe Abb. 9.19).

Abb. 9.19: Durch die Outline View wird eine Zeile im Editor markiert.

9.5 Und Action ...: Actions in der Eclipse Workbench (Teil 1)


Nun wollen wir unsere Outline View auch noch mit der umgekehrten Funktionalität aus-
statten:
쐌 Alle zu einer Zeile im Editor gehörenden Zweige im Baum der Outline View sollen
nach der aktuellen Position des Cursors im Editor selektiert werden können (siehe
Abb. 9.20).

464
eclipse_v01.book Seite 465 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

Abb. 9.20: Selektieren aller Zweige, die zu einer Zeile gehören

Dazu müssen wir den Java Properties Tree erweitern1: Wir speichern in einer java.
util.HashMap zu jeder Zeilennumer alle TreeNodes des Baums (im Attribut lineNum-
berMap der Klasse JavaPropertiesTree) ab, die zur Zeile im Java Properties Editor mit
dieser Zeilennummer gehören. Diese können dann anhand der Zeilennummer selektiert
werden. Dazu holt die Methode selectNodesFromLineNumber aus der HashMap die
TreeNodes, die zur aktuellen Zeile gehören:

Vector treeNodes = (Vector) lineNumberMap.get(lineNum);

Dann wird über diese TreeNodes iteriert. Für jeden TreeNode wird der Baum bis zu die-
sem TreeNode expandiert:

treeviewer.expandToLevel(it.next(), 1));

Zum Schluss werden alle Zweige selektiert:

StructuredSelection sel = new StructuredSelection(treeNodes);


treeviewer.setSelection(sel, true);

Der Aufbau der HashMap erfolgt in der Methode insertInLayer:

package com.entwickler.eclipsebuch.controls;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

1. Ab hier beginnt die vierte Version des Plug-Ins com.entwickler.eclipsebuch.controls. Man findet es auf der CD
im Verzeichnis Kap_9/controls/v4.

465
eclipse_v01.book Seite 466 Montag, 30. Januar 2006 12:02 12

Und Action ...: Actions in der Eclipse Workbench (Teil 1)

import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;

public class JavaPropertiesTree {


private TreeMap model;
private TreeViewer treeviewer;
private Vector javaPropertiesSelectors;
private HashMap lineNumberMap;

private class LineNumbers {


...
}

public TreeViewer getTreeviewer() {


return treeviewer;
}

private Map insertInLayer(String key, int line, Map layer) {


Map subLayer;
TreeNode node;
if (layer.containsKey(key)) {
node = (TreeNode) layer.get(key);
LineNumbers ln = (LineNumbers) node.getValue();
ln.addNumber(line);
subLayer = (Map) node.getChildren();
} else {
subLayer = new TreeMap();
LineNumbers ln = new LineNumbers(key);
ln.addNumber(line);
node = new TreeNode(ln, subLayer);
layer.put(key, node);
}
Vector treeNodes;
Integer lineNum = new Integer(line);
if (lineNumberMap.containsKey(lineNum)) {
treeNodes = (Vector) lineNumberMap.get(lineNum);

466
eclipse_v01.book Seite 467 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

} else {
treeNodes = new Vector();
lineNumberMap.put(lineNum, treeNodes);
}
treeNodes.add(node);
return subLayer;
}

public void selectNodesFromLineNumber(int line) {


Integer lineNum = new Integer(line);
if (!lineNumberMap.containsKey(lineNum)) {
return;
}
Vector treeNodes = (Vector) lineNumberMap.get(lineNum);
Iterator it = treeNodes.iterator();
while(it.hasNext()) {
treeviewer.expandToLevel(it.next(), 1);
}
StructuredSelection sel = new StructuredSelection(treeNodes);
treeviewer.setSelection(sel, true);
}

public void parseDocument(IDocument document) {


...
}

private class TreeSelectionChangedListener


implements ISelectionChangedListener {
...
}

...

public JavaPropertiesTree(
TreeViewer treeviewer,
IDocument document) {
this.treeviewer = treeviewer;
javaPropertiesSelectors = new Vector();
lineNumberMap = new HashMap();
treeviewer.addSelectionChangedListener(
new TreeSelectionChangedListener());
treeviewer.setContentProvider(new TreeContentProvider());
parseDocument(document);
}
}

467
eclipse_v01.book Seite 468 Montag, 30. Januar 2006 12:02 12

Und Action ...: Actions in der Eclipse Workbench (Teil 1)

Außerdem müssen wir den JavaPropertiesEditor dazu bringen, dass er uns Zeile und
Spalte der aktuellen Cursorposition preisgibt. Die Methode getCursorPosition der
Großvaterklasse org.eclipse.ui.texteditor.AbstractTextEditor gibt diese In-
formationen in einem String (z.B. „3 : 5“ für Zeile 3 und Spalte 5) zurück. Dieser String
muss entsprechend verarbeitet werden. Dabei soll die erste Zeile bzw. Spalte jeweils die
Position 0 erhalten1:

package com.entwickler.javapropertieseditor;

import java.util.StringTokenizer;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.ui.editors.text.TextEditor;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;

public class JavaPropertiesEditor extends TextEditor {

...

public int[] getLineAndColumn() {


String cursorPos = getCursorPosition();
StringTokenizer st = new StringTokenizer(cursorPos, " : ");
int[] pos = new int[2];
pos[0] = (new Integer(st.nextToken())).intValue() - 1;
pos[1] = (new Integer(st.nextToken())).intValue() - 1;
return pos;
}
}

Die Selektion der Zweige des Baums soll über eine Schaltfläche im Toolbar der Outline
View ausgelöst werden.
Doch bevor wir an dieser Stelle weitergehen, sollten wir den Aufbau der Eclipse-Oberflä-
che verstanden haben. Also machen wir einen kleinen ...

1. Ab hier beginnt die dritte Version des Plug-Ins com.entwickler.eclipsebuch.javapropertieseditor. Man findet
sie auf der CD in dem Verzeichnis Kap_9/javapropertieseditor/v3.

468
eclipse_v01.book Seite 469 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

9.6 ... Einschub: Aufbau der Eclipse-Oberfläche


Der Ausgangspunkt der Eclipse-Oberfläche ist die Workbench (siehe Abb. 9.21). Sie exis-
tiert pro Eclipse-Instanz genau ein Mal.
Jede Workbench besteht aus einem bis mehreren Workbench Windows. Wenn man Eclipse
öffnet, ist ein Workbench Window offen. Über WINDOW | NEW WINDOW kann man weitere
Workbench Windows öffnen. Jedes Workbench Window besitzt genau eine Workbench
Page1. Eine Workbench Page besteht aus mehreren Workbench Parts, die wiederum Editor
Parts oder View Parts sein können.

Abb. 9.21: Die Eclipse Workbench

Wo sich die View Parts innerhalb der Workbench Page befinden, bestimmt der Anwender.
Die Editor Parts befinden sich immer innerhalb der Editor Area. Sowohl Editor Part als
auch View Part besitzen jeweils eine Editor Site bzw. View Site. Über die Site erhält man
Zugriff auf die so genannten Action Bars. Action Bars sind der Menu Manager (für Menüs),
der Toolbar Manager (für Toolbar oder Coolbars) und der Status Line Manager (für die Sta-
tuszeile, siehe Abb. 9.21, 9.22, 9.23).

1. Die Ebene der Workbench Page erscheint angesichts der Tatsache, dass pro Workbench Window nur eine Page
existieren darf, nicht ganz einleuchtend. Allerdings gibt es diese Restriktion erst seit Eclipse 2.0.

469
eclipse_v01.book Seite 470 Montag, 30. Januar 2006 12:02 12

... Einschub: Aufbau der Eclipse-Oberfläche

Abb. 9.22: Menü und Toolbar eines View Parts

Abb. 9.23: Menü und Toolbar eines Editor Parts

Diese Struktur spiegelt sich im Modell der Interfaces der Eclipse-Oberfläche wider (siehe
Abb. 9.24).

470
eclipse_v01.book Seite 471 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

Abb. 9.24: Modell der Interfaces der Eclipse-Oberfläche (Ausschnitt)1

1. Aus Layoutgründen erscheinen hier abgeleitete Klassen über ihren Vorfahren.

471
eclipse_v01.book Seite 472 Montag, 30. Januar 2006 12:02 12

... Einschub: Aufbau der Eclipse-Oberfläche

Über das Interface IWorkbench erhält man Referenzen auf Workbench Windows. Außer-
dem können Perspektiven geöffnet werden und es liefert einige nützliche Services (siehe
Tabelle 9.1).

Tabelle 9.1: Interface IWorkbench (Auszug)

Methode Beschreibung
void Registriert einen Window Listener. Ein Window
addWindowListener(IWindow- Listener wird beim Öffnen, Schließen, Aktivie-
Listener listener) ren und Deaktivieren von Workbench Windows
aufgerufen.
void close() Schließt die Workbench.
IWorkbenchWindow getActive- Liefert das aktive Workbench Window.
WorkbenchWindow()

Display getDisplay() Liefert das SWT Display. Ein Display ist die Ver-
bindung zwischen SWT und dem darunter lie-
genden Betriebssystem. Es implementiert u.a.
die Event Loop.
IEditorRegistry getEditor- Liefert die Editor Registry, über die z.B. die Edi-
Registry() toren für einen bestimmten Dateinamen oder
einen bestimmten Inhaltstyp (IContentType)
gesucht werden können.
IElementFactory getElement- Liefert die Element Factory, die mit der ID fac-
Factory(String factoryId) toryId beim Extension Point org.eclipse.ui.
elementFactories registriert wurde. Über eine
Element Factory kann ein Objekt, das in einem
Zustand (IMemento) gespeichert wurde, wie-
derhergestellt werden. Klassen, die in einem
Zustand (IMemento) gespeichert und wieder
hergestellt werden können, sollten das Inter-
face IPersistableElement implementieren.
IPerspectiveRegistry Liefert die Perspective Registry, über die Per-
getPerspectiveRegistry() spektiven gesucht werden können und über die
einer Workbench eine Default-Perspektive
zugeordnet werden kann.
ISharedImages Liefert eine Sammlung von Bildern und Icons,
getSharedImages() die von allen Plug-Ins benutzt werden können.
IWorkbenchWindow[] Liefert ein Array aller Workbench Windows.
getWorkbenchWindows()

IWorkingSetManager getWor- Liefert den Working Set Manager, über den


kingSetManager() Working Sets erstellt und verwaltet werden kön-
nen.

472
eclipse_v01.book Seite 473 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

Tabelle 9.1: Interface IWorkbench (Auszug) (Forts.)

Methode Beschreibung
void removeWindowListe- Löscht einen über addWindowListener
ner(IWindowListener listener) registrierten Window Listener.
restart() Startet die Workbench neu.
boolean saveAllEditors Speichert alle nicht gespeicherten Editoren und
(boolean confirm) fragt vorher gegebenenfalls (confirm ==
true) beim Benutzer nach.

IWorkbenchPage showPerspec- Zeigt die Perspektive an, die mit der perspec-
tive(String perspectiveId, tiveId beim Extension Point org.eclipse.ui.
IWorkbenchWindow window) perspective registriert wurde.

Über das Interface IWorkbenchWindow kann die Workbench Page erfragt werden. Auch
sie liefert nützliche Services (siehe Tabelle 9.2).

Tabelle 9.2: Tabelle 9.2: Interface IWorkbenchWindow (Auszug)

Methode Beschreibung
void close() Schließt das Workbench Window.
IWorkbenchPage Liefert die aktive Workbench Page. Da ab
getActivePage() Eclipse 2.0 jedes Workbench Window nur eine
Page besitzen darf, wird damit die einzige Page
zurückgeliefert.
IPartService getPartService() Liefert den Part Service, über den Part Listener
(IPartListener) registriert werden können.
Ein Part Listener wird über das Öffnen, Schlie-
ßen, Aktivieren und Deaktivieren von Parts
(Editor Part oder View Part) informiert.
ISelectionService Liefert den Selection Service, über den Selec-
getSelectionService() tion Listener registriert werden können. Ein
Selection Listener wird informiert, wenn sich
die Selection in einem Part (Editor Part oder
View Part) ändert.
Shell getShell() Liefert die SWT Shell des Workbench Win-
dows.
IWorkbench getWorkbench() Liefert die Workbench, zu der das Workbench
Window gehört.

473
eclipse_v01.book Seite 474 Montag, 30. Januar 2006 12:02 12

... Einschub: Aufbau der Eclipse-Oberfläche

Das Interface IWorkbenchPage liefert Methoden zum Verwalten der enthaltenen Editors
und Views (siehe Tabelle 9.3).

Tabelle 9.3: Das Interface IWorkbenchPage (Auszug)

Methode Beschreibung
void activate(IWorkbenchPart Aktiviert ein Workbench Part (Editor Part
part) oder View Part).
void bringToTop(IWorkbenchPart Holt ein Workbench Part nach oben.
part)

boolean closeAllEditors(boolean Schließt alle Editors, die in der Workbench


save) Page enthalten sind.
boolean closeEditor(IEditorPart Schließt einen bestimmten Editor.
editor, boolean save)

IEditorPart findEditor(IEditor- Sucht den Editor, dem ein bestimmter Editor


Input input) Input zugeordnet ist.
IViewPart findView Sucht die View, die mit der viewId beim
(String viewId) Extension Point org.eclipse.ui.views regis-
triert wurde.
IEditorPart getActiveEditor() Liefert den aktiven Editor.
IEditorPart[] Liefert alle ungespeicherten Editors.
getDirtyEditors()

IEditorReference[] getEditor- Liefert Referenzen auf alle Editor Parts die-


References() ser Workbench Page.
INavigationHistory getNavi- Liefert die Navigation History, die alle zuletzt
gationHistory() besuchten Positionen in allen Editors spei-
chert.
IPerspectiveDescriptor get- Liefert eine Beschreibung (Perspective
Perspective() Descriptor) der aktuellen Perspektive. Über
IPerspectiveDescriptor kann u.a. die ID
der Perspektive erfragt werden.
IViewReference[] getView- Liefert Referenzen aller View Parts dieser
References() Workbench Page.
void hideView(IViewPart view) Versteckt eine bestimmte View.
boolean isEditorAreaVisible() Liefert zurück, ob die Editor Area, d.h. der
Bereich, in dem sich alle Editors befinden,
sichtbar ist.
boolean isPartVisible(IWork- Liefert zurück, ob ein Workbench Part sicht-
benchPart part) bar ist.

474
eclipse_v01.book Seite 475 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

Tabelle 9.3: Das Interface IWorkbenchPage (Auszug) (Forts.)

Methode Beschreibung
IEditorPart openEditor(IEditor- Öffnet den Editor, der mit der editorId
Input input, String editorId, beim Extension Point org.eclipse.ui.editors
boolean activate) registriert wurde, mit dem übergebenen Edi-
tor Input. Falls activate == true, wird der
Editor auch gleich aktiviert.
void resetPerspective() Setzt eine Perspektive auf die Einstellungen
beim letzten savePerspective() zurück.
boolean saveAllEditors() Speichert alle Editors.
boolean saveEditor(IEditorPart Speichert einen bestimmten Editor und fragt
editor, boolean confirm) gegebenenfalls (confirm == true) vorher
nach.
void savePerspective() Speichert die Einstellungen der aktuellen
Perspektive im zugehörigen Perspective
Descriptor.
void savePerspectiveAs(IPer- Speichert die Einstellungen der aktuellen
spectiveDescriptor perspective) Perspektive im übergebenen Perspective
Descriptor.
void setEditorAreaVisible(boo- Versteckt (showEditorArea == false)
lean showEditorArea) oder zeigt die Editor Area an (showEdi-
torArea == true).

void setPerspective(IPerspec- Setzt die aktuelle Perspektive auf die im


tiveDescriptor perspective) übergebenen Perspective Descriptor gespei-
cherten Einstellungen.
IViewPart showView(String Zeigt die View an, die mit der viewId beim
viewId) Extension Point org.eclipse.ui.views regis-
triert wurde.

Über die Interfaces IEditorPart und IViewPart kann man die dem Part zugeordnete
Site (IEditorSite bzw. IViewSite) auslesen (IEditorPart.getEditorSite()
oder IViewPart.getViewSite()). Die beiden Interfaces IEditorSite und IView-
Site sind vom Interface IWorkbenchPartSite abgeleitet, das Informationen über das
Plug-In liefern kann, in dem eine View oder ein Editor definiert sind (siehe Tabelle 9.4).

Tabelle 9.4: Das Interface IWorkbenchPartSite (Auszug)

Methode Beschreibung
String getId() Liefert die ID der View oder des Editors, wie sie in der
Extension, in der die View oder der Editor definiert sind,
angegeben sind.

475
eclipse_v01.book Seite 476 Montag, 30. Januar 2006 12:02 12

... Einschub: Aufbau der Eclipse-Oberfläche

Tabelle 9.4: Das Interface IWorkbenchPartSite (Auszug) (Forts.)

Methode Beschreibung
String getPluginId() Liefert die ID des Plug-In, in dem View oder Editor defi-
niert sind.

Die von IWorkbenchPartSite abgeleiteten Interfaces IEditorSite und IViewSite


liefern darüber hinaus mit getActionBars die Action Bars (IActionBars) der Site (sie-
he Tabelle 9.5).

Tabelle 9.5: Das Interface IActionBars (Auszug)

Methode Beschreibung
IMenuManager Liefert den Menu Manager, der für das Pull Down Menu
getMenuManager() zuständig ist. Bei View Parts ist das Menü in der Toolbar
angesiedelt (siehe Abb. 9.23). Bei Editor Parts ist das
Menü das Hauptmenü des Workbench Window (siehe
Abb. 9.21).
IStatusLineManager Liefert den Status Line Manger, der für die Statuszeile
getStatusLineManager() zuständig ist. Die Statuszeile ist bei View Part und Edi-
tor Part am unteren Rand des Workspace Windows
(siehe Abb. 9.21).
IToolBarManager Liefert den Toolbar Manager. Die Toolbar befindet sich
getToolBarManager() bei View Parts am oberen Rand der View (siehe Abb.
9.22). Bei Editor Parts ist die Toolbar gleich der Toolbar
des Workbench Windows. Die Toolbar des Workbench
Window ist eigentlich eine Coolbar. Eine Coolbar ist wie-
derum eine Toolbar, die aus mehreren Toolbars besteht,
die vom Anwender beliebig verschoben werden können
(siehe Abb. 9.23).

Die implementierenden Klassen sind meist in internen (*.internal.*) Packages zu finden


und gehören somit nicht zum offiziellen API von Eclipse bzw. des jeweiligen Plug-In (siehe
Tabelle 9.6).

Tabelle 9.6: Implementierende Klassen der Interfaces der Oberfläche (Beispiele)

Interface Implementierende Klassen


org.eclipse.ui.IWorkbench org.eclipse.ui.internal.
Workbench

org.eclipse.ui. org.eclipse.ui.internal.
IWorkbenchWindow WorkbenchWindow

org.eclipse.ui. org.eclipse.ui.internal.
IWorkbenchPage WorkbenchPage

476
eclipse_v01.book Seite 477 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

Tabelle 9.6: Implementierende Klassen der Interfaces der Oberfläche (Beispiele) (Forts.)

Interface Implementierende Klassen


org.eclipse.ui. org.eclipse.ui.internal.
IViewReference ViewReference

org.eclipse.ui. org.eclipse.ui.internal.
IEditorReference EditorReference

org.eclipse.ui.IViewPart org.eclipse.jdt.internal.ui.
packageview.
PackageExplorerPart (Package Explorer View)
org.eclipse.jdt.internal.ui.
typehierarchy.
TypeHierarchyViewPart (Type Hierarchy View)
org.eclipse.ui.views.markers.inter-
nal.ProblemView (Problems View)
org.eclipse.search.internal.
ui.SearchResultView (Search View)
...
org.eclipse.ui.IEditorPart org.eclipse.ui.editors.text.
TextEditor (Texteditor)
org.eclipse.pde.internal.
ui.editor.plugin.
ManifestEditor (Plug-In Manifest Editor)
org.eclipse.ant.internal.ui.
editor.AntEditor (Ant Editor)
...
org.eclipse.ui.IViewSite org.eclipse.ui.internal.
ViewSite

org.eclipse.ui.IEditorSite org.eclipse.ui.internal.
EditorSite

org.eclipse.ui.IActionBars org.eclipse.ui.internal.
ViewActionBars (für Views)
org.eclipse.ui.internal.
EditorActionBars (für Editors)

org.eclipse.jface.action. org.eclipse.ui.internal.
IMenuManager ViewPane$PaneMenuManager (wird bei Views
benutzt; innere Klasse von ViewPane)
org.eclipse.ui.internal.
EditorMenuManager (wird bei Editors benutzt)
org.eclipse.jface.action.
MenuManager (wird bei Views und Menus für
Untermenüs benutzt)

477
eclipse_v01.book Seite 478 Montag, 30. Januar 2006 12:02 12

... Einschub: Aufbau der Eclipse-Oberfläche

Tabelle 9.6: Implementierende Klassen der Interfaces der Oberfläche (Beispiele) (Forts.)

Interface Implementierende Klassen


org.eclipse.jface.action. org.eclipse.ui.internal.
IToolBarManager ViewPane$PaneToolBarManager (wird bei
Views benutzt; innere Klasse von ViewPane)
org.eclipse.jface.action.
ToolBarManager (wird bei Editors benutzt)

org.eclipse.jface.action. org.eclipse.jface.action.
IStatusLineManager SubStatusLineManager

Der Menu Manager, der Status Line Manager und der Toolbar Manager können als Contri-
bution Manager so genannte Contribution Items (Interface org.eclipse.jface.ac-
tion.IContributionItem) aufnehmen (siehe Abb. 9.25). Ein Contribution Item ist z.B.
die Klasse org.eclipse.jface.action.ActionContributionItem. Sie verweist
auf eine Action (Interface org.eclipse.jface.action.IAction).

Abb. 9.25: Die Contribution Items der Contribution Manager (Auszug)

Eine Action beschreibt Verhalten und Aussehen einer bestimmten Funktionalität. In einer
Toolbar erscheint eine Action als Button und in einem Menü als Menüpunkt1. Auch ein
Menu Manager ist ein Contribution Item. Dadurch werden Untermenüs modelliert. Des
Weiteren existieren noch Group Marker (org.eclipse.jface.action.GroupMarker)
und Separators (org.eclipse.jface.action.Separator). Mit Group Markers kön-
nen Contribution Items in Gruppen aufgeteilt werden, so dass z.B. ein Menüpunkt einer be-
stimmten Gruppe in einem Menü zugeordnet werden kann. Group Markers sind unsichtbar.
Separators sind spezielle Group Markers, die darüber hinaus eine sichtbare Repräsentation
besitzen. In einem Menü erscheinen sie als Trennlinie. Für die Statuszeile gibt es Contri-
bution Items vom Typ org.eclipse.ui.texteditor.StatusLineContribution-
Item. Sie erscheinen als Feld innerhalb der Statuszeile, in dem Text ausgegeben werden
kann.

1. Wir werden dies gleich an einem konkreten Beispiel sehen.

478
eclipse_v01.book Seite 479 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

Um den Aufbau der Oberfläche am lebenden Objekt betrachten zu können, wollen wir ein
kleines Plug-In schreiben, das die aktuelle Objektstruktur anzeigt. Als Nebeneffekt lernen
wir, wie man im Objektmodell der Oberfläche navigiert.
Das Plug-In soll eine neue View mit Namen WORKBENCH VIEW unter der neuen Kategorie
WORKBENCH anbieten. Die Workbench View soll in einem Tree Viewer alle Workbench
Windows der Workbench auflisten. Den Aufbau zeigt Abb. 9.26.

Abb. 9.26: Aufbau des Tree Viewers der Workbench View

Der Tree Viewer zeigt jeweils den Klassennamen und das Ergebnis der toString-Metho-
de des Objekts an. Wählt man im Tree Viewer einen View Part oder Editor Part, wird der Ti-
tel in der Statuszeile angezeigt. Wählt man eine Action, wird deren Name in der Statuszeile
angezeigt. Wählt man eine View Site oder Editor Site, werden die ID der View oder des
Editors (wie in der definierenden Extension angegeben) und die ID des Plug-In, in dem sich
diese Extension befindet, angezeigt.

479
eclipse_v01.book Seite 480 Montag, 30. Januar 2006 12:02 12

... Einschub: Aufbau der Eclipse-Oberfläche

Wir erzeugen ein leeres Plug-In-Projekt mit Namen com.entwickler.eclipsebuch.work-


benchview1. Das Plug-In Manifest, das eine Extension für den Extension Point org.eclipse.
ui.views definiert, sieht folgendermaßen aus2:

<?xml version="1.0" encoding="UTF-8"?>


<?eclipse version="3.0"?><plugin
id="com.entwickler.eclipsebuch.workbenchview"
name="Workbenchview Plug-In"
version="1.0.0"
provider-name="ENTWICKLER"
class="com.entwickler.eclipsebuch.workbenchview.\
WorkbenchViewPlugin">

<runtime>
<library name="workbenchview.jar">
<export name="*"/>
</library>
</runtime>

<requires>
<import plugin="org.eclipse.ui.workbench"/>
<import plugin="org.eclipse.jface"/>
<import plugin="org.eclipse.ui.views"/>
<import plugin="org.eclipse.ui"/>
<import plugin="org.eclipse.ui.workbench.texteditor"/>
<import plugin="org.eclipse.core.runtime"/>
</requires>

<extension
point="org.eclipse.ui.views">
<view
name="Workbench View"
icon="icons/tree_mode.gif"
category="com.entwickler.eclipsebuch.workbenchview.\
category.workbench"
class="com.entwickler.eclipsebuch.workbenchview.WorkbenchView"
id="com.entwickler.eclipsebuch.workbenchview.workbenchview">
</view>
<category
name="Workbench"

1. Dies ist die erste Version des Plug-Ins com.entwickler.eclipsebuch.workbenchview. Sie befindet sich auf der
CD im Verzeichnis Kap_9\workbenchview\v1.
2. Ein solches Plug-In Manifest entsteht nur, wenn kein OSGi Bundle Manifest erzeugt wird (Haken bei CREATE
AN OSGI BUNLDE MANIFEST entfernen).
Die Backslashes (\) sollen anzeigen, dass es in der nächsten Zeile weitergeht. Sie dürfen nicht eingegeben wer-
den.

480
eclipse_v01.book Seite 481 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

id="com.entwickler.eclipsebuch.workbenchview.category.\
workbench">
</category>
</extension>
</plugin>

Die Klasse WorkbenchView im Package com.entwickler.eclipsebuch.work-


benchview1 besitzt verschiedene innere Klassen:
쐌 WorkbenchTreeContentProvider
Content Provider des Tree Viewers. Ausgehend von der Workbench werden in der Me-
thode getElements zunächst alle Workbench Windows ermittelt. In der Methode
getChildren werden dann abhängig von der aktuellen Ebene im Tree Viewer die
Zweige des Baums erzeugt. Bekommt getChildren ein Workbench Window als Pa-
rameter übergeben, so werden die Pages zurückgegeben. Bekommt getChildren eine
Page als Parameter übergeben, so werden die View References (IViewReference)
und Editor References (IEditorReference) zurückgegeben usw. (siehe Abb. 9.24).
쐌 WorkbenchTreeLabelProvider
Label Provider des Tree Viewer. Für jedes vom Content Provider gelieferte Objekt (über
getElement oder getChildren) wird die Methode getText des Label Provider auf-
gerufen. Der Rückgabewert von getText wird dann im Tree Viewer angezeigt.
쐌 WorkbenchTreeSelectionChangedListener
Selection Changed Listener des Tree Viewer. Die Methode selectionChanged wird
immer dann aufgerufen, wenn der Anwender Zweige im Baum selektiert. Je nachdem,
was selektiert ist, werden zusätzliche Informationen in der Statuszeile angezeigt.
쐌 WorkbenchViewRefreshAction
Eine Action, die einen Neuaufbau des Tree Viewer auslöst. Sie erscheint als Button in
der Toolbar der View. Im Konstruktor der Action werden Titel, Tooltip und Icon gesetzt.
Sobald der Benutzer den Button der Action betätigt, wird die run-Methode von Work-
benchViewRefreshAction aufgerufen. In der Methode createPartControl von
WorkbenchView wird die Action als Button in der Toolbar der View eingebracht.
Die Methode createPartControl erzeugt den Tree Viewer und weist ihm Content Pro-
vider, Label Provider und Selection Changed Listener zu. Außerdem wird als Modell des
Tree Viewer die Workbench gesetzt:

treeViewer.setInput(PlatformUI.getWorkbench()

Danach wird die Refresh Action in die Toolbar gesetzt und die Statuszeile bekommt drei
Objekte vom Typ StatusLineContributionItem zugewiesen, in denen der Selection
Changed Listener Informationen des im Tree Viewer angewählten Objekts ausgibt.

1. Das Package müssen wir vorher noch erzeugen.

481
eclipse_v01.book Seite 482 Montag, 30. Januar 2006 12:02 12

... Einschub: Aufbau der Eclipse-Oberfläche

package com.entwickler.eclipsebuch.workbenchview;

...

public class WorkbenchView extends ViewPart {


private StatusLineContributionItem statusLineItem1;
private StatusLineContributionItem statusLineItem2;
private StatusLineContributionItem statusLineItem3;
private TreeViewer treeViewer;

private class WorkbenchTreeSelectionChangedListener


implements ISelectionChangedListener {

public void selectionChanged(SelectionChangedEvent event) {


StructuredSelection selection =
(StructuredSelection) event.getSelection();
if (selection.size() == 1) {
Object element = selection.getFirstElement();
statusLineItem1.setText("");
statusLineItem2.setText("");
statusLineItem3.setText("");
if (element instanceof IWorkbenchPart) {
statusLineItem1.setText(((IWorkbenchPart) element).
getTitle());
} else if (element instanceof IAction) {
statusLineItem2.setText(((IAction) element).getText());
} else if (element instanceof IWorkbenchPartSite) {
statusLineItem1.setText(
((IWorkbenchPartSite) element).getId());
statusLineItem3.setText(
((IWorkbenchPartSite) element).getPluginId());
}
}
}
}

private class WorkbenchTreeLabelProvider extends LabelProvider {

public String getText(Object element) {


if (element != null) {
return element.getClass().getName()
+ " ["
+ element.toString()
+ "]";
}
return "<null>";
}
}

482
eclipse_v01.book Seite 483 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

private class WorkbenchTreeContentProvider


implements ITreeContentProvider {

public Object[] getChildren(Object parentElement) {


if (parentElement instanceof IWorkbenchWindow) {
IWorkbenchWindow window =
(IWorkbenchWindow) parentElement;
return window.getPages();
}
if (parentElement instanceof IWorkbenchPage) {
IWorkbenchPage page = (IWorkbenchPage) parentElement;
List editorReferences =
Arrays.asList(page.getEditorReferences());
List viewReferences =
Arrays.asList(page.getViewReferences());
Vector children = new Vector();
children.addAll(viewReferences);
children.addAll(editorReferences);
return children.toArray();
}
if (parentElement instanceof IWorkbenchPartReference) {
IWorkbenchPartReference partref =
(IWorkbenchPartReference) parentElement;
return new Object[] {partref.getPart(true)};
}
if (parentElement instanceof IWorkbenchPart) {
IWorkbenchPart part = (IWorkbenchPart) parentElement;
return new Object[] {part.getSite()};
}
if (parentElement instanceof IViewSite) {
IViewSite viewSite = (IViewSite) parentElement;
return new Object[] {viewSite.getActionBars()};
}
if (parentElement instanceof IEditorSite) {
IEditorSite editorSite = (IEditorSite) parentElement;
return new Object[] {editorSite.getActionBars()};
}
if (parentElement instanceof IActionBars) {
IActionBars actionBars = (IActionBars) parentElement;
Vector managers = new Vector();
managers.add(actionBars.getMenuManager());
managers.add(actionBars.getStatusLineManager());
managers.add(actionBars.getToolBarManager());
return managers.toArray();
}
if (parentElement instanceof IContributionManager) {
IContributionManager manager =

483
eclipse_v01.book Seite 484 Montag, 30. Januar 2006 12:02 12

... Einschub: Aufbau der Eclipse-Oberfläche

(IContributionManager) parentElement;
return manager.getItems();
}
if (parentElement instanceof ActionContributionItem) {
ActionContributionItem item =
(ActionContributionItem) parentElement;
return new Object[] {item.getAction()};
}
return new Object[0];
}

public Object getParent(Object element) {


return null;
}

public boolean hasChildren(Object element) {


Object[] children = getChildren(element);
return (children != null && children.length > 0);
}

public Object[] getElements(Object inputElement) {


if (inputElement instanceof IWorkbench) {
IWorkbenchWindow[] windows =
((IWorkbench) inputElement).getWorkbenchWindows();
return windows;
}
return null;
}

public void dispose() {}

public void inputChanged(


Viewer viewer,
Object oldInput,
Object newInput) {}
}

private class WorkbenchViewRefreshAction extends Action {

public WorkbenchViewRefreshAction() {
try {
setDescription("Refresh");
setToolTipText("Refresh outline tree");
ImageDescriptor image =
ImageDescriptor.createFromURL(
new URL(
WorkbenchviewPlugin
.getDefault()

484
eclipse_v01.book Seite 485 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

.getBundle()
.getEntry("/"),
"icons/refresh_nav.gif"));
setImageDescriptor(image);
} catch (MalformedURLException e) {
WorkbenchviewPlugin.getDefault().log(e);
}
}

public void run() {


treeViewer.refresh();
}
}

public void createPartControl(Composite parent) {


treeViewer =
new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL |
SWT.V_SCROLL);
treeViewer.setContentProvider(
new WorkbenchTreeContentProvider());
treeViewer.addSelectionChangedListener(
new WorkbenchTreeSelectionChangedListener());
treeViewer.setLabelProvider(new WorkbenchTreeLabelProvider());
treeViewer.setInput(PlatformUI.getWorkbench());

IToolBarManager toolbar =
getViewSite().getActionBars().getToolBarManager();
toolbar.add(new WorkbenchViewRefreshAction());

IStatusLineManager statusline =
getViewSite().getActionBars().getStatusLineManager();
statusLineItem1 =
new StatusLineContributionItem("statusLineItem1", true, 40);
statusLineItem2 =
new StatusLineContributionItem("statusLineItem2", true, 40);
statusLineItem3 =
new StatusLineContributionItem("statusLineItem3", true, 40);
statusline.add(statusLineItem1);
statusline.add(statusLineItem2);
statusline.add(statusLineItem3);
}

public void setFocus() {


treeViewer.refresh();
treeViewer.getControl().setFocus();
}
}

485
eclipse_v01.book Seite 486 Montag, 30. Januar 2006 12:02 12

... Einschub: Aufbau der Eclipse-Oberfläche

Abb. 9.27 zeigt die Workbench View in Aktion, wenn neben der Workbench View der
Package Explorer und unser Java Properties Editor geöffnet sind.

Abb. 9.27: Die Workbench View in Aktion

Beim Erzeugen des Plug-In-Projekts com.entwickler.eclipsebuch.workbenchview ist eine


Plug-In-Klasse generiert worden (WorkbenchViewPlugin). Wir spendieren ihr noch eine
Methode für das Logging von Fehlern (in der Error Log View):

package com.entwickler.eclipsebuch.workbenchview;

(...)

public class WorkbenchviewPlugin extends AbstractUIPlugin {


public static final String PLUGIN_ID =
"com.entwickler.eclipsebuch.workbenchview";

(...)

public void log(Throwable e) {


IStatus status =
new Status(IStatus.ERROR, PLUGIN_ID, -1, "Exception", e);
getLog().log(status);
}
}

486
eclipse_v01.book Seite 487 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

9.7 Und Action ...: Actions in der Eclipse Workbench (Teil 2)


Nun wollen wir an der Outline View des Java Property Editors weiterarbeiten. Zur Erinne-
rung: Wir wollten über einen Button in der Toolbar der Outline View alle Zweige des
Baums selektieren, die zu der Zeile im Editor gehören, in der gerade der Cursor steht (siehe
Abb. 9.20). Inzwischen wissen wir, dass hinter einem Button eine Action steht. Und wir
wissen auch, wie wir diese Action in die Toolbar einbringen.
Zunächst erstellen wir die Action (JPELinkAction). Wir definieren sie als innere Klasse
in JPOAdapter. Dazu erhält JPOAdapter eine Methode linkWithCurrentLine, die am
Tree Viewer die Selektion der Zweige auslöst. Die Action JPELinkAction wird in der
Methode createControl in der Toolbar der View eingebracht.

package com.entwickler.eclipsebuch.javapropertieseditor;
...

public class JPOAdapter extends ContentOutlinePage {


private JavaPropertiesEditor editor;
private JavaPropertiesTree jpt;

public JPOAdapter(JavaPropertiesEditor editor) {


this.editor = editor;
}

...

private class JPELinkAction extends Action {


public JPELinkAction() {
super();
try {
setDescription("Link");
setToolTipText("Link with java properties editor");
ImageDescriptor image =
ImageDescriptor.createFromURL(
new URL(
JPEPlugin.getDefault().getBundle().
getEntry("/"),
"icons/backward_nav.gif"));
setImageDescriptor(image);
} catch (MalformedURLException e) {
JPEPlugin.getDefault().log(e);
}
}

public void run() {


linkWithCurrentLine();
}
}

487
eclipse_v01.book Seite 488 Montag, 30. Januar 2006 12:02 12

Und Action ...: Actions in der Eclipse Workbench (Teil 2)

...

public void createControl(Composite parent) {


super.createControl(parent);
jpt = new JavaPropertiesTree(getTreeViewer(), getDocument());
jpt.addJavaPropertiesSelector(new JPOSelector());

IToolBarManager toolBarManager =
getSite().getActionBars().getToolBarManager();
toolBarManager.add(new JPELinkAction());
}

public void linkWithCurrentLine() {


int line = editor.getLineAndColumn()[0];
linkWithLineNumber(line);
}

public void linkWithLineNumber(int linenum) {


jpt.selectNodesFromLineNumber(linenum);
}
}

Nachdem wir die Eclipse Application gestartet haben, müssten wir ein ähnliches Bild wie
in Abb. 9.20 sehen.

9.7.1 Ein Button mit persistentem Status


Auch wenn unser Java Property Editor schon recht komfortabel ist, sind wir immer noch
nicht zufrieden. Wir wollen mehr. Wir kennen alle den Toggle-Button „Link with Editor“ in
der Outline View des Java Editors:
Wenn er gedrückt ist, wird stets die Methode, das Attribut, die innere Klasse usw. selektiert,
auf der/dem gerade der Cursor im Java Editor steht. Eine solche Funktionalität soll auch die
Outline View des Java Properties Editors bekommen.
Um automatisch nach jeder Änderung der Cursorposition die Selektion in der Outline View
ändern zu können, muss man über jede Änderung der Cursorposition im Editor informiert
werden. Leider liefert der Texteditor (org.eclipse.ui.editors.text.TextEditor)
von Haus aus nicht diese Möglichkeit (z.B. über so etwas wie einen ICursorPosi-
tionChangedListener).
Also müssen wir dem JavaPropertiesEditor eine derartige Funktionalität verschaffen:
Die Methode handleCursorPositionChanged von AbstractTextEditor – eine
Klasse in der Ahnenreihe von JavaPropertiesEditor – wird nach jeder Änderung der
Cursorposition aufgerufen. Hier haben wir den Angriffspunkt: Die Klasse JavaProper-

488
eclipse_v01.book Seite 489 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

tiesEditor muss nur die Möglichkeit zur Verfügung stellen, Actions registrieren zu kön-
nen, die nach jeder Änderung der Cursorposition aufgerufen werden1:

package com.entwickler.javapropertieseditor;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.Iterator;
import java.util.StringTokenizer;
import java.util.Vector;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.CoolBar;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.ui.editors.text.TextEditor;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;

public class JavaPropertiesEditor extends TextEditor {


private JPOAdapter jpoadapter;
private Vector cursorPositionChangedActions;

public JavaPropertiesEditor() {
super();
jpoadapter = new JPOAdapter(this);
cursorPositionChangedActions = new Vector();
}

public void addCursorPositionChangedAction(IAction action) {


cursorPositionChangedActions.add(action);
}

public void removeCursorPositionChangedAction(IAction action) {


cursorPositionChangedActions.remove(action);
}

protected void handleCursorPositionChanged() {


super.handleCursorPositionChanged();
Iterator it = cursorPositionChangedActions.iterator();

1. Ab hier beginnt die vierte Version des Plug-Ins com.entwickler.eclipsebuch.javapropertieseditor. Man findet
sie auf der CD in dem Verzeichnis Kap_9/javapropertieseditor/v4.

489
eclipse_v01.book Seite 490 Montag, 30. Januar 2006 12:02 12

Und Action ...: Actions in der Eclipse Workbench (Teil 2)

while(it.hasNext()) {
IAction action = (IAction) it.next();
action.run();
}
}

...

Beim Editor kann eine Action (IAction) an- oder abgemeldet werden, die bei jeder Cur-
sorbewegung ausgelöst wird. Eine angemeldete Action kann dann z.B. die Selektion inner-
halb der Outline View auslösen.
Im folgenden Listing nehmen wir an, wir hätten eine Klasse mit Namen Persistent-
ToggleAction, die das Verhalten eines Toggle-Buttons implementiert1. Die Erben von
PersistentToggleAction müssen nur die beiden Methoden toggleOn und toggle-
Off überschreiben, die bei Ein- bzw. Ausschalten des Toggle-Buttons aufgerufen werden.
Wir melden innerhalb von toggleOn eine Action für die Selection innerhalb der Outline
View an (die innere Klasse JPECursorPositionChangedAction) und innerhalb von
toggleOff wieder ab. In createControl wird die neue Toggle Action in die Toolbar
eingebracht:

package com.entwickler.javapropertieseditor;

import java.net.MalformedURLException;
import java.net.URL;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.views.contentoutline.ContentOutlinePage;
import com.entwickler.controls.IJavaPropertiesSelector;
import com.entwickler.controls.JavaPropertiesTree;
import com.entwickler.controls.actions.PersistentToggleAction;

public class JPOAdapter extends ContentOutlinePage {


private JavaPropertiesEditor editor;
private JavaPropertiesTree jpt;

1. Die Klasse PersistentToggleAction werden wir in Kürze implementieren.

490
eclipse_v01.book Seite 491 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

private JPECursorPositionChangedAction cpcAction;

public JPOAdapter(JavaPropertiesEditor editor) {


this.editor = editor;
cpcAction = new JPECursorPositionChangedAction();
}

...

private class JPECursorPositionChangedAction extends Action {

public void run() {


int line = editor.getLineAndColumn()[0];
linkWithLineNumber(line);
}
}

private class JPEToggleLinkAction


extends PersistentToggleAction {

public JPEToggleLinkAction() throws MalformedURLException {


super(
"toggleLinkingAction",
"Toggle Linking",
"Toggle Linking",
new URL(
JPEPlugin.getDefault().getBundle().getEntry("/"),
"icons/synced.gif"));
}

protected void toggleOn() {


editor.addCursorPositionChangedAction(cpcAction);
}

protected void toggleOff() {


editor.removeCursorPositionChangedAction(cpcAction);
}
}

...

public void createControl(Composite parent) {


super.createControl(parent);
jpt = new JavaPropertiesTree(getTreeViewer(), getDocument());
jpt.addJavaPropertiesSelector(new JPOSelector());

IToolBarManager toolBarManager =
getSite().getActionBars().getToolBarManager();

491
eclipse_v01.book Seite 492 Montag, 30. Januar 2006 12:02 12

Und Action ...: Actions in der Eclipse Workbench (Teil 2)

toolBarManager.add(new JPELinkAction());
try {
toolBarManager.add(new JPEToggleLinkAction());
} catch (MalformedURLException e) {
JPEPlugin.getDefault().log(e);
}
}
}

Wenn das obige Listing kein Wunschtraum bleiben soll, schulden wir noch die Persis-
tentToggleAction1. Dabei spendieren wir ihr noch die Fähigkeit, ihren Zustand über das
Schließen und Öffnen von Eclipse zu behalten.
Eclipse bietet die Möglichkeit, an jeder Ressource persistente Informationen zu hinter-
legen. Eine Ressource ist ein Ordner (Folder), eine Datei (File), ein Projekt (Project) oder
auch der Vorgesetzte von all diesen: die Workspace Root. Jede Ressource implementiert
das Interface org.eclipse.core.resources.IResource. Dieses Interface bietet fol-
gende Methoden zur Hinterlegung persistenter Informationen an:
쐌 void setSessionProperty
(QualifiedName key, Object value)
Speichert den Wert value unter dem Schlüssel key an der Ressource für die Dauer der
Session, d.h. bis zum Schließen von Eclipse.
쐌 Object getSessionProperty(QualifiedName key)
Gibt diesen Wert zurück.
쐌 void setPersistentProperty
(QualifiedName key, String value)
Speichert den Wert value unter dem Schlüssel key an der Ressource persistent über
das Schließen und wieder Öffnen von Eclipse hinaus.
쐌 String getPersistentProperty
(QualifiedName key)
Gibt diesen Wert zurück.
Der QualifiedName besteht aus zwei Strings, dem Qualifier und dem Local Name. Als
Qualifier bietet sich die ID des Plug-Ins an. Damit alle etwas von unserer Persistent-
ToggleAction haben, stellen wir sie im Plug-In com.entwickler.eclipsebuch.
controls allen zur Verfügung:

package com.entwickler.eclipsebuch.controls.actions;

import java.net.URL;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;

1. Ab hier beginnt die fünfte Version von com.entwickler.eclipsebuch.controls. Man findet es auf der CD im Ver-
zeichnis Kap_9/controls/v5.

492
eclipse_v01.book Seite 493 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.resource.ImageDescriptor;
import com.entwickler.eclipsebuch.controls.ControlsPlugin;

public class PersistentToggleAction extends Action {


private boolean toggleStatus = false;
private String id;

public boolean isToggleStatus() {


return toggleStatus;
}

public PersistentToggleAction(
String id,
String description,
String toolTipText,
URL iconURL) {
super(id);
this.id = id;
setDescription(description);
setToolTipText(toolTipText);
initToggleStatus();
setChecked(toggleStatus);
if (iconURL != null) {
ImageDescriptor image =
ImageDescriptor.createFromURL(iconURL);
setImageDescriptor(image);
}
}

/**
* Den Status der Toggle Action aus den Persistent
* Properties auslesen
*/
private void initToggleStatus() {
try {
IWorkspaceRoot wsroot =
ResourcesPlugin.getWorkspace().getRoot();
String statusStr =
wsroot.getPersistentProperty(
new QualifiedName(ControlsPlugin.PLUGIN_ID, id));
toggleStatus = (new Boolean(statusStr)).booleanValue();
} catch (CoreException e) {
ControlsPlugin.getDefault().log(e);
}
}

493
eclipse_v01.book Seite 494 Montag, 30. Januar 2006 12:02 12

Und Action ...: Actions in der Eclipse Workbench (Teil 2)

/**
* Den Status der Action wechseln
* und abspeichern
*/
private void toggleToggleStatus() {
try {
toggleStatus = !toggleStatus;
String statusStr = Boolean.toString(toggleStatus);
IWorkspaceRoot wsroot =
ResourcesPlugin.getWorkspace().getRoot();
wsroot.setPersistentProperty(
new QualifiedName(
ControlsPlugin.PLUGIN_ID,
"toggleLinkingStatus"),
statusStr);
} catch (CoreException e) {
ControlsPlugin.getDefault().log(e);
}
}

/**
* Kann in der abgeleiteten Klasse
* ueberschrieben werden.
*/
protected void toggleOn() {
};

protected void toggleOff() {


};

public void run() {


toggleToggleStatus();
if (toggleStatus) {
toggleOn();
} else {
toggleOff();
}
}
}

Nun können wir die Eclipse Application starten und sehen einen Toggle-Button in der Tool-
bar der Outline View des Java Properties Editor. Ist er gedrückt, wird stets die aktuelle Zeile
des Editors in der Outline View selektiert (siehe Abb. 9.28).

494
eclipse_v01.book Seite 495 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

Abb. 9.28: Der Toggle-Button in Aktion

9.8 Page Book View: ein Buch mit sieben Seiten


Was machen eigentlich die neuen Buttons in der Toolbar der Outline View für den Java Pro-
perties Editor, wenn ein anderer Editor den Fokus hat, der eine andere Outline View besitzt
(z.B. ein Java Editor)? Einfach mal ausprobieren und schon sieht man es: Sie verschwinden.
Das ist auch gut so, denn schließlich haben die Buttons in den anderen Outline Views kei-
nen Sinn. Aber wie geht das?
Wir erinnern uns: Der JPOAdapter, der die Outline View für den Java Properties Editor
implementiert, leitet von ContentOutlinePage ab. ContentOutlinePage ist wieder-
um eine Page Book View (org.eclipse.ui.part.PageBookView, siehe Abb. 9.29).

Abb. 9.29: Klassenmodell der Outline View

Eine Page Book View ist eine View, die mehrere Seiten (org.eclipse.ui.part.Page)
anzeigen kann. Die Outline View (ContentOutline) fragt die Page, die angezeigt werden
soll (ContentOutlinePage), jeweils vom aktiven Editor über die Methode getAdapter
mit dem Argument ContentOutlinePage.class ab. Wir erinnern uns, dass wir diese
Methode beim Java Properties Editor überschrieben haben. Jeder Page sind Action Bars zu-
geordnet, d.h., sie hat z.B. ihre eigene Toolbar. Und diese wird nur dann angezeigt, wenn
diese Page gerade aktiv ist.

495
eclipse_v01.book Seite 496 Montag, 30. Januar 2006 12:02 12

Page Book View: ein Buch mit sieben Seiten

Über getCurrentPage kann von einer Page Book View die gerade aktive Page erfragt
werden. Um die Klasse der gerade aktiven Page in der Outline View zu sehen, erweitern wir
unsere oben erstellte Workbench View1:

package com.entwickler.eclipsebuch.workbenchview;
...
import org.eclipse.ui.part.PageBookView;
...

public class WorkbenchView extends ViewPart {


...
private class WorkbenchTreeLabelProvider extends LabelProvider {

public String getText(Object element) {


if (element != null) {
return element.getClass().getName()
+ " ["
+ element.toString()
+ "]"
+ (element instanceof PageBookView
? " - "
+ ((PageBookView) element)
.getCurrentPage()
.getClass()
.getName()
: "");
}
return "<null>";
}
}

...

Bei aktivem Java Properties Editor und geöffneter Outline View kann man nun die Klasse
der entsprechenden Page (JPOAdapter) sehen (siehe Abb. 9.30):

1. Ab hier ensteht die zweite Version von com.entwickler.eclipsebuch.workbenchview. Sie befindet sich auf der
CD im Verzeichnis Kap_9/workbenchview/v2.

496
eclipse_v01.book Seite 497 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

Abb. 9.30: Die Workbench View zeigt die Klasse der aktiven Page an.

9.8.1 Action Delegates oder „Wie kommt man von außen


in das Buch?“
Jetzt wollen wir einen Schwierigkeitsgrad weitergehen. Angenommen, wir wollten einer
ContentOutlinePage eines anderen Plug-In eine neue Action spendieren. Dazu nehmen
wir an, das Plug-In com.entwickler.eclipsebuch.javapropertieseditor wäre
ein fremdes Plug-In, dessen Code wir nicht verändern wollen. Wir werden der Outline View
für den Java Properties Editor (JPOAdapter) zwei Buttons spendieren:
쐌 Der erste Button soll den Baum aktualisieren.
쐌 Der zweite Button soll den Baum auf einen Schlag zusammenklappen.
Die beiden Buttons sollen über den Extension Point org.eclipse.ui.viewActions an-
gemeldet werden. Doch hier haben wir ein kleines Problem. Man kann über diesen Exten-
sion Point einen Button in den Toolbar einer anderen View einbringen. Aber leider nicht in
der Toolbar nur einer Seite eines Page Book. Die Action, die wir in der Toolbar einbringen,
muss also überprüfen, welche Seite gerade angezeigt wird. Ist es nicht die richtige Seite (in
unserem Fall JPOAdapter), so muss sie sich selbst sperren.
Wir wollen eine abstrakte Klasse mit diesem Verhalten implementieren (PageBook-
ActionDelegate). Da sie auch anderen nützen kann, packen wir sie in unser Plug-In
com.entwickler.eclipsebuch.controls1. Bevor wir uns den Code dieser Klasse an-
sehen, ein paar Anmerkungen vorweg:
쐌 Eine Action, die über den Extension Point org.eclipse.ui.viewActions ange-
meldet wird, um in der Toolbar einer View zu erscheinen, muss das Interface org.
eclipse.ui.IViewActionDelegate implementieren.
쐌 Es wird nicht diese Action selbst in der Toolbar angezeigt, sondern eine Stellvertreterin
(die so genannte Plug-In Action, z.B. org.eclipse.ui.internal.ViewPlugin-

1. Dabei entsteht die sechste Version dieses Plug-Ins. Sie befindet sich auf der CD in dem Verzeichnis
Kap_9/controls/v6.

497
eclipse_v01.book Seite 498 Montag, 30. Januar 2006 12:02 12

Page Book View: ein Buch mit sieben Seiten

Action). Wenn der Toolbar-Button betätigt wird, delegiert die Plug-In Action die Aus-
führung an die eigentliche Action. Man nennt darum die Action, die den Ausführungs-
code enthält, einen Action Delegate. Wir nennen unsere Klasse analog PageBook-
ActionDelegate.
쐌 Der Action Delegate bekommt bei der Initialisierung sowohl die Plug-In Action
(init(IAction)) als auch die View, in deren Toolbar die Plug-In Action erscheint
(init(IViewPart)), übergeben.
쐌 PageBookActionDelegate horcht über einen Part Listener (PartListener) mit,
wenn in der Outline View die Page gewechselt wird. Dann prüft sie nach, ob die Page
vom richtigen Typ ist. Wenn ja, wird die Plug-In Action aktiviert. Wenn nicht, wird sie
deaktiviert.
쐌 Welchen Typ die Page haben muss, bekommt PageBookActionDelegate über den
Konstruktor übergeben (pageBookActionDelegate(Class)).
쐌 In der Methode isCorrectPage wird entschieden, ob die aktuelle Seite der Outline
View vom richtigen Typ ist (der im Konstruktor übergeben wurde).
쐌 Die run-Methode einer abgeleiteten konkreten Klasse wird nur aufgerufen, wenn in der
Outline View die Page vom richtigen Typ angezeigt wird (run(IAction)).

package com.entwickler.eclipsebuch.controls.actions;

import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.IViewActionDelegate;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.actions.ActionDelegate;
import org.eclipse.ui.part.IPage;
import org.eclipse.ui.part.PageBookView;

public abstract class PageBookActionDelegate


extends ActionDelegate
implements IViewActionDelegate {
private IViewPart view = null;
private IAction action = null;
private Class pageClass;

public PageBookActionDelegate(Class pageClass) {


super();
this.pageClass = pageClass;
}

/**
* Je nachdem welche Seite gerade angezeigt wird,
* wird die Action ein- oder ausgeschaltet.
*/

498
eclipse_v01.book Seite 499 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

private void enableOrDisable() {


if(action == null) {
return;
}
if (isCorrectPageClass()) {
action.setEnabled(true);
} else {
action.setEnabled(false);
}
}

/**
* Sobald eine Seite des Page Book aktiviert wird,
* wird entschieden, ob die Aktion ein- oder
* ausgeschaltet wird.
*/
private class PartListener implements IPartListener {

public void partActivated(IWorkbenchPart part) {


enableOrDisable();
}

public void partBroughtToTop(IWorkbenchPart part) {}


public void partClosed(IWorkbenchPart part) {}
public void partDeactivated(IWorkbenchPart part) {}
public void partOpened(IWorkbenchPart part) {}
}

public void init(IViewPart view) {


this.view = view;
enableOrDisable();
view
.getSite()
.getWorkbenchWindow()
.getPartService()
.addPartListener(
new PartListener());
}

public void selectionChanged(


IAction action,
ISelection selection) {}

protected IPage getCurrentPage() {


if (view instanceof PageBookView) {
return ((PageBookView) view).getCurrentPage();
}
return null;
}

499
eclipse_v01.book Seite 500 Montag, 30. Januar 2006 12:02 12

Page Book View: ein Buch mit sieben Seiten

private boolean isCorrectPageClass() {


IPage page = getCurrentPage();
return pageClass.isInstance(page);
}

public void run(IAction action) {


if (isCorrectPageClass()) {
run();
}
}

public void init(IAction action) {


super.init(action);
this.action = action;
enableOrDisable();
}

public abstract void run();


}

Die Actions zum Zusammenklappen (JPOCollapseAllActionDelegate) und Aktuali-


sieren (JPORefreshActionDelegate) sind mit dieser Klasse einfach zu implementieren.
Wir legen uns aber vorher ein neues Plug-In mit Namen com.entwickler.eclipsebuch.jpo-
contributions1 und darin ein Package mit gleichem Namen an. Darin erzeugen wir die fol-
genden Klassen2:

package com.entwickler.eclipsebuch.jpocontributions;

import com.entwickler.eclipsebuch.\
controls.actions.PageBookActionDelegate;
import
com.entwickler.eclipsebuch.javapropertieseditor.JPOAdapter;

public class JPOCollapseAllActionDelegate


extends PageBookActionDelegate {

public JPOCollapseAllActionDelegate() {
super(JPOAdapter.class);
}

1. Die erste Version dieses Plug-Ins findet man auf der CD in dem Verzeichnis Kap_9/jpocontributions/v1.
2. Die Backslashes (\) sollen anzeigen, dass es in der nächsten Zeile weitergeht. Sie dürfen nicht eingegeben
werden.

500
eclipse_v01.book Seite 501 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

public void run() {


JPOAdapter jpo = (JPOAdapter) getCurrentPage();
jpo.getJavaPropertiesTree().getTreeviewer().collapseAll();
}
}

package com.entwickler.eclipsebuch.jpocontributions;

import com.entwickler.eclipsebuch.controls.\
actions.PageBookActionDelegate;
import
com.entwickler.eclipsebuch.javapropertieseditor.JPOAdapter;

public class JPORefreshActionDelegate extends


PageBookActionDelegate {

public JPORefreshActionDelegate() {
super(JPOAdapter.class);
}

public void run() {


JPOAdapter jpo = (JPOAdapter) getCurrentPage();
jpo.update();
}
}

Nun fehlt noch die Definition der Extensions für den Extension Point org.eclipse.ui.view-
Actions. Hier muss die ID der Outline View angegeben werden. Man erhält sie, indem man
entweder in der Extension nachsieht, die die Outline View einführt. Sie befindet sich im
Plug-In org.eclipse.ui.views (siehe Abb. 9.31).

Abb. 9.31: Definition der Outline View in der Extension

Oder man nimmt die oben erstellte Workbench View zu Hilfe. Ist die Outline View geöffnet
und wählt man im Baum der Workbench View die View Site der Outline View, so sieht man

501
eclipse_v01.book Seite 502 Montag, 30. Januar 2006 12:02 12

Page Book View: ein Buch mit sieben Seiten

die ID in der Statuszeile (siehe Abb. 9.32). Außerdem wird die ID des Plug-Ins angezeigt,
in dem sich die Extension befindet.

Abb. 9.32: Die Workbench View zeigt die ID der Outline View an

Das Plug-In Manifest sieht damit wie folgt aus1:

<?xml version="1.0" encoding="UTF-8"?>


<?eclipse version="3.0"?><plugin
id="com.entwickler.eclipsebuch.jpocontributions"
name="com.entwickler.eclipsebuch.jpocontributions"
version="1.0.0"
class="com.entwickler.eclipsebuch.jpocontributions.\
JPOContributionsPlugin">

<runtime>
<library name="jpocontributions.jar">
<export name="*"/>
</library>
</runtime>
<requires>
<import plugin="com.entwickler.eclipsebuch.controls"/>
<import plugin="com.entwickler.eclipsebuch.\
javapropertieseditor"/>
<import plugin="org.eclipse.ui.workbench"/>
<import plugin="org.eclipse.ui.views"/>
<import plugin="org.eclipse.jface"/>
</requires>

1. Die Backslashes (\) sollen anzeigen, dass es in der nächsten Zeile weitergeht. Sie dürfen nicht eingegeben
werden.

502
eclipse_v01.book Seite 503 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

<extension point="org.eclipse.ui.viewActions">
<viewContribution
targetID="org.eclipse.ui.views.ContentOutline"
id="com.entwickler.eclipsebuch.jpocontributions.\
refreshaction">
<action label="Refresh"
icon="icons/refresh_nav.gif"
class="com.entwickler.eclipsebuch.jpocontributions.\
JPORefreshActionDelegate"
tooltip="Refresh Property Tree"
toolbarPath="additions"
id="com.entwickler.eclipsebuch.jpocontributions.\
refreshaction">
</action>
</viewContribution>
<viewContribution
targetID="org.eclipse.ui.views.ContentOutline"
id="com.entwickler.eclipsebuch.jpocontributions.\
collapseallaction">
<action label="Collapse All"
icon="icons/collapseall.gif"
class="com.entwickler.eclipsebuch.jpocontributions.\
JPOCollapseAllActionDelegate"
tooltip="Collapse all Branches in Property Tree"
toolbarPath="additions"
id="com.entwickler.eclipsebuch.jpocontributions.\
collapseallaction">
</action>
</viewContribution>
</extension>
</plugin>

Nun können wir die Eclipse Application starten. Wir sind aber nicht vollständig zufrieden.
Denn auch wenn ein anderer Editor den Fokus hat und damit die Outline View keinen
JPOAdapter anzeigt, sind die neuen Buttons in der Toolbar immer noch aktiviert1 (siehe
Abb. 9.33).

1. Dies ist allerdings nur ein kosmetisches Problem. Denn in der PageBookActionDelegate wird die Funk-
tionalität des Action Delegate (run-Methode) nur dann aufgerufen, wenn die Page Book View die richtige
Seite anzeigt.

503
eclipse_v01.book Seite 504 Montag, 30. Januar 2006 12:02 12

Page Book View: ein Buch mit sieben Seiten

Abb. 9.33: Die neuen Buttons sind in der falschen Outline View eingeschaltet.

Wie kommt das? Das ist die Kehrseite des so genannten Lazy Loading. In Eclipse werden
Plug-Ins erst dann geladen, wenn sie auch wirklich benötigt werden. Das ist auch der Grund
der Aufteilung zwischen den Plug-In Actions, die in der Toolbar angezeigt werden, und den
Action Delegates, die den auszuführenden Code enthalten. Erst wenn der Button der Plug-
In Action zum ersten Mal betätigt wird, wird der Action Delegate instanziiert. So kann die
Ladezeit von Eclipse auch bei vielen Plug-Ins klein gehalten werden. Und so verbrauchen
Plug-Ins, die man momentan nicht benötigt, keinen Speicherplatz.
Uns macht dieses Verhalten jedoch einen Strich durch die Rechnung. Denn erst der Action
Delegate (PageBookActionDelegate) entscheidet, ob die Plug-In Action aus- oder ein-
geschaltet werden soll. Doch wird der Action Delegate überhaupt erst instanziiert, wenn die
Plug-In Action zum ersten Mal ausgelöst wird. So kann bis dahin nicht entschieden werden,
ob die Plug-In Action aus- oder eingeschaltet werden soll. Und somit ist sie eingeschaltet.
Eclipse hat dieses Problem erkannt. So bietet der Extension Point org.eclipse.ui.view-
Actions die Möglichkeit, die Aktivierung der Plug-In Action abhängig von der aktuellen
Selektion in der View zu steuern. Ein Beispiel sind die folgenden Zeilen, die in der Defi-
nition der Extension für den Extension Point org.eclipse.ui.viewActions innerhalb des
action-Elements stehen müssen:

<enablement>
<or>
<objectClass name = "org.eclipse.core.resources.IFile"/>
<objectClass name = "org.eclipse.core.resources.IFolder"/>
</or>
</enablement>

504
eclipse_v01.book Seite 505 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

Die Zeilen sagen aus, dass die Plug-In Action genau dann eingeschaltet ist, wenn in der
View nur Objekte selektiert sind, die entweder das Interface org.eclipse.core.re-
sources.IFile oder das Interface org.eclipse.core.resources.IFolder imple-
mentieren. Mit anderen Worten: Die Plug-In Action ist genau dann eingeschaltet, wenn in
der View nur Dateien oder Ordner selektiert sind.
Es gibt aber leider keine Möglichkeit, die Extension für den Extension Point org.
eclipse.ui.viewActions so zu konfigurieren, dass die Plug-In Action nur dann einge-
schaltet sein soll, wenn die Seite einer Page Book View von einem bestimmten Typ ist. Aus
diesem Grund haben wir ja auch diese Funktionalität in die Klasse PageBookAction-
Delegate aufgenommen. So schaltet PageBookActionDelegate die Plug-In Action je
nach Typ der angezeigten Page aus bzw. ein. Aber leider wird diese Klasse zu spät instan-
ziiert, so dass sie zunächst die Plug-In Action eben nicht aus- bzw. einschalten kann. Es
müsste also eine Möglichkeit geben, den PageBookActionDelegate gleich beim Laden
von Eclipse zu instanziieren. Dies sollte man am besten innerhalb der Extension von
org.eclipse.ui.viewActions steuern können.
Aus didaktischen Gründen wollen wir den Extension Point org.eclipse.ui.viewActions und
das Plug-In org.eclipse.ui.workbench, in dem sich die Implementierung dieses Extension
Point befindet, dahingehend abändern. Wir haben zwar in diesem Kapitel schon des Öfteren
betont, dass die Änderung anderer Plug-Ins nur selten der richtige Weg ist, aber man kann
dabei zumindest einiges lernen. Denn um andere Plug-Ins abändern zu können, muss man
ihre Innereien kennen.
In diesem Fall sind die anderen Plug-Ins org.eclipse.ui und org.eclipse.ui.workbench.
org.eclipse.ui deklariert den Extension Point org.eclipse.ui.viewActions, den wir so abän-
dern wollen, dass innerhalb der Extension angegeben werden kann, ob der Action Delegate
sofort instanziiert wird. org.eclipse.ui.workbench enthält die Implementierung dieses Ex-
tension Points.
Als Erstes importieren wir die Plug-Ins org.eclipse.ui und org.eclipse.ui.workbench als
Source Projects (im Kontextmenü der Plug-Ins in der PLUG-INS: VIEW IMPORT | AS SOURCE
PROJECT). Als Nächstes benötigen wir die XML-Schema-Definition des Extension Point
org.eclipse.ui.viewActions. Komischerweise befinden sich die XML-Schema-Definitionen
der ausgelieferten Plug-Ins nicht im Verzeichnis des jeweiligen Plug-Ins. Stattdessen befin-
den sie sich alle innerhalb des Plug-Ins org.eclipse.rcp.source. In der Plug-In View klappen
wir dieses Plug-In auf. Im Unterverzeichnis src existiert wiederum ein Unterverzeichnis
des Plug-Ins org.eclipse.ui (org.eclipse.ui_3.1.0). Dort gibt es das Unterverzeichnis schema
mit den XML-Schema-Definitionen aller Extension Points, die in diesem Plug-In deklariert
werden. Wir kopieren es komplett in das Verzeichnis des Plug-In-Projekts org.eclipse.ui im
Workspace.
Nun öffnen wir das Plug-In Manifest des Plug-In-Projekts org.eclipse.ui, wählen auf der
Seite EXTENSION POINTS den Extension Point viewActions und öffnen den XML Schema Edi-
tor (im Kontextmenü des Extension Point: OPEN SCHEMA)1.

1. Die geänderte XML-Schema-Datei befindet sich auf der CD unter Kap_9/ui/org.eclipse.ui/schema/view-


Actions.exsd.

505
eclipse_v01.book Seite 506 Montag, 30. Januar 2006 12:02 12

Page Book View: ein Buch mit sieben Seiten

Wir wollen nun dem Element action ein neues Attribut mit Namen earlyInit spendie-
ren. Das Attribut soll die beiden möglichen Werte yes oder no bekommen können. Falls
yes, wird das Action Delegate, dessen Klasse über das Attribut class angegeben wird, di-
rekt beim Laden der Plug-In Action instanziiert und initialisiert. Wir wählen also das Ele-
ment action aus und erzeugen über NEW ATTRIBUT ein neues Attribut. Über die Properties
View können wir nun dieses neue Attribut bearbeiten. Wir geben ihm den Namen early-
Init und als Restriction wählen wir innerhalb des Type Restriction-Dialogs enumeration
aus und geben als mögliche Werte no und yes an (siehe Abb. 9.34).

Abb. 9.34: Der Type Restriction-Dialog für das neue Attribut earlyInit

Nachdem wir noch ein paar Worte im Feld DESCRIPTION verloren haben, können wir den
XML-Schema-Editor wieder schließen. Wenn wir nun im Plug-In Manifest von org.eclipse.
ui auf der Seite EXTENSION POINTS die Beschreibung des Extension Point viewAction öffnen
(im Kontextmenü des Extension Point: SHOW DESCRIPTION), so sehen wir gleich unsere Än-
derungen an dem Extension Point (siehe Abb. 9.35).

Abb. 9.35: Beschreibung des veränderten Extension Point

506
eclipse_v01.book Seite 507 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

Die Anpassungen zur Implementierung des geänderten Extension Point sind minimal. In
der Klasse org.eclipse.ui.internal.ActionDescriptor (im Plug-In org.eclipse.
ui.workbench) werden die Extensions des Extension Point viewAction ausgelesen und die
Plug-In Actions werden erzeugt. Zunächst fügen wir aber in der Klasse org.eclipse.
ui.internal.registry.IWorkbenchRegistryConstants eine neue Konstante für
das neue Attribut earlyInit hinzu15:

public class IWorkbenchRegistryConstants {


...
..public static final String ATT_ID = "id";//$NON-NLS-1$
...
public static final String ATT_ACCELERATOR =
"accelerator";//$NON-NLS-1$
public static final String ATT_EARLY_INIT = "earlyInit";
...
}

Der Konstruktor von org.eclipse.ui.internal.ActionDescriptor bekommt ein


Objekt vom Typ org.eclipse.core.runtime.IConfigurationElement übergeben,
über das auf die Elemente der Extension dieses Extension Point zugegriffen werden kann.
Zu Beginn des Konstruktors lesen wir zunächst das neue Attribut aus:

public ActionDescriptor(
IConfigurationElement actionElement,
int targetType,
Object target) {
(...)
String accelerator =
actionElement.getAttribute(ATT_ACCELERATOR);
String earlyInit = actionElement.getAttribute
(WorkbenchRegistryConstants.ATT_EARLY_INIT);

..// Verify input.

(...)

Nun muss nur noch (etwas weiter hinten im Konstruktor, kurz nachdem die Plug-In Action
erzeugt worden ist) der Action Delegate erzeugt werden. Aber nur, wenn das Attribut
earlyInit auf yes steht:

1. Die geänderten Code-Stellen befinden sich auf der CD im Verzeichnis Kap_9/ui.workbench in den Dateien
ActionDescriptor_changes.txt und IWorkbenchRegistryConstants_changes.txt.

507
eclipse_v01.book Seite 508 Montag, 30. Januar 2006 12:02 12

Page Book View: ein Buch mit sieben Seiten

public ActionDescriptor(
IConfigurationElement actionElement,
int targetType,
Object target) {
...
// Create action.
action = createAction(targetType,
actionElement, target, style);
if(earlyInit != null && earlyInit.equals("yes")) {
action.createDelegate();
}

...

Das neue Attribut earlyInit können wir nun in der Extension für den Extension Point
org.eclipse.ui.viewActions im Plug-In com.entwickler.eclipsebuch.jpocontributions auf
yes setzen16 (siehe Abb. 9.36).

Abb. 9.36: Das neue Attribut earlyInit wird in der Extension gesetzt.

1. Dadurch entsteht die zweite Version von com.entwickler.eclipsebuch.jpocontributions, die man auf der CD im
Verzeichnis Kap_9/jpocontributions/v2 findet.

508
eclipse_v01.book Seite 509 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

Die Extension sieht dann wie folgt aus1:

<extension
point="org.eclipse.ui.viewActions">
<viewContribution
targetID="org.eclipse.ui.views.ContentOutline"
id="com.entwickler.eclipsebuch.jpocontributions.refresaction">
<action
label="Refresh"
icon="icons/refresh_nav.gif"
earlyInit="yes"
tooltip="Refresh Property Tree"
class="com.entwickler.eclipsebuch.\
jpocontributions.JPORefreshActionDelegate"
toolbarPath="additions"
id="com.entwickler.eclipsebuch.\
jpocontributions.refreshaction">
</action>
</viewContribution>
<viewContribution
targetID="org.eclipse.ui.views.ContentOutline"
id="com.entwickler.eclipsebuch.\
jpocontributions.collapseallaction">
<action
label="Collapse All"
icon="icons/collapseall.gif"
earlyInit="yes"
tooltip="Collapse all Branches in Property Tree"
class="com.entwickler.eclipsebuch.\
jpocontributions.JPOCollapseAllActionDelegate"
toolbarPath="additions"
id="com.entwickler.eclipsebuch.\
jpocontributions.collapseallaction">
</action>
</viewContribution>
</extension>

Wenn wir jetzt die Eclipse Application starten, sind die neuen Buttons in der Outline View
von vorneherein korrekt ein- bzw. ausgeschaltet.
Wir haben gesehen, dass es nicht ohne Weiteres möglich ist, einer Page Book View von
außen einen Button für die Toolbar (oder einen Menüpunkt für das Menü) zu spendieren,
der von Anfang an nur bei einer bestimmten Page eingeschaltet ist. Ganz abgesehen davon,
dass es noch schwieriger ist, den Button nur bei einer bestimmten Page sichtbar sein zu las-
sen. Aus diesem Grund lassen wir die Outline View des Java Properties Editors die Actions

1. Die Backslashes (\) sollen anzeigen, dass es in der nächsten Zeile weitergeht. Sie dürfen nicht eingegeben
werden.

509
eclipse_v01.book Seite 510 Montag, 30. Januar 2006 12:02 12

Page Book View: ein Buch mit sieben Seiten

selber bei der Toolbar anmelden (wie auch schon bei der JPELinkAction)1. Wir ergänzen
den JPOAdapter um die Actions als innere Klassen und fügen sie in createControl der
Toolbar hinzu2:

public class JPOAdapter extends ContentOutlinePage {


private JavaPropertiesEditor editor;
private JavaPropertiesTree jpt;

(...)

private class JPECollapseAllAction extends Action {


public JPECollapseAllAction() {
super();
try {
setDescription("Collapse All");
setToolTipText("Collapse all nodes in the tree");
ImageDescriptor image =
ImageDescriptor.createFromURL(
new URL(
JPEPlugin.
getDefault().getBundle().getEntry("/"),
"icons/collapseall.gif"));
setImageDescriptor(image);
} catch (MalformedURLException e) {
JPEPlugin.getDefault().log(e);
}
}

public void run() {


jpt.getTreeviewer().collapseAll();
}
}

private class JPERefreshAction extends Action {


public JPERefreshAction() {
super();
try {
setDescription("Refresh");
setToolTipText("Refresh the tree");

1. Schließlich hatte es ja mehr didaktischen Charakter. Denn da wir höchstselbst die Autoren des Plug-Ins
com.entwickler.javapropertieseditor sind, müssen wir Actions nicht unbedingt über eine Extension einbringen.
Die Plug-In-Projekte com.entwickler.eclipsebuch.jpocontributions, org.eclipse.ui und org.eclipse.ui.work-
bench können wir nun schließen oder löschen.
2. Damit entsteht die fünfte Version von com.entwickler.eclipsebuch.javapropertieseditor. Sie befindet sich auf
der CD im Verzeichnis Kap_9/javapropertieseditor/v5.

510
eclipse_v01.book Seite 511 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

ImageDescriptor image =
ImageDescriptor.createFromURL(
new URL(
JPEPlugin.
getDefault().getBundle().getEntry("/"),
"icons/refresh_nav.gif"));
setImageDescriptor(image);
} catch (MalformedURLException e) {
JPEPlugin.getDefault().log(e);
}
}

public void run() {


update();
}
}

public void createControl(Composite parent) {


super.createControl(parent);
jpt = new JavaPropertiesTree(getTreeViewer(), getDocument());
jpt.addJavaPropertiesSelector(new JPOSelector());

IToolBarManager toolBarManager =
getSite().getActionBars().getToolBarManager();
toolBarManager.add(new JPELinkAction());
try {
toolBarManager.add(new JPEToggleLinkAction());
} catch (MalformedURLException e) {
JPEPlugin.getDefault().log(e);
}
toolBarManager.add(new JPERefreshAction());
toolBarManager.add(new JPECollapseAllAction());
}

...

9.9 Markers und Annotations


Nun wollen wir der Outline View für den Java Properties Editor eine weitere Funktionalität
verschaffen. Wir erinnern uns, dass bei einer Selektion eines Zweigs im Tree Viewer der
Outline View auch die entsprechende Zeile im Java Properties Editor selektiert wird. Aller-
dings nur, wenn der Zweig genau einer Property und damit genau einer Zeile im Editor ent-
spricht.

511
eclipse_v01.book Seite 512 Montag, 30. Januar 2006 12:02 12

Markers und Annotations

So wird in Abb. 9.37 mit der Selektion des Zweigs „ist“ keine Zeile im Editor selektiert, da
dieser Zweig aus den beiden Properties
쐌 dies.ist.eine.property
쐌 dies.ist.auch.eine.property
stammt.

Abb. 9.37: Die Zeile im Editor wird nicht selektiert.

Aber auch in diesem Fall wollen wir die Zeilen im Editor irgendwie markieren. Es bieten
sich zwei Techniken an: Markers und Annotations.
Markers kann man einer Ressource zuordnen. Jeder Marker besitzt einen Typ und beliebig
viele Attribute. Annotations werden hingegen den beiden vertikalen Balken rechts und links
eines jeden Editors zugeordnet. Im linken Balken – dem Vertical Ruler – wird eine Annota-
tion mit einem Icon angezeigt. Sie erscheint neben der Zeile, der die Annotation zugeordnet
ist. Im rechten Balken – dem Overview Ruler – erscheint eine Annotation als kleines Recht-
eck. Dabei werden alle Annotations im Editor gleichzeitig im Overview Ruler angezeigt.
Wir wollen beide Techniken anwenden. Als Erstes nehmen wir uns Marker vor. In Eclipse
werden an verschiedenen Stellen Marker verwendet, z.B. für:
쐌 Bookmarks
쐌 Tasks
쐌 Fehler
쐌 Breakpoints
Diese Marker wollen wir uns zunächst einmal genauer ansehen. Wir erzeugen also eine
Marker View.
Dafür erstellen wir uns ein neues, leeres Plug-In Project mit Namen com.entwickler.eclipse-
buch.markerview1. Für unsere Marker View benutzen wir den Extension Point org.eclipse.
ui.views.

1. Die erste Version dieses Plug-Ins befindet sich auf der CD im Verzeichnis Kap_9/markerview/v1.

512
eclipse_v01.book Seite 513 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

Das Plug-In Manifest sieht wie folgt aus:

<?xml version="1.0" encoding="UTF-8"?>


<?eclipse version="3.0"?><plugin
id="com.entwickler.eclipsebuch.markerview"
name="com.entwickler.eclipsebuch.markerview"
version="1.0.0"
provider-name="ENTWICKLER"

class="com.entwickler.eclipsebuch.markerview.MarkerViewPlugin">

<runtime>
<library name="markerview.jar">
<export name="*"/>
</library>
</runtime>
<requires>
<import plugin="org.eclipse.ui.workbench"/>
<import plugin="org.eclipse.jface"/>
<import plugin="org.eclipse.ui.views"/>
<import plugin="org.eclipse.ui"/>
<import plugin="org.eclipse.ui.workbench.texteditor"/>
<import plugin="org.eclipse.ui.ide"/>
<import plugin="org.eclipse.core.resources"/>
</requires>

<extension
point="org.eclipse.ui.views">
<view
name="Marker View"
icon="icons/prefs_misc.gif"
class="com.entwickler.eclipsebuch.markerview.MarkerView"
id="com.entwickler.eclipsebuch.markerview.view1">
</view>
</extension>
</plugin>

Die Klasse com.entwickler.eclipsebuch.markerview.MarkerView müssen wir


nun erstellen:
쐌 Sie erzeugt einen Tree Viewer (in der Methode createPartControl) und weist ihm
das File des aktuellen Editors zu (in der Methode update).
쐌 Der Content Provider des Tree Viewer liest alle Marker des File aus (in der Methode
getElements der inneren Klasse MarkerViewContentProvider).
쐌 Der Content Provider liest von allen Markern alle Attribute aus (in der Methode get-
Children). Jedes Attribut verpackt er als ein Schlüssel-Wert-Paar in der inneren Klas-
se KeyValuePair.

513
eclipse_v01.book Seite 514 Montag, 30. Januar 2006 12:02 12

Markers und Annotations

쐌 Der Label Provider zeigt von den Markern den Typ und von den Attributen die Namen
und den Wert an (in der Methode getText der inneren Klasse MarkerViewLabel-
Provider).
쐌 Die Action MarkerViewRefreshAction wird in der Toolbar der View angezeigt
(Methode: createPartControl). Sie ermittelt jeweils den aktuellen Editor und aktu-
alisiert den Tree Viewer, so dass er die Marker dieses Editors anzeigt (Methode: up-
date).

package com.entwickler.eclipsebuch.markerview;

...

public class MarkerView extends ViewPart {


TreeViewer treeViewer;

private class KeyValuePair {


private Object key;
private Object value;

public KeyValuePair(Object key, Object value) {


this.key = key;
this.value = value;
}

public Object getKey() {


return key;
}
public Object getValue() {
return value;
}
}

private class MarkerViewContentProvider


implements ITreeContentProvider {

public Object[] getChildren(Object parentElement) {


try {
if (parentElement instanceof IMarker) {
// gib die Attribute als Key-Value-Paare zurueck
IMarker marker = (IMarker) parentElement;
Map attributes = marker.getAttributes();
Set keys = attributes.keySet();
Iterator keyIt = keys.iterator();
KeyValuePair[] pairs = new KeyValuePair[keys.size()];
int i = 0;
while (keyIt.hasNext()) {
Object key = keyIt.next();

514
eclipse_v01.book Seite 515 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

Object value = attributes.get(key);


pairs[i] = new KeyValuePair(key, value);
i++;
}
return pairs;
}
} catch (Exception e) {
MarkerviewPlugin.getDefault().log(e);
}
return null;
}

public Object getParent(Object element) {


return null;
}

public boolean hasChildren(Object element) {


return (getChildren(element) != null);
}

public Object[] getElements(Object inputElement) {


try {
if (inputElement instanceof IResource) {
// alle Marker auslesen
IResource res = (IResource) inputElement;
IMarker[] allMarkers =
res.findMarkers(null, true, IResource.DEPTH_INFINITE);
return allMarkers;
}
} catch (Exception e) {
MarkerviewPlugin.getDefault().log(e);
}
return null;
}

public void dispose() {}

public void inputChanged(


Viewer viewer,
Object oldInput,
Object newInput) {
}

515
eclipse_v01.book Seite 516 Montag, 30. Januar 2006 12:02 12

Markers und Annotations

private class MarkerViewLabelProvider extends LabelProvider {

public String getText(Object element) {


try {
if (element instanceof IMarker) {
IMarker marker = (IMarker) element;
return marker.getType();
} else if (element instanceof KeyValuePair) {
KeyValuePair pair = (KeyValuePair) element;
return pair.getKey() + " : " + pair.getValue();
}
} catch (Exception e) {
MarkerviewPlugin.getDefault().log(e);
}
return "";
}
}

private class MarkerViewRefreshAction extends Action {

public MarkerViewRefreshAction() {
try {
setDescription("Refresh");
setToolTipText("Refresh markers");
ImageDescriptor image =
ImageDescriptor.createFromURL(
new URL(
MarkerviewPlugin
.getDefault()
.getBundle()
.getEntry("/"),
"icons/refresh_nav.gif"));
setImageDescriptor(image);
} catch (MalformedURLException e) {
MarkerviewPlugin.getDefault().log(e);
}
}

public void run() {


update();
}
}

/**
* Baue die MarkerView neu auf
*/

516
eclipse_v01.book Seite 517 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

private void update() {


IWorkbench workbench = PlatformUI.getWorkbench();
IWorkbenchWindow window =
workbench.getActiveWorkbenchWindow();
IWorkbenchPage page = window.getActivePage();
IEditorPart editor = page.getActiveEditor();
IEditorInput editorinput = editor.getEditorInput();
if (editorinput instanceof IFileEditorInput) {
IFile file = ((IFileEditorInput) editorinput).getFile();
treeViewer.setInput(file);
}
treeViewer.refresh();
}

public void createPartControl(Composite parent) {


treeViewer =
new TreeViewer(parent,
SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
treeViewer.
setContentProvider(new MarkerViewContentProvider());
treeViewer.setLabelProvider(new MarkerViewLabelProvider());

IToolBarManager toolbar =
getViewSite().getActionBars().getToolBarManager();
toolbar.add(new MarkerViewRefreshAction());

update();
}

public void setFocus() {


update();
}
}

In der generierten Plug-In-Klasse fügen wir noch eine Methode zur Ausgabe von Fehlern in
die Error Log View ein:

package com.entwickler.eclipsebuch.markerview;

(...)

public class MarkerviewPlugin extends AbstractUIPlugin {


public static final String PLUGIN_ID =
"com.entwickler.eclipsebuch.markerview";

(...)

517
eclipse_v01.book Seite 518 Montag, 30. Januar 2006 12:02 12

Markers und Annotations

public void log(Throwable e) {


IStatus status =
new Status(IStatus.ERROR, PLUGIN_ID, -1, "Exception", e);
getLog().log(status);
}
}

Nach dem Start der Eclipse Application kann man die Marker View öffnen (WINDOWS |
SHOW VIEW | OTHER... | OTHER | MARKER VIEW) und sich die Marker anderer Views mitsamt
ihren Attributen ansehen (siehe Abb. 9.38).

Abb. 9.38: Die Marker View zeigt ein Bookmark und eine Task an.

9.9.1 Marker für Properties


In dem in Abb. 9.37 gezeigten Fall sollen nun für alle Properties, die zu dem markierten
Zweig im Baum der Outline View gehören, Marker erzeugt werden. Um diese Marker an-
zuzeigen, wollen wir unsere Marker View benutzen. Damit man aber in dieser View nicht
von allen Arten von Markern erschlagen wird, wollen wir der View einen Filter verpassen.
Schaltet man ihn ein, werden nur die Marker des gerade selektierten Typs angezeigt. Zum
Ein- und Ausschalten des Filters benötigen wir eine Toggle Action. Wir erinnern uns, dass
wir vor einigen Seiten eine Basisklasse für persistente Toggle-Buttons erzeugt haben
(PersistentToggleAction). Analog werden wir eine Basisklasse für transiente Toggle
Actions implementieren. Wie ihre persistente Schwester legen wir sie innerhalb des Plug-
Ins com.entwicker.eclipsebuch.controls an1:

1. Damit entsteht die siebte und letzte Version dieses Plug-Ins. Sie befindet sich auf der CD in dem Verzeichnis
Kap_9/controls/v7.

518
eclipse_v01.book Seite 519 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

package com.entwickler.eclipsebuch.controls.actions;

import java.net.URL;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.resource.ImageDescriptor;

public class TransientToggleAction extends Action {


private boolean toggleStatus = false;

public boolean isToggleStatus() {


return toggleStatus;
}

public TransientToggleAction(
String id,
String description,
String toolTipText,
URL iconURL) {
super(id);
setDescription(description);
setToolTipText(toolTipText);
setChecked(toggleStatus);
if (iconURL != null) {
ImageDescriptor image =
ImageDescriptor.createFromURL(iconURL);
setImageDescriptor(image);
}
}

protected void toggleOn() {


};

protected void toggleOff() {


};

public void run() {


toggleStatus = !toggleStatus;
if (toggleStatus) {
toggleOn();
} else {
toggleOff();
}
}
}

519
eclipse_v01.book Seite 520 Montag, 30. Januar 2006 12:02 12

Markers und Annotations

쐌 Mit dieser Basisklasse werden wir nun eine Action zum Filtern der Marker implemen-
tieren (MarkerViewFilterAction).
쐌 Da die Filter Action nur eingeschaltet sein soll, wenn genau ein Marker selektiert ist,
implementieren wir einen Post Selection Change Listener für den Tree Viewer (Mar-
kerViewSelectionChangeListener). Die Methode selectionChanged wird
nach jeder Änderung der Selektion des Tree Viewer aufgerufen.
쐌 Mit einem Doppelklick auf einen Marker in der Marker View soll die entsprechende
Zeile im Editor, zu der der Marker gehört, selektiert werden (MarkerViewDouble-
ClickListener). Jedoch nur, wenn der Marker das Attribut lineNumber besitzt.
쐌 In diesem Fall soll auch die Zeilennummer in der Marker View neben dem Marker Type
angezeigt werden (MarkerViewLabelProvider)1.

package com.entwickler.eclipsebuch.markerview;

...

public class MarkerView extends ViewPart {


TreeViewer treeViewer;
IAction markerFilterAction = null;
String markerFilterType;

...

private class MarkerViewContentProvider


implements ITreeContentProvider {

public Object[] getChildren(Object parentElement) {

...

...

public Object[] getElements(Object inputElement) {


try {
if (inputElement instanceof IResource) {
IResource res = (IResource) inputElement;
/*
* Falls der Filter Button selektiert ist,
* so filtere nur den gerade selektierten Marker-Typ
*/

1. Nun entsteht die zweite Version von com.entwickler.eclipsebuch.markerview, die man auf der CD im Verzeich-
nis Kap_9/markerview/v2 findet.

520
eclipse_v01.book Seite 521 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

String filter =
markerFilterAction.isChecked() ?
markerFilterType : null;
IMarker[] allMarkers =
res.findMarkers(filter, true,
IResource.DEPTH_INFINITE);
return allMarkers;
}
} catch (Exception e) {
MarkerviewPlugin.getDefault().log(e);
}
return null;
}

...
}

private class MarkerViewLabelProvider extends LabelProvider {

public String getText(Object element) {


try {
if (element instanceof IMarker) {
IMarker marker = (IMarker) element;
Object lineNumber = marker.getAttribute("lineNumber");
if(lineNumber == null) {
return marker.getType();
}
else {
return marker.getType() + " (" + lineNumber + ")";
}
} else if (element instanceof KeyValuePair) {
KeyValuePair pair = (KeyValuePair) element;
return pair.getKey() + " : " + pair.getValue();
}
} catch (Exception e) {
MarkerviewPlugin.getDefault().log(e);
}
return "";
}
}

...

private class MarkerViewSelectionChangedListener


implements ISelectionChangedListener {

521
eclipse_v01.book Seite 522 Montag, 30. Januar 2006 12:02 12

Markers und Annotations

public void selectionChanged(SelectionChangedEvent event) {


try {
IStructuredSelection sel =
(IStructuredSelection) event.getSelection();
/*
* Falls nur ein Marker selektiert ist,
* so merke dessen Typ und schalte den Filter Button frei.
* Durch Auswählen des Filter-Button kann dann
* später nach diesem
* Marker-Typ gefiltert werden.
*/
if (sel.size() == 1) {
Object first = sel.getFirstElement();
if (first instanceof IMarker) {
markerFilterType = ((IMarker) first).getType();
markerFilterAction.setEnabled(true);
return;
}
}
} catch (Exception e) {
MarkerviewPlugin.getDefault().log(e);
}
markerFilterType = null;
if (!markerFilterAction.isChecked()) {
markerFilterAction.setEnabled(false);
}
}
}

/*
* Der Filter Button hat selbst keine Funktionalität.
* Er kann nur ein- und ausgeschaltet werden.
* Dieser Zustand wird dann später ausgelesen.
*/
private class MarkerViewFilterAction
extends TransientToggleAction {

public MarkerViewFilterAction() throws MalformedURLException {


super(
MarkerviewPlugin.PLUGIN_ID + "ToggleFilterAction",
"Filter",
"Filter only markers of selected type",
new URL(
MarkerviewPlugin
.getDefault().getBundle().getEntry("/"),
"icons/filter_ps.gif"));
}

522
eclipse_v01.book Seite 523 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

protected void toggleOff() {


update();
}

protected void toggleOn() {


update();
}

/*
* Durch Doppelklick auf einen Marker soll der
* zu dem Marker gehörende Bereich im Dokument selekitiert
* werden.
*/
private class MarkerViewDoubleClickListener
implements IDoubleClickListener {

public void doubleClick(DoubleClickEvent event) {


try {
IStructuredSelection sel =
(IStructuredSelection) event.getSelection();
if (sel.size() != 1) {
return;
}
Iterator it = sel.iterator();
Object obj = it.next();
if (obj instanceof IMarker) {
IMarker marker = (IMarker) obj;
Integer line = (Integer) marker
.getAttribute("lineNumber");
if (line != null) {
IWorkbench workbench = PlatformUI.getWorkbench();
IWorkbenchWindow window =
workbench.getActiveWorkbenchWindow();
IWorkbenchPage page = window.getActivePage();
IEditorPart editor = page.getActiveEditor();
if (editor instanceof AbstractTextEditor) {
AbstractTextEditor textEditor =
(AbstractTextEditor) editor;
IEditorInput input = textEditor.getEditorInput();
IDocument document =
textEditor.getDocumentProvider()
.getDocument(input);
int linenumber = line.intValue();
IRegion region =
document.getLineInformation(linenumber);

523
eclipse_v01.book Seite 524 Montag, 30. Januar 2006 12:02 12

Markers und Annotations

textEditor.selectAndReveal(
region.getOffset(),
region.getLength());
textEditor.setHighlightRange(
region.getOffset(),
region.getLength(),
true);
}
}
}
} catch (Exception e) {
MarkerviewPlugin.getDefault().log(e);
}
}
}

private void update() {

...

public void createPartControl(Composite parent) {


try {
treeViewer =
new TreeViewer(
parent,
SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
treeViewer.setContentProvider
(new MarkerViewContentProvider());
treeViewer.setLabelProvider(new MarkerViewLabelProvider());
treeViewer.addPostSelectionChangedListener(
new MarkerViewSelectionChangedListener());
treeViewer.addDoubleClickListener(
new MarkerViewDoubleClickListener());

IToolBarManager toolbar =
getViewSite().getActionBars().getToolBarManager();
toolbar.add(new MarkerViewRefreshAction());
markerFilterAction = new MarkerViewFilterAction();
markerFilterAction.setEnabled(false);
toolbar.add(markerFilterAction);

update();
} catch (Exception e) {
MarkerviewPlugin.getDefault().log(e);
}
}

524
eclipse_v01.book Seite 525 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

...

Nun können wir alle Marker anzeigen und filtern.


Jetzt wollen wir eigene Marker erzeugen. Das ist nicht schwierig. Man muss nur an einem
Objekt der Art org.eclipse.core.resources.IResource die Methode create-
Marker aufrufen. Als Parameter bekommt sie den Typ des Markers als String übergeben.
Sei editor eine Referenz auf einen AbstractTextEditor (ein Urahn von JavaPro-
pertiesEditor). Dann kann man dem File, das der Editor anzeigt, einen Marker wie
folgt zuweisen1:

IFile file =
((IFileEditorInput) editor.getEditorInput()).getFile();
IMarker marker =
file.createMarker
("com.entwickler.eclipsebuch.\
javapropertieseditor.propertymarker");

marker kann man nun über marker.setAttribute beliebige Attribute zuweisen. Wir
wollen dem Marker die Attribute lineNumber, property und value zuweisen. Das erste
soll einen java.lang.Integer-Wert enthalten, der die Zeilennummer enthält, in der die
Property steht, die wir markieren wollen. Das zweite Attribut enthält den Namen dieser
Property und das dritte den Wert.
Da wir die Properties aus der Outline View des Java Properties Editors erzeugen wollen,
müssen wir uns neben der Zeilennummer auch den Namen und den Wert einer jeden
Property an den Knoten des Tree Viewersder Outline View merken. Wir müssen also ein paar
Anpassungen an com.entwickler.eclipsebuch.controls.JavaPropertiesTree
vornehmen. Der Tree Viewer besitzt nun Knoten, die aus Vektoren des neuen Typs com.
entwickler.eclipsebuch.controls.PropertyLine bestehen. PropertyLine kann
neben der Zeilennummer auch Namen und Wert einer Property abspeichern. Darüber hinaus
wird auch die Region, d.h. der Offset des ersten und des letzten Zeichens, einer Zeile abge-
speichert. Wie wir bald sehen werden, benötigen wir sie für die Annotations:

package com.entwickler.eclipsebuch.controls;

import org.eclipse.jface.text.IRegion;

1. Die Backslashes (\) sollen anzeigen, dass es in der nächsten Zeile weitergeht. Sie dürfen nicht eingegeben
werden.

525
eclipse_v01.book Seite 526 Montag, 30. Januar 2006 12:02 12

Markers und Annotations

public class PropertyLine {


private int lineNumber;
private String property;
private String value;
private IRegion region;

public PropertyLine(
int lineNumber,
IRegion region,
String property,
String value) {
this.lineNumber = lineNumber;
this.property = property;
this.region = region;
this.value = value;
}

public int getLineNumber() {


return lineNumber;
}

public String getProperty() {


return property;
}

public IRegion getRegion() {


return region;
}

public String getValue() {


return value;
}
}

In JavaPropertiesTree müssen wir nun den Aufbau des Modells für den Tree Viewer
entsprechend anpassen. Zum Abspeichern der PropertyLines an den Knoten wird die in-
nere Klasse Lines eingeführt. Außerdem benötigt man die Methode getAllLinesFrom-
Selection, die sämtliche PropertyLines aller aktuell selektierten Knoten zurückgibt.
Beim Auswerten der Zeilen des Java Properties Editor über reguläre Ausdrücke können wir
mit

String value = m.group(2);

den Wert der Property auslesen.

526
eclipse_v01.book Seite 527 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

package com.entwickler.controls;

...

public class JavaPropertiesTree {


private TreeMap model;
private TreeViewer treeviewer;
private Vector javaPropertiesSelectors;
private HashMap lineNumberMap;

private class Lines {


private String name;
private Vector lines;

public Lines(String name) {


this.name = name;
lines = new Vector();
}

public void addLine(PropertyLine line) {


lines.add(line);
}

public Vector getLines() {


return lines;
}

public String toString() {


return name;
}
}

...

private Map insertInLayer(


String key,
int line,
IRegion region,
Map layer,
String property,
String value) {
Map subLayer;
TreeNode node;
PropertyLine propertyLine = new PropertyLine(line, region,
property, value);
if (layer.containsKey(key)) {
node = (TreeNode) layer.get(key);

527
eclipse_v01.book Seite 528 Montag, 30. Januar 2006 12:02 12

Markers und Annotations

Lines lines = (Lines) node.getValue();


lines.addLine(propertyLine);
subLayer = (Map) node.getChildren();
} else {
subLayer = new TreeMap();
Lines lines = new Lines(key);
lines.addLine(propertyLine);
node = new TreeNode(lines, subLayer);
layer.put(key, node);
}
Vector treeNodes;
Integer lineNum = new Integer(line);
if (lineNumberMap.containsKey(lineNum)) {
treeNodes = (Vector) lineNumberMap.get(lineNum);
} else {
treeNodes = new Vector();
lineNumberMap.put(lineNum, treeNodes);
}
treeNodes.add(node);
return subLayer;
}

public Vector getAllLinesFromSelection() {


IStructuredSelection selection =
(IStructuredSelection) treeviewer.getSelection();
Iterator selIt = selection.iterator();
Vector allLines = new Vector();
while (selIt.hasNext()) {
TreeNode node = (TreeNode) selIt.next();
Lines ln = (Lines) node.getValue();
Vector lines = ln.getLines();
allLines.addAll(lines);
}
return allLines;
}

...

public void parseDocument(IDocument document) {


try {
int numOfLines = document.getNumberOfLines();
String pattern = " *((?:\\w|\\.)+) *= *(.*)";
Pattern p = Pattern.compile(pattern);
model = new TreeMap();
for (int l = 0; l < numOfLines; l++) {
IRegion region = document.getLineInformation(l);

528
eclipse_v01.book Seite 529 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

String line =
document.get(region.getOffset(), region.getLength());
Matcher m = p.matcher(line);
if (m.matches()) {
String property = m.group(1);
String value = m.group(2);
String[] propertyParts = property.split("\\.");
Map layer = model;
for (int i = 0; i < propertyParts.length; i++) {
layer = insertInLayer(propertyParts[i], l,
region, layer, property, value);
}
}
}
treeviewer.setInput(model);
} catch (BadLocationException e) {
ControlsPlugin.getDefault().log(e);
}
}

private class TreeSelectionChangedListener


implements ISelectionChangedListener {
public void selectionChanged(SelectionChangedEvent event) {
if (javaPropertiesSelectors.size() == 0) {
return;
}
IStructuredSelection selection =
(IStructuredSelection) event.getSelection();
if (selection.size() != 1) {
return;
}
Iterator selIt = selection.iterator();
while (selIt.hasNext()) {
TreeNode node = (TreeNode) selIt.next();
Lines ln = (Lines) node.getValue();
Vector lines = ln.getLines();
if (lines.size() != 1) {
return;
}
int num = ((PropertyLine) lines.elementAt(0)).
getLineNumber();
Iterator jpsIt = javaPropertiesSelectors.iterator();
while (jpsIt.hasNext()) {
((IJavaPropertiesSelector) jpsIt.next()).
selectOneLine(num);
}
}

529
eclipse_v01.book Seite 530 Montag, 30. Januar 2006 12:02 12

Markers und Annotations

}
}

// ...

Nun können wir endlich die Marker mit all ihren Attributen aus der Outline View heraus
erzeugen. Den Typ des Markers und seine Attribute melden wir über eine Extension des Ex-
tension Point org.eclipse.core.resources.markers an. Dazu erweitern wir das Manifest des
Plug-Ins com.entwicker.eclipsebuch.javapropertieseditor1:

<extension
id="propertymarker"
name="Property Marker"
point="org.eclipse.core.resources.markers">
<super
type="org.eclipse.core.resource.markers">
</super>
<attribute
name="lineNumber">
</attribute>
<attribute
name="property">
</attribute>
<attribute
name="value">
</attribute>
</extension>

Das Element super erlaubt eine Vererbungshierarchie unter den Markern. So kann man
beim Suchen nach Markern z.B. alle abgeleiteten Marker mitsuchen. Doch bevor wir zum
eigentlichen Erzeugen der Marker kommen, müssen wir uns noch um das Erzeugen der An-
notations für den linken Balken (Vertical Ruler) und den rechten Balken (Overview Ruler)
des Editors kümmern. Für die Annotations müssen wir einen Annotation-Typ über den Ex-
tension Point org.eclipse.ui.editors.annotationTypes definieren. Auch hierfür erweitern wir
das Manifest des Plug-Ins com.entwicker.eclipsebuch.javapropertieseditor2:

1. Damit entsteht die sechste und letzte Version von com.entwickler.eclipsebuch.javapropertieseditor. Man findet
sie auf der CD in dem Verzeichnis Kap_9/javapropertieseditor/v6.
2. Die Backslashes (\) sollen anzeigen, dass es in der nächsten Zeile weitergeht. Sie dürfen nicht eingegeben
werden.

530
eclipse_v01.book Seite 531 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

<extension
point="org.eclipse.ui.editors.annotationTypes">
<type
name="com.entwickler.eclipsebuch.javapropertieseditor.\
annotationtype.propertymarker">
</type>
</extension>

Das Aussehen dieser Annotations (Farbe im Overview Ruler und Icon im Vertical Ruler)
definieren wir über den Extension Point org.eclipse.ui.editors.markerAnnotationSpecifica-
tion innerhalb desselben Manifest1:

<extension
point="org.eclipse.ui.editors.markerAnnotationSpecification">
<specification
colorPreferenceValue="244,200,45"
isGoToPreviousNavigationTargetKey=
"isPropertymarkerGoToPreviousNavigationTarget"
annotationType=
"com.entwickler.javapropertieseditor.\
annotationtype.propertymarker"
showInNextPrevDropdownToolbarAction="true"
verticalRulerPreferenceValue="true"
colorPreferenceKey="propertymarkerIndicationColor"
isGoToNextNavigationTargetKey=
"isPropertymarkerGoToNextNavigationTarget"
showInNextPrevDropdownToolbarActionKey=
"showPropertymarkerInNextPrevDropdownToolbarAction"
overviewRulerPreferenceValue="true"
highlightPreferenceKey="propertymarkerIndicationHighlighting"
presentationLayer="4"
icon="icons/forward_nav.gif"
label="Properties"
textPreferenceKey="propertymarkerIndication"
verticalRulerPreferenceKey=
"propertymarkerIndicationInVerticalRuler"
overviewRulerPreferenceKey=
"propertymarkerIndicationInOverviewRuler">
</specification>
</extension>

1. Die Backslashes (\) sollen anzeigen, dass es in der nächsten Zeile weitergeht. Sie dürfen nicht eingegeben
werden.

531
eclipse_v01.book Seite 532 Montag, 30. Januar 2006 12:02 12

Markers und Annotations

Annotations werden dem so genannten Annotation Model eines Vertical Ruler oder Over-
view Ruler zugewiesen. Referenzen auf den Vertical Ruler und den Overview Ruler sind
aber nur über protected-Methoden von Vaterklassen von JavaPropertiesEditor zu-
gänglich. Da wir aus der Outline View heraus den beiden Rulern Annotations zuweisen
wollen, müssen wir sie über öffentliche Methoden zugänglich machen:

public class JavaPropertiesEditor extends TextEditor {

(...)

public IAnnotationModel getVerticalRulerAnnotationModel() {


return getVerticalRuler().getModel();
}

public IAnnotationModel getOverviewRulerAnnotationModel() {


return getOverviewRuler().getModel();
}

(...)

Jetzt können wir Markers und Annotations erzeugen. Dies geschieht in der Methode
createPropertyMarkers. Das Erzeugen der Markers wird über einen Button im Tool-
bar der Outline View ausgelöst (Action JPECreatePropertyMarkersAction). Außer-
dem können die bestehenden Marker über einen weiteren Button gelöscht werden (Action
JPEDeletePropertyMarkersAction):

package com.entwickler.eclipsebuch.javapropertieseditor;

...

import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.IAnnotationModel;

...

public class JPOAdapter extends ContentOutlinePage {


private JavaPropertiesEditor editor;
private JavaPropertiesTree jpt;

...

532
eclipse_v01.book Seite 533 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

private class JPECreatePropertyMarkersAction extends Action {


public JPECreatePropertyMarkersAction() {
super();
try {
setDescription("Create Markers");
setToolTipText
("Create markers at all selected properties");
ImageDescriptor image =
ImageDescriptor.createFromURL(
new URL(
JPEPlugin.getDefault()
.getBundle.getEntry("/"),
"icons/add_exc.gif"));
setImageDescriptor(image);
} catch (MalformedURLException e) {
JPEPlugin.getDefault().log(e);
}
}

public void run() {


deleteAllPropertyMarkers();
createPropertyMarkers();
}
}

private class JPEDeletePropertyMarkersAction extends Action {


public JPEDeletePropertyMarkersAction() {
super();
try {
setDescription("Delete Markers");
setToolTipText("Delete all markers");
ImageDescriptor image =
ImageDescriptor.createFromURL(
new URL(
JPEPlugin.getDefault()
.getBundle().getEntry("/"),
"icons/removea_exc.gif"));
setImageDescriptor(image);
} catch (MalformedURLException e) {
JPEPlugin.getDefault().log(e);
}
}

public void run() {


deleteAllPropertyMarkers();
}
}

533
eclipse_v01.book Seite 534 Montag, 30. Januar 2006 12:02 12

Markers und Annotations

...

private void deleteAllAnnotations(IAnnotationModel amodel) {


Iterator annotationIt = amodel.getAnnotationIterator();
while (annotationIt.hasNext()) {
Annotation annotation = (Annotation) annotationIt.next();
if (annotation
.getType()
.equals(JPEPlugin.PROPERTYMARKER_ANNOTATION_TYPE)) {
amodel.removeAnnotation(annotation);
}
}
}

public void deleteAllPropertyMarkers() {


try {
IFile file =
((IFileEditorInput) editor.getEditorInput()).getFile();
file.deleteMarkers(
JPEPlugin.PROPERTYMARKER_ID,
true,
IResource.DEPTH_INFINITE);
IAnnotationModel overviewRulerModel =
editor.getOverviewRulerAnnotationModel();
IAnnotationModel verticalRulerModel =
editor.getVerticalRulerAnnotationModel();
deleteAllAnnotations(overviewRulerModel);
deleteAllAnnotations(verticalRulerModel);
} catch (CoreException e) {
JPEPlugin.getDefault().log(e);
}
}

/**
* Erzeuge Property Markers für alle gerade im
* Java Properties Editor markierten Properties.
*/
public void createPropertyMarkers() {
// Hole Property Lines aller selektierten Properties
Vector lines = jpt.getAllLinesFromSelection();
Iterator numIt = lines.iterator();
IFile file =
((IFileEditorInput) editor.getEditorInput()).getFile();
IAnnotationModel overviewRulerModel =
editor.getOverviewRulerAnnotationModel();
IAnnotationModel verticalRulerModel =
editor.getVerticalRulerAnnotationModel();

534
eclipse_v01.book Seite 535 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

try {
while (numIt.hasNext()) {
// erzeuge einen Marker an der Ressource
IMarker marker =
file.createMarker(JPEPlugin.PROPERTYMARKER_ID);
PropertyLine pl = (PropertyLine) numIt.next();
String property = pl.getProperty();
String value = pl.getValue();
IRegion region = pl.getRegion();
int line = pl.getLineNumber();
marker.setAttribute(
JPEPlugin.PROPERTYMARKER_ATTRIBUTE_LINE,
line + 1);
marker.setAttribute(
JPEPlugin.PROPERTYMARKER_ATTRIBUTE_PROPERTY,
property);
marker.setAttribute(
JPEPlugin.PROPERTYMARKER_ATTRIBUTE_VALUE,
value);

/*
* Erzeuge eine Annotation und weise
* sie dem Vertical Ruler und dem
* Overview Ruler zu.
*/
Annotation a =
new Annotation(
JPEPlugin.PROPERTYMARKER_ANNOTATION_TYPE,
true,
property);
Position position =
new Position(region.getOffset(), region.getLength());
overviewRulerModel.addAnnotation(a, position);
verticalRulerModel.addAnnotation(a, position);
}
} catch (CoreException e) {
JPEPlugin.getDefault().log(e);
}
}

public void createControl(Composite parent) {


super.createControl(parent);
jpt = new JavaPropertiesTree(getTreeViewer(), getDocument());
jpt.addJavaPropertiesSelector(new JPOSelector());

IToolBarManager toolBarManager =
getSite().getActionBars().getToolBarManager();

535
eclipse_v01.book Seite 536 Montag, 30. Januar 2006 12:02 12

Markers und Annotations

toolBarManager.add(new JPELinkAction());
try {
toolBarManager.add(new JPEToggleLinkAction());
} catch (MalformedURLException e) {
JPEPlugin.getDefault().log(e);
}
toolBarManager.add(new JPERefreshAction());
toolBarManager.add(new JPECollapseAllAction());
toolBarManager.add(new JPECreatePropertyMarkersAction());
toolBarManager.add(new JPEDeletePropertyMarkersAction());
}

...

Die Konstanten für Typ und Attribute der Marker sind in der Plug-In-Klasse JPEPlugin
definiert1:

public class JPEPlugin extends AbstractUIPlugin {


public static final String PLUGIN_ID =
"com.entwickler.javapropertieseditor";
public static final String PROPERTYMARKER_ID =
PLUGIN_ID + ".propertymarker";
public static final String PROPERTYMARKER_ATTRIBUTE_LINE =
"lineNumber";
public static final String PROPERTYMARKER_ATTRIBUTE_PROPERTY =
"property";
public static final String PROPERTYMARKER_ATTRIBUTE_VALUE =
"value";
public static final String PROPERTYMARKER_ANNOTATION_TYPE =
"com.entwickler.javapropertieseditor.\
annotationtype.propertymarker";
...

Nun können wir die Eclipse Application starten und in der Outline View des Java Properties
Editor Marker zu einer Property erzeugen (siehe Abb. 9.39).

1. Die Backslashes (\) sollen anzeigen, dass es in der nächsten Zeile weitergeht. Sie dürfen nicht eingegeben
werden.

536
eclipse_v01.book Seite 537 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

Abb. 9.39: Erzeugen von Markern

9.10 OSGi
Während mit Eclipse 2.1 Plug-Ins nicht zur Laufzeit dynamisch nachgeladen werden
konnten, war dies bereits für Eclipse 3.0 geplant. So existiert folgender Eintrag im Bugzilla
(Bug/Anforderung Nummer 37687)1 vom 15. Mai 2003:
„Support adding and removing plug-ins dynamically. Installation and configuration of
features and plug-ins currently only happens during Eclipse Platform startup. The plug-in
registry should be made dynamic so that features and plug-ins can be added or removed
without necessarily having to restart Eclipse. This will also entail adding mechanisms for
handling the arrival and departure of extensions and extension points. Plug-In developers
will likely require additional support from PDE in writing and debugging well-behaved
dynamic plug-ins.“
Am 22. Mai 2003 wurde er wie folgt erweitert:
„Additional mechanisms such as services will be added to support the dynamic pro-
gramming model. Alternative runtimes (e.g., OSGi) which offer explicit support for dyna-
mic components will also be investigated and used as appropriate.“
OSGi steht für Open Service Gateway Initiative und ist eine Spezifikation für eine offene
Plattform zur Bereitstellung von Komponenten und Diensten in Endgeräten2. Endgeräte
können Handys, PDAs, PCs usw. sein. In einem OSGi-kompatiblen Framework kann man
so genannte Bundles und Services laden und ausführen. Bundles haben dabei starke Ähn-

1. Bugzilla ist das System, mit dem das Eclipse-Team Fehler und Anforderungen verwaltet
(http://bugs.eclipse.org).
2. Genau genommen ist OSGi das Gremium, das diese Spezifikation verabschiedet hat: http:\\www.osgi.org.
Eine Spezifikation kann man sich unter http://www.osgi.org/resources/spec_download.asp herunterladen.

537
eclipse_v01.book Seite 538 Montag, 30. Januar 2006 12:02 12

OSGi

lichkeiten mit Eclipse Plug-Ins. Aus diesem Grund sollte die Verwendung dieses Standards
auch untersucht werden. Das Eclipse Team hat OSGi nicht nur untersucht, sondern den
kompletten Laufzeitkern (Eclipse Runtime) umgeschrieben. Ab 3.0 ist Eclipse ein OSGi-
kompatibles Framework. Am 15. April 2004 wurde Vollzug gemeldet:
„We have done all the work we will for 3.0. The main target was to enable dynamic addition
of plugins for RCP1 scenarios. This has been accomplished. Enabling dynamic removal is
mechanically supported in the new runtime but complete (systemic) support requires the co-
operation and efforts of all plugin writers. This will be approached in an incremental way
over future releases.“
Wir werden uns nun OSGi Bundles und Services genauer ansehen.

9.10.1 Bundles
Mit einem Bundle werden in einem OSGi Framework Anwendungen installiert und ausge-
führt. Ein Bundle besteht aus dem ausführbaren Code und einem Bundle Manifest mit Na-
men MANIFEST.MF. Ein Eclipse-Plug-In entspricht weitgehend einem Bundle, mit dem
Unterschied, dass das Plug-In Manifest plugin.xml einen anderen Aufbau hat als ein OSGi
Manifest. Während plugin.xml im XML-Format vorliegt, hat MANIFEST.MF den folgen-
den Aufbau:

Schlüssel: Wert

Auch enthält plugin.xml mehr Informationen als MANIFEST.MF, denn Extension und Ex-
tension Points kennt OSGi nicht. Eclipse 3.1 bietet die Möglichkeit, Plug-Ins zu erstellen,
die zum OSGi-Standard kompatibel sind. Dabei werden die Informationen in der Datei
MANIFEST.MF gehalten. Nur Informationen zu Extensions und Extension Points werden
noch in plugin.xml gehalten.
Aber auch gewöhnliche Plug-Ins kann man wie Bundles behandeln. Um dies zu veran-
schaulichen, werden wir uns eine Bundle View erstellen. Sie soll alle vorhandenen Bundles
und Informationen zu diesen Bundles anzeigen. Das dafür notwendige Plug-In erstellen wir
im OSGi-kompatiblen Format.
Wir erstellen ein leeres Plug-In mit Namen com.entwickler.eclipsebuch.osgiviews im
OSGi-Format, indem wir im Wizard für Plug-In-Projekte den Haken bei CREATE AN OSGI
BUNDLE MANIFEST stehen lassen2. Nun wird keine Datei mit Namen plugin.xml, dafür aber
eine Datei MANIFEST.MF (im Verzeichnis META-INF) erzeugt.
Auf der Seite MANIFEST.MF im Plug-In Manifest Editor sehen wir, dass sämtliche Infor-
mation im Bundle Manifest gehalten wird:

1. RCP steht für die Rich Client Platform. Mit der RCP dient Eclipse als Plattform für beliebige Anwendungen.
2. Das Plug-In com.entwickler.eclipsebuch.osgiviews findet man auf der CD im Verzeichnis Kap_9/osgiviews.
Es enthält alle nachfolgend erstellten Views.

538
eclipse_v01.book Seite 539 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Osgiviews Plug-In
Bundle-SymbolicName: com.entwickler.eclipsebuch.osgiviews;
singleton:=true
Bundle-Version: 1.0.0
Bundle-Activator:
com.entwickler.eclipsebuch.osgiviews.OsgiviewsPlugin
Bundle-Vendor: ENTWICKLER
Bundle-Localization: plugin
Require-Bundle: org.eclipse.ui,
org.eclipse.core.runtime
Eclipse-AutoStart: true

Eine Datei namens plugin.xml existiert dagegen nicht.


Um alle Bundles auslesen zu können, benötigen wir einen Bundle Context (org.osgi.
framework.BundleContext). Der Bundle Context wird dem Bundle Activator (org.
osgi.framework.BundleActivator) beim Start eines Bundle übergeben. Der Bundle
Activator ist nichts anderes als die uns bereits bekannte Plug-In-Klasse, hier com.ent-
wickler.eclipsebuch.osgiviews.OsgiviewsPlugin. Wir merken uns den Bundle
Context und geben ihn über die Methode getBundleContext nach außen bekannt. Au-
ßerdem spendieren wir der Plug-In-Klasse eine Fehler-Log-Methode (log):

package com.entwickler.eclipsebuch.osgiviews;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;

public class OsgiviewsPlugin extends AbstractUIPlugin {


public static final String PLUGIN_ID =
"com.entwickler.eclipsebuch.osgiviews";
private BundleContext bundleContext;
(...)

/**
* This method is called upon plug-in activation
*/
public void start(BundleContext context) throws Exception {
super.start(context);
bundleContext = context;
}

539
eclipse_v01.book Seite 540 Montag, 30. Januar 2006 12:02 12

OSGi

public void log(Throwable e) {


IStatus status =
new Status(IStatus.ERROR, PLUGIN_ID, -1, "Exception", e);
getLog().log(status);
}
}

Die Plug-In-Klasse erscheint im Bundle Manifest als Bundle Activator:

Bundle-Activator:
com.entwickler.eclipsebuch.osgiviews.OsgiviewsPlugin

Die Klasse org.eclipse.ui.plugin.AbstractUIPlugin implementiert das Inter-


face BundleActivator. Über die Instanz der Klasse BundleContext, die wir als Para-
meter der Methode start bekommen, können wir alle Bundles auslesen. Dazu benutzt
man die Methode getBundles, die ein Array von Objekten zurückliefert, die das Interface
Bundle implementieren. Über Bundle kann man nun die ID (getBundleID), den Zu-
stand (getState), den Ort (getLocation) und die Zeilen des Manifests (getHeader) er-
fragen.
Ein Bundle kann sich in folgenden Zuständen befinden:
쐌 INSTALLED (2)
Bundle ist installiert.
쐌 RESOLVED (4)
Abhängigkeiten zu anderen Bundles konnten aufgelöst werden.
쐌 STARTING (8)
Bundle startet gerade.
쐌 ACTIVE (32)
Bundle ist gestartet.
쐌 STOPPING (16)
Bundle stoppt gerade.
Wir erzeugen eine Bundle View (com.entwickler.eclipsebuch.osgiviews.views.
BundleView) mit einem Tree Viewer, der alle Informationen sämtlicher Bundles anzeigt1:

package com.entwickler.eclipsebuch.osgiviews.views;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.Dictionary;
import java.util.Enumeration;

1. Zusätzlich zum Tree Viewer enthalten alle nachfolgenden Views noch eine Refresh Action, um die View neu
aufzubauen.

540
eclipse_v01.book Seite 541 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

import java.util.Vector;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.part.ViewPart;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;

public class BundleView extends ViewPart {


TreeViewer treeViewer;

private class BundleViewContentProvider


implements ITreeContentProvider {

public Object[] getChildren(Object parentElement) {


try {
if (parentElement instanceof Bundle) {
Bundle bundle = (Bundle) parentElement;
return new Object[] {
"ID : " + Long.toString(bundle.getBundleId()),
"State : " + Integer.toString(bundle.getState()),
bundle.getLocation(),
bundle.getHeaders()
};
}
/*
* getHeader gibt ein java.lang.Dictionary zurueck
*/
else if(parentElement instanceof Dictionary) {
Dictionary dict = (Dictionary) parentElement;
Enumeration dictEnum = dict.keys();
Vector keyValPairs = new Vector();
while(dictEnum.hasMoreElements()) {
Object key = dictEnum.nextElement();
Object val = dict.get(key);
keyValPairs.add(key + " : " + val);
}
return keyValPairs.toArray();
}
} catch (Exception e) {
OsgiviewsPlugin.getDefault().log(e);
}

541
eclipse_v01.book Seite 542 Montag, 30. Januar 2006 12:02 12

OSGi

return null;
}

public Object getParent(Object element) {


return null;
}

public boolean hasChildren(Object element) {


return (getChildren(element) != null);
}

public Object[] getElements(Object inputElement) {


try {
if (inputElement instanceof BundleContext) {
// lese alle Bundles aus
Bundle[] bundles = ((BundleContext) inputElement).
getBundles();
return bundles;
}
} catch (Exception e) {
OsgiviewsPlugin.getDefault().log(e);
}
return null;
}

public void dispose() {


}

public void inputChanged(Viewer viewer,


Object oldInput, Object newInput) {
}

private class BundleViewLabelProvider extends LabelProvider {

public String getText(Object element) {


try {
if (element instanceof Bundle) {
Bundle bundle = (Bundle) element;
return bundle.getSymbolicName();
}
if(element instanceof Dictionary) {
return "Headers";
}
} catch (Exception e) {
OsgiviewsPlugin.getDefault().log(e);
}

542
eclipse_v01.book Seite 543 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

return element.toString();
}
}

private class BundleViewRefreshAction extends Action {

public BundleViewRefreshAction() {
try {
setDescription("Refresh");
setToolTipText("Refresh Bundle View");
ImageDescriptor image =
ImageDescriptor.createFromURL(
new URL(
OsgiviewsPlugin.getDefault().
getBundle().getEntry("/"),
"icons/refresh_nav.gif"));
setImageDescriptor(image);
} catch (MalformedURLException e) {
OsgiviewsPlugin.getDefault().log(e);
}
}

public void run() {


update();
}
}

/*
* erzeuge den Tree Viewer
*/
public void createPartControl(Composite parent) {
try {
treeViewer =
new TreeViewer(parent,
SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
treeViewer.setContentProvider(
new BundleViewContentProvider());
treeViewer.setLabelProvider(new BundleViewLabelProvider());
treeViewer.setInput(
OsgiviewsPlugin.getDefault().getBundleContext());
IToolBarManager toolbar =
getViewSite().getActionBars().getToolBarManager();
toolbar.add(new BundleViewRefreshAction());
} catch (Exception e) {
OsgiviewsPlugin.getDefault().log(e);
}
}

543
eclipse_v01.book Seite 544 Montag, 30. Januar 2006 12:02 12

OSGi

private void update() {


treeViewer.refresh();
}

public void setFocus() {


update();
}
}

Nun erstellen wir mit dem Plug-In Manifest Editor eine Extension für den Extension Point
org.eclipse.ui.views. Die Extension deklariert eine View (Klasse com.entwickler.eclipse-
buch.osgiviews.BundleView) mit Namen Bundle View in der Kategorie OSGi. Dabei ent-
steht die Datei plugin.xml mit dem nachfolgenden Inhalt:

<plugin>
<extension
point="org.eclipse.ui.views">
<category
name="OSGi"
id="com.entwickler.eclipsebuch.osgiviews">
</category>
<view
name="Bundle View"
icon="icons/default_co.gif"
category="com.entwickler.eclipsebuch.osgiviews"
class="com.entwickler.eclipsebuch.osgiviews.views.\
BundleView"
id="com.entwickler.eclipsebuch.osgiviews.views.BundleView">
</view>
</extension>
</plugin>

Nun können wir die Eclipse Application starten und die Bundle View öffnen (siehe Abb.
9.40).
Dabei sehen wir, dass auch bei Plug-Ins im herkömmlichen Format die Zeilen des Bundle
Manifest angezeigt werden, obwohl hier MANIFEST.MF nicht existiert. Eclipse holt sich
die Informationen aus plugin.xml und simuliert das Vorhandensein von MANIFEST.MF.
Am Java Properties Editor können wir den Zustandsübergang eines Bundle beobachten.
Bevor der Java Properties Editor zum ersten Mal geöffnet worden ist, ist das Bundle (Plug-
In) com.entwickler.eclipsebuch.javapropertieseditor im Zustand 4, d.h. RE-
SOLVED (siehe Abb. 9.40). Öffnen wir nun den Java Properties Editor, so geht das Bundle
in den Zustand ACTIVE (32) über (siehe Abb. 9.41).

544
eclipse_v01.book Seite 545 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

Abb. 9.40: Die Bundle View (Bundle javapropertieseditor ist RESOLVED)

Abb. 9.41: Bundle javapropertieseditor ist ACTIVE

9.10.2 Services
Außer Bundles bietet ein OSGi Framework auch Services. Ein Service wird durch ein
Bundle implementiert und kann über ein Interface aufgerufen werden. Welche Services zur
Verfügung stehen, kann man wieder über den Bundle Context erfragen. Dabei erhält man
zunächst Referenzen auf Services (org.osgi.framework.ServiceReference). Über

545
eclipse_v01.book Seite 546 Montag, 30. Januar 2006 12:02 12

OSGi

eine Service-Referenz kann man vom Bundle Context den eigentlichen Service erhalten
(Methode getService). Außerdem lassen sich über eine Service-Referenz die Eigen-
schaften eines Service erfragen (Methoden getPropertyKeys und getProperty).
Wir erstellen eine weitere View (com.entwickler.eclipsebuch.osgiviews.Ser-
viceView), die alle vorhandenen Services mit ihren Eigenschaften anzeigt:

package com.entwickler.eclipsebuch.osgiviews.views;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.Vector;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.part.ViewPart;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;

public class ServiceView extends ViewPart {

TreeViewer treeViewer;
BundleContext bundleContext = null;

private class ServiceViewContentProvider


implements ITreeContentProvider {

public Object[] getChildren(


Object parentElement) {
try {
if (parentElement instanceof ServiceReference) {
ServiceReference sref =
(ServiceReference) parentElement;
String[] keys = sref.getPropertyKeys();
Vector keyValPairs = new Vector();
for (int i = 0; i < keys.length; i++) {
Object val = sref
.getProperty(keys[i]);
/*
* Die Eigenschaft objectClass ist vom Typ
* String[]
*/

546
eclipse_v01.book Seite 547 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

if (val.getClass().isArray()) {
Object[] arr = (Object[]) val;
for (int j = 0; j < arr.length; j++) {
keyValPairs.add(keys[i] + "[" + j
+ "] : " + arr[j]);
}
} else {
keyValPairs.add(keys[i] + " : "
+ val.toString());
}
}
return keyValPairs.toArray();
}
} catch (Exception e) {
OsgiviewsPlugin.getDefault().log(e);
}
return null;
}

public Object getParent(Object element) {


return null;
}

public boolean hasChildren(Object element) {


return (getChildren(element) != null);
}

public Object[] getElements(


Object inputElement) {
try {
if (inputElement instanceof BundleContext) {
/*
* Hole ein Array von Referenzen aller Services
*/
ServiceReference[] srefs = bundleContext
.getServiceReferences(null, null);
return srefs;
}
} catch (Exception e) {
OsgiviewsPlugin.getDefault().log(e);
}
return null;
}

public void dispose() {


}

547
eclipse_v01.book Seite 548 Montag, 30. Januar 2006 12:02 12

OSGi

public void inputChanged(Viewer viewer,


Object oldInput, Object newInput) {
}

private class ServiceViewRefreshAction extends


Action {

public ServiceViewRefreshAction() {
try {
setDescription("Refresh");
setToolTipText("Refresh Service View");
ImageDescriptor image = ImageDescriptor
.createFromURL(new URL(
OsgiviewsPlugin.getDefault()
.getBundle().getEntry("/"),
"icons/refresh_nav.gif"));
setImageDescriptor(image);
} catch (MalformedURLException e) {
OsgiviewsPlugin.getDefault().log(e);
}
}

public void run() {


update();
}
}

public void createPartControl(Composite parent) {


try {
treeViewer = new TreeViewer(parent,
SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
treeViewer
.setContentProvider(new ServiceViewContentProvider());
/*
* Da der Bundle Context auch im Content Provider
* benoetigt wird, wird er in einer Instanz-Variablen
* abgelegt
*/
bundleContext = OsgiviewsPlugin
.getDefault().getBundleContext();
treeViewer.setInput(bundleContext);
IToolBarManager toolbar = getViewSite()
.getActionBars().getToolBarManager();
toolbar.add(new ServiceViewRefreshAction());
} catch (Exception e) {

548
eclipse_v01.book Seite 549 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

OsgiviewsPlugin.getDefault().log(e);
}
}

private void update() {


treeViewer.refresh();
}

public void setFocus() {


update();
}
}

In plugin.xml müssen wir eine Extension für diese View erzeugen:

<plugin>
<extension
point="org.eclipse.ui.views">
<category
name="OSGi"
id="com.entwickler.eclipsebuch.osgiviews">
</category>
<view
name="Bundle View"
icon="icons/default_co.gif"
category="com.entwickler.eclipsebuch.osgiviews"
class="com.entwickler.eclipsebuch.osgiviews.views.\
BundleView"
id="com.entwickler.eclipsebuch.osgiviews.views.BundleView">
</view>
<view
category="com.entwickler.eclipsebuch.osgiviews"
class="com.entwickler.eclipsebuch.osgiviews.views.\
ServiceView"
icon="icons/default_co.gif"
id="com.entwickler.eclipsebuch.osgiviews.view2"
name="Service View"/>
</extension>
</plugin>

Nun können wir wieder die Eclipse Application starten und die Service View öffnen (siehe
Abb. 9.42).

549
eclipse_v01.book Seite 550 Montag, 30. Januar 2006 12:02 12

OSGi

Abb. 9.42: Die Service View

9.10.3 Die Benutzung eines Service


Zum Schluss wollen wir einen Service beispielhaft benutzen. Dazu nehmen wir den Service
EnvironmentInfo, über den man Information zur Umgebung, in der Eclipse läuft, er-
halten kann. Dazu wollen wir uns eine Environment Service View (com.entwickler.
eclipsebuch.osgiviews.EnvironmentServiceView) erstellen, die alle Informatio-
nen anzeigt, die dieser Service liefert.
In der Eigenschaft objectClass eines Service sehen wir alle Interfaces, über die der je-
weilige Service angesprochen werden kann (siehe Abb. 9.42). Über dieses Interface kann
man auch vom Bundle Context die Service-Referenz erhalten. Wir besorgen uns eine Re-
ferenz auf den Service EnvironmentInfo:

ServiceReference envInfoRef = bundleContext.


getServiceReference(EnvironmentInfo.class.getName());

Mit der Referenz besorgen wir uns den eigentlichen Service:

EnvironmentInfo envInfo = (EnvironmentInfo)


bundleContext.getService(envInfoRef);

An envInfo können wir nun die Informationen abfragen:

String[] commandLineArgs = envInfo.getCommandLineArgs()

550
eclipse_v01.book Seite 551 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

In der Environment Service View zeigen wir alle Informationen in einem Tree Viewer an:

package com.entwickler.eclipsebuch.osgiviews.views;

import java.net.MalformedURLException;
import java.net.URL;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.osgi.service.environment.EnvironmentInfo;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.part.ViewPart;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;

public class EnvironmentServiceView extends ViewPart {

TreeViewer treeViewer;
EnvironmentInfo envInfo = null;

private class EnvironmentServceViewContentProvider


implements ITreeContentProvider {

public Object[] getChildren(Object parentElement) {


try {
if (parentElement instanceof String) {
String val = (String) parentElement;
if (val.equals("CommandLineArgs")) {
return envInfo.getCommandLineArgs();
} else if (val.equals("FramworkArgs")) {
return envInfo.getFrameworkArgs();
} else if (val.equals("NonFrameworkArgs")) {
return envInfo.getNonFrameworkArgs();
} else if (val.equals("NL")) {
return new String[] { envInfo.getNL()};
} else if (val.equals("OS")) {
return new String[] { envInfo.getOS()};
} else if (val.equals("OSArch")) {
return new String[] { envInfo.getWS()};
} else if (val.equals("WS")) {
return new String[] { envInfo.getWS()};
} else if (val.equals("DebugMode")) {
return new String[] { Boolean.toString(envInfo
.inDebugMode())};

551
eclipse_v01.book Seite 552 Montag, 30. Januar 2006 12:02 12

OSGi

} else if (val.equals("DevelopmentMode")) {
return new String[] { Boolean
.toString(envInfo.inDevelopmentMode())};
}
}
} catch (Exception e) {
OsgiviewsPlugin.getDefault().log(e);
}
return null;
}

public Object getParent(Object element) {


return null;
}

public boolean hasChildren(Object element) {


return (getChildren(element) != null);
}

public Object[] getElements(Object inputElement) {


try {
if (inputElement instanceof EnvironmentInfo) {
return new String[] {
"CommandLineArgs", "FramworkArgs",
"NonFrameworkArgs", "NL", "OS", "OSArch", "WS",
"DebugMode", "DevelopmentMode"}; }
} catch (Exception e) {
OsgiviewsPlugin.getDefault().log(e);
}
return null;
}

public void dispose() {


}

public void inputChanged(Viewer viewer,


Object oldInput, Object newInput) {
}

private class EnvironmentServiceViewRefreshAction


extends Action {

public EnvironmentServiceViewRefreshAction() {
try {
setDescription("Refresh");
setToolTipText("Refresh Environment Service View");

552
eclipse_v01.book Seite 553 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

ImageDescriptor image = ImageDescriptor


.createFromURL(new URL(OsgiviewsPlugin
.getDefault().getBundle().getEntry("/"),
"icons/refresh_nav.gif"));
setImageDescriptor(image);
} catch (MalformedURLException e) {
OsgiviewsPlugin.getDefault().log(e);
}
}

public void run() {


update();
}
}

public void createPartControl(Composite parent) {


try {
treeViewer = new TreeViewer(parent, SWT.MULTI
| SWT.H_SCROLL | SWT.V_SCROLL);
treeViewer.setContentProvider(
new EnvironmentServceViewContentProvider());
BundleContext bundleContext = OsgiviewsPlugin
.getDefault().getBundleContext();
ServiceReference envInfoRef = bundleContext
.getServiceReference(EnvironmentInfo.class.getName());
envInfo = (EnvironmentInfo) bundleContext
.getService(envInfoRef);
treeViewer.setInput(envInfo);
IToolBarManager toolbar = getViewSite()
.getActionBars().getToolBarManager();
toolbar.add(new EnvironmentServiceViewRefreshAction());
} catch (Exception e) {
OsgiviewsPlugin.getDefault().log(e);
}
}

private void update() {


treeViewer.refresh();
}

public void setFocus() {


update();
}
}

553
eclipse_v01.book Seite 554 Montag, 30. Januar 2006 12:02 12

OSGi

Auch für diese View benötigen wir eine Extension in plugin.xml1:

<plugin>
<extension
point="org.eclipse.ui.views">
(...)
<view
category="com.entwickler.eclipsebuch.osgiviews"
class="com.entwickler.eclipsebuch.osgiviews.views.\
ServiceView"
icon="icons/default_co.gif"
id="com.entwickler.eclipsebuch.osgiviews.view2"
name="Service View"/>
<view
category="com.entwickler.eclipsebuch.osgiviews"
class="com.entwickler.eclipsebuch.osgiviews.views.\
EnvironmentServiceView"
icon="icons/default_co.gif"
id="com.entwickler.eclipsebuch.osgiviews.view3"
name="Environment Service View"/>
</extension>
</plugin>

Nun starten wir die Eclipse Application und öffnen die Environment Service View (siehe
Abb. 9.43).

Abb. 9.43: Die Environment Service View

1. Die Backslashes (\) sollen anzeigen, dass es in der nächsten Zeile weiter geht. Sie dürfen nicht eingegeben werden.

554
eclipse_v01.book Seite 555 Montag, 30. Januar 2006 12:02 12

9 – Plug-In-Entwicklung für die Eclipse Workbench

9.11 Fazit und Ausblick


In diesem Kapitel habe wir vielen Techniken von Eclipse kennen gelernt und in unserem
Plug-In-Programmier-Praktikum eingesetzt:
쐌 Die dynamische Erweiterung von Klassen über Adapter (IAdaptable)
쐌 Das Implementieren von Adaptern für fremde Klassen über den Adapter Manager
(IAdapterManager)
쐌 Das Erstellen einer Adapter Factory (IAdapterFactory) für den Adapter Manager
쐌 Die hierarchische Darstellung von geordneten Informationen in einem Tree Viewer
(TreeViewer) mit Content Provider (ITreeContentProvider) und Label Provider
(ILabelProvider)
쐌 Das Reagieren auf Änderungen der Selektion eines Tree Viewer mit einem Selection
Changed Listener (ISelectionChangedListener)
쐌 Die Darstellung von zusätzlichen Informationen für einen Editor mithilfe einer Seite der
Outline View (IContentOutlinePage)
쐌 Die Erstellung eines Adapters für IContentOutlinePage
쐌 Die Erstellung von Extensions für einen Extension Point und die Definition einer Ex-
tension im Plug-In Manifest (plugin.xml)
쐌 Das Importieren eines Plug-Ins als Binary Project oder Source Project in den Work-
space
쐌 Die Erstellung eines eigenen Editors durch Ableiten von der Klasse TextEditor
쐌 Das Anmelden eines neuen Editors über den Extension Point org.eclipse.ui.editors
쐌 Das Reagieren auf die Änderung der Cursorposition in einem Editor (AbstractText-
Editor.handleCursorPositionChanged)
쐌 Das Selektieren von Text in einem Texteditor (TextEditor.selectAndReveal,
TextEditor.setHighlightRange)
쐌 Das Debuggen von Plug-Ins über eine Run-time Workbench
쐌 Die Plug-In-Klasse (AbstractUIPlugin)
쐌 Das Protokollieren von Fehlermeldungen eines Plug-Ins über die Plug-In-Klasse (Me-
thode getLog)
쐌 Die Aktivierung von Plug-Ins (Methode startup)
쐌 Das Aktivieren eines Plug-In beim Starten von Eclipse (Extension Point org.eclipse.
ui.startup)
쐌 Das XML-Schema eines Extension Point (*.exsd)
쐌 Die Definition eines Extension Point über den Plug-In Manifest Editor und den XML
Schema Editor
쐌 Die Implementierung eines Extension Point und das Auslesen aller Extensions für die-
sen Extension Point über die Plug-In Registry (IPluginRegistry.getConfigura-
tionElementsFor)
쐌 Die Instanziierung einer Klasse, die über eine Extension übergeben wird (IConfigu-
rationElement.createExecutableExtension)
쐌 Der Aufbau der Eclipse Workbench über Workbench (IWorkbench), Workbench Win-
dows (IWorkbenchWindow) und Workbench Page (IWorkbenchPage), View-Refe-
renzen (IViewReference) und Editor-Referenzen (IEditorReference), View Parts

555
eclipse_v01.book Seite 556 Montag, 30. Januar 2006 12:02 12

Fazit und Ausblick

(IViewPart) und Editor Parts (IEditorPart), View Sites (IViewSite) und Editor
Sites (IEditorSite), Action Bars (IActionBar), Toolbar Manager (IToolBar-
Manager), Menu Manager (IMenuManage) und Status Line Manager (IStatusLine-
Manager)
쐌 Das Anmelden einer neuen View über den Extension Point org.eclipse.ui.views
쐌 Die Implementierung von Buttons, Menüpunkten etc. über Actions (IAction), Contri-
bution Items (IContributionItem) und Action Delegates (IActionDelegate)
쐌 Das deklarative Aktivieren von Action Delegates innerhab einer Extension für den Ex-
tension Point org.eclipse.ui.viewActions (Element enablement)
쐌 Das Abspeichern von Informationen an Ressourcen der Workbench (Methoden
getSessionProperty, getPersistentProperty, setSessionProperty, set-
PersistentProperty von IResource)
쐌 Die mehrseitigen Page Book Views (PageBookView)
쐌 Das Abspeichern und die Darstellung von Informationen an Ressourcen über Marker
(IResource.createMarker)
쐌 Das Abspeichern und die Darstellung von Informationen im Overview Ruler und im
Vertical Ruler eines Texteditors über Annotations (IAnnotationModel.addAnnota-
tion)
쐌 OSGi Bundles und Services
Doch wie in der Einleitung schon angedeutet, konnte dieses Plug-In-Programmier-Prakti-
kum keine umfassende Referenz bieten. So mussten einige Eclipse-Techniken unberück-
sichtigt bleiben. Zum Beispiel:
쐌 Das Deployment von Plug-Ins über den Export Wizard (EXPORT | DEPLOYABLE PLUG-INS
AND FRAGMENTS im Kontextmenü eines Plug-In-Projekts)
쐌 Das Erzeugen von eigenen Perspektiven über den Extension Point org.eclipse.ui.per-
spectiveExtensions
쐌 Das Arbeiten mit Ressourcen wie Projekten (IProject), Dateien (IFile) oder Ver-
zeichnissen (IFolder)1
쐌 Das Einbinden von Online-Hilfe über den Extension Point org.eclipse.help.toc
쐌 Die Kommunikation zwischen einer View und der Properties View über das Interface
IPropertySource
쐌 Das Reagieren auf Änderungen in einer View über das Interface ISelectionListe-
ner
쐌 Das Anzeigen von Informationen in Tabellen über einen Table Viewer (TableViewer)2
Doch wir sind jetzt weit genug mit Eclipse vertraut, so dass wir uns diese Techniken über
die Eclipse-Online-Hilfe aneignen können.
Im nächsten Kapitel werden wir uns mit Extensions zu dem Java Development Toolkit
(JDT) befassen.

1. Wir werden es im nächsten Kapitel kennen lernen.


2. Analog dem Tree Viewer

556
eclipse_v01.book Seite 557 Montag, 30. Januar 2006 12:02 12

10 Plug-In-Entwicklung für das JDT

von Oliver Widder

Im vorangegangenen Kapitel haben wir die Entwicklung von Plug-Ins für die Eclipse
Workbench mit dem PDE kennen gelernt. Nun wollen wir mit Plug-Ins Java Code analy-
sieren und erzeugen. Dazu werden wir folgende Plug-Ins entwickeln1:
쐌 Ein Plug-In (com.entwickler.eclipsebuch.soapDeployer), das eine Java-Klasse als einen
Soap Web Service unter der Soap-Implementierung Axis2 von Apache installiert.
쐌 Ein Plug-In (com.entwickler.eclipsebuch.soapClientGenerator), das zu jedem Web-Ser-
vice einen kleinen SWT-basierten Test-Client generiert.
쐌 Ein Plug-In (com.entwickler.eclipsebuch.AstView), mit dem man sich den Syntaxbaum
von Java Files ansehen kann.

10.1 Soap
Soap ist ein auf XML basierendes Protokoll. Es existiert in zwei Varianten: Soap Document
und Soap RPC.
In der ersten Variante können XML-Dokumente über das Protokoll übertragen werden. Mit
der zweiten Variante kann eine Funktion (ein Service) mit beliebigen Parametern über eine
Soap-Nachricht aufgerufen und das Ergebnis wieder zurückgegeben werden. Soap Docu-
ment wird von der W3C, der Organisation, die Soap standardisiert, empfohlen. Aus diesem
Grund wollen wir uns in diesem Kapitel auch nur mit Soap Document beschäftigen.
Wie und in welcher Programmiersprache der Service implementiert ist, spielt für den Auf-
rufer des Service keine Rolle. Denn Soap-Nachrichten werden in textueller Form über klas-
sische IP-basierte Protokolle wie HTTP oder FTP verschickt. In den meisten Fällen kommt
das HTTP-Protokoll zum Einsatz. Soap-Nachrichten können aber auch über SMTP (Mail),
FTP oder ein asynchrones Message-Protokoll wie MQSeries verschickt werden.
Im Folgenden ist die Kommunikation eines Soap Clients mit einem Soap Server abgebildet.
Beide werden wir im Laufe dieses Kapitels mit unseren neuen Plug-Ins entwickeln.
Der Web Service bekommt eine Zeichenkette übergeben und liefert sie mit einem vorange-
stellten »Hallo« zurück:

1. Die Plug-Ins in diesem Kapitel nutzen die neuen Fähigkeiten von Java 5. Aus diesem Grund sollte die JDK
Compliance (PROPERTIES | JAVA COMPILER) auf 5.0 gesetzt werden.
2. Wir werden Axis2 in der Version 0.91 verwenden.

557
eclipse_v01.book Seite 558 Montag, 30. Januar 2006 12:02 12

Soap

POST /axis2/services/HelloService/sayHello HTTP/1.1


User-Agent: Axis/2.0
SOAPAction:
Connection: Keep-Alive
Host: 127.0.0.1:11000
Content-Length: 336
Content-Type: text/xml; charset=utf-8

<?xml version='1.0' encoding='utf-8'?>


<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header></soapenv:Header>
<soapenv:Body>
<eclipsebuch:sayHello
xmlns:eclipsebuch="http://entwickler.com/eclipsebuch">
<eclipsebuch:text>Fritz</eclipsebuch:text>
</eclipsebuch:sayHello>
</soapenv:Body>
</soapenv:Envelope>

Im ersten Teil steht der HTTP Header. Hier sehen wir, dass die Soap-Nachricht per HTTP
Post Request übertragen wird. Nach dem Action Tag <?xml ...?> für XML-Version und
Encoding Style folgt die eigentliche Soap-Nachricht. Sie wird von dem Soap Envelope
(<soapenv:Envelope ... >) umrahmt. Der Envelope enthält die Definition der benötig-
ten XML Namespaces.
Der Header (soapenv:Header) innerhalb des Envelope ist hier leer. Danach folgt der
Soap Body (soapenv:Body). Im Soap Body befindet sich mit dem Namespace eclipse-
buch ein von uns definiertes XML-Dokument. Es enthält den Namen der Methode des Web
Service (eclipsebuch:sayHello) und darin ein Element (eclipsebuch:text), das
den Text enthält, der vom Web Service mit einem vorangestellten »Hallo« zurückgeliefert
werden soll.
Der Web Service antwortet wiederum mit einer Soap-Antwort-Nachricht. Sie wird inner-
halb eines HTTP Response übertragen.

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Set-Cookie: JSESSIONID=B0BCB01EA9C4D0AA21EB7868CE6AF6E3; Path=/
axis2
Content-Type: text/xml;charset=utf-8
Transfer-Encoding: chunked
Date: Tue, 01 Nov 2005 21:08:07 GMT

<?xml version='1.0' encoding='utf-8'?>


<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>

558
eclipse_v01.book Seite 559 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

<wsa:From
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">
<wsa:Address>http://127.0.0.1:11000/axis2/services/
HelloService/sayHello</wsa:Address>
</wsa:From>
</soapenv:Header>
<soapenv:Body>
<eclipsebuch:sayHello
xmlns:eclipsebuch="http://entwickler.com/eclipsebuch">
<eclipsebuch:text>Hallo Fritz</eclipsebuch:text>
</eclipsebuch:sayHello>
</soapenv:Body>
</soapenv:Envelope>

Zunächst werden im HTTP Header die HTTP-Version (HTTP/1.1), der Status (200 OK),
der Content Type, Zeit und Datum und der Name des versendenen Servers übertragen.
Apache-Coyote ist der im von uns verwendeten Servlet Container Tomcat1 enthaltene
HTTP Server.
Die Soap-Nachricht umfasst wieder einen Envelope, der aus einem Header und einem Body
besteht. Der Header enthält die Adresse (wsa:From2), aus der die Antwort kommt. Im
Body befindet sich wiederum ein von uns definiertes XML-Dokument, das die vom Service
generierte Zeichenkette enthält (im Element eclipsebuch:text).

10.2 Apache Axis


Apache Axis ist eine Soap-Implementierung für Java. Mit Axis lassen sich Soap Clients
und Soap Web Services erstellen. Der Web Service läuft als Servlet innerhalb eines Servlet
Containers. In dem Beispiel dieses Kapitels werden wir wie in Kapitel 8 den Webserver
Jakarta Tomcat verwenden. Tomcat enthält den Servlet Container Catalina.

10.2.1 Installation von Tomcat


Zur Installation von Tomcat unter Windows lädt man sich von von der Tomcat Homepage
unter http://jakarta.apache.org/tomcat die Installtionsdatei herunter. Wir benutzen die Ver-
sion 5.5.123. Das Installationsdatei heißt jakarta-tomcat-5.5.12.exe. Mit einem Dop-
pelklick auf diese Datei wird Tomcat installiert. Die Installation ist selbsterklärend. Als
Port wählen wir den Standard-Port 8080 (siehe Abb. 10.1).

1. Wir verwenden Tomcat in der Version 5.5.12.


2. wsa steht für Web Service Addressing und ist ein Web Service-Standardprotokoll. Wir werden in diesem Kapi-
tel nicht weiter auf die vielen Web Service-Standards eingehen.
3. Diese Version ist auch auf der CD vorhanden.

559
eclipse_v01.book Seite 560 Montag, 30. Januar 2006 12:02 12

Apache Axis

Abb. 10.1: Installation von Tomcat für den Port 8080

Nach der Installation existiert im Programme-Menü nun die Programmgruppe Apache


Apache Tomcat 5.5. Sie enthält Configure Tomcat, womit Tomcat gestartet, gestoppt und
konfiguriert werden kann. Über den Button Start, kann Tomcat als Dienst gestartet werden
(siehe Abb. 10.2).

Abb. 10.2: Die Konfigurationskonsole von Tomcat

Nach dem Start können wir in einem Browser ausprobieren, ob alles funktioniert hat. Unter
http://localhost:8080/ sollte man nun die Startseite von Tomcat sehen (siehe Abb. 10.3).
Über die oben beschriebene Konsole können wir Tomcat auch wieder stoppen.

560
eclipse_v01.book Seite 561 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

Abb. 10.3: Die Tomcat-Startseite

10.2.2 Installation von Axis


Zur Installation von Axis laden wir uns von der Axis Homepage http://xml.apache.org/axis
die Installationsdatei herunter. Wir benutzen die Version 0.91. Die Installationsdatei lautet
axis2-0.91-bin.zip1. Sie ist ein gepackter Verzeichnisbaum. Dieser Baum enthält die Datei
axis2.war im Verzeichnis webapps. Das axis2.war kopieren wir nach <Tomcat-Installa-
tionsverzeichnis>/webapps2. Fertig ist die Installation. Unter http://localhost:8080/axis2/
sollten wir nun die Axis-Startseite sehen (siehe Abb. 10.4).
Über den Link Validate prüfen wir, ob alles vorhanden ist, was Axis braucht. Wir folgen
diesem Link und müssten nun sehen, dass Axis alles gefunden hat und den ersten Web Ser-
vice, der die Versionsnummer von Axis2 liefert, erfolgreich aufgerufen hat (siehe Abb.
10.5).

1. Diese Version ist auch auf der CD vorhanden.


2. Eine war-Datei ist ein gepackter Verzeichnisbaum. Tomcat wird ihn beim ersten Zugriff automatisch aus-
packen.

561
eclipse_v01.book Seite 562 Montag, 30. Januar 2006 12:02 12

Das Web Service Deployment Plug-In

Abb. 10.4: Die Axis-Startseite

Abb. 10.5: Axis ist erfolgreich installiert

10.3 Das Web Service Deployment Plug-In


Wir wollen nun ein Plug-In erstellen, mit dem sich komfortabel beliebige Java-Klassen als
Web Service unter Axis installieren lassen. Das Plug-In soll folgende Funktionalitäten an-
bieten:
쐌 Generierung der Service-Konfigurationsdatei service.xml. service.xml enthält Angaben
wie Name des Web Service, welche Java-Klasse und welche Methoden der Service an-
bietet.

562
eclipse_v01.book Seite 563 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

쐌 Deployment des Service durch Erstellen eines Service-Archivs und Kopieren in das
Zielverzeichnis der Axis-Installation.
Alle Einstellungen werden über einen SWT-Dialog vorgenommen und dauerhaft im
Eclipse Workspace gespeichert.
Wir wollen das Plug-In nun Schritt für Schritt erstellen. Anschließend definieren wir für
dieses Plug-In noch einen Extension Point, über den sich weitere Plug-Ins einhängen kön-
nen, die für die Web Services Test-Clients erstellen wollen. Dann erstellen wir ein weiteres
Plug-In, das eine Extension für diesen Extension Point implementiert und SWT-basierte
Test-Clients erstellt.

10.4 Den Rahmen erzeugen


Das Plug-In soll einen Menüpunkt im Kontextmenü eines Java File anbieten. Über diesen
Menüpunkt werden die Methoden der Public-Klasse in diesem File als Web Service imple-
mentiert (siehe Abb. 10.6).

Abb. 10.6: Die neuen Menüpunkte

Für Plug-Ins, die Menüpunkte zu einem Kontextmenü hinzufügen, bietet Eclipse einen
Wizard an. Dazu wählen wir zunächst mit FILE | NEW | PROJECT... den Project Wizard für
neue Projekte1. Mit dem Punkt PLUG-IN PROJECT geben wir an, dass wir ein Plug-In-Projekt

1. Das gesamte Plug-In-Projekt befindet sich auf der CD im Verzeichnis Kap_10.

563
eclipse_v01.book Seite 564 Montag, 30. Januar 2006 12:02 12

Den Rahmen erzeugen

erstellen wollen. Auf der nächtsen Seite geben wir als Namen des Projekts com.entwick-
ler.eclipsebuch.soapDeployer ein. Nun kann man Einstellungen bezüglich der Struktur des
Projektverzeichnisses und der in Kapitel 10 vorgestellten Plug-In-Klasse vornehmen. Wir
lassen die Voreinstellungen unverändert (siehe Abb. 10.7).

Abb. 10.7: Dritte Seite des Wizard zum Erstellen des Plug-In-Projekts

Nun bietet uns Eclipse vorgefertigte Gerüste für unser Plug-In an. So kann uns Eclipse ein
Plug-In mit einer View oder einem Editor generieren. Wir wählen den Punkt PLUG-IN WITH
A POPUP MENU (siehe Abb. 10.8).

Abb. 10.8: Eclipse bietet vorgefertigte Plug-In-Gerüste an

564
eclipse_v01.book Seite 565 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

Auf der nächsten Seite müssen wir angeben, in welchen Kontextmenüs der neue Menü-
punkt erscheint, wie er heißen soll usw. (siehe Abb. 10.9).

Abb. 10.9: Letzte Einstellungen für unser Plug-In

Wir wollen die Einstellungen der Reihe nach durchgehen:

TARGET OBJECT'S CLASS


Für Kontextmenüs wird der Extension Point org.eclipse.ui.popupMenus benutzt. Über die-
sen Extension Point können zwei verschiedene Arten von Kontextmenüs erzeugt werden:
쐌 Kontextmenüs für eine bestimmte View
쐌 Kontextmenüs für Objekte eines bestimmten Typs wie z.B. Java File, Java-Klasse, Me-
thode, Datei, Ordner usw.
Uns interessiert hier die zweite Art. Der neue Menüpunkt soll im Kontextmenü von Java
Files erscheinen. Java Files werden allerdings in den verschiedenen Views durch unter-
schiedliche Klassen repräsentiert. Im Package Explorer, oder genauer im TreeViewer des
Package Explorers, werden Java Files durch die Klasse org.eclipse.jdt.internal.
core.CompilationUnit repräsentiert. Über diese Klasse können wir später auch Infor-
mationen wie Methodennamen und -signaturen abfragen. CompilationUnit implemen-
tiert das Interface org.eclipse.jdt.core.IJavaElement. Über TARGET OBJECTS'S
CLASS können wir nun angeben, dass der neue Menüpunkt nur in Kontextmenüs von Ob-
jekten erscheinen soll, die durch das Interface org.eclipse.jdt.core.IJavaElement
repräsentiert werden.

565
eclipse_v01.book Seite 566 Montag, 30. Januar 2006 12:02 12

Den Rahmen erzeugen

NAME FILTER
Über NAME FILTER kann man weiter einschränken, in welchen Kontextmenüs der neue
Menüpunkt erscheinen soll. Trägt man hier *.java ein, so muss der Name des Objekts die
Endung java besitzen.

SUBMENU NAME und ACTION LABEL


Hier stehen die Namen des neuen Untermenüs im Kontextmenü und des Menüpunkts im
Untermenü. Über den Menüpunkt wird die neue Funktionalität ausgelöst. Wir nennen das
Untermenü »Soap Deployer« und den Menüpunkt »Deploy as Soap Web Service...«.

JAVA PACKAGE NAME und ACTION CLASS


Hier stehen die Namen des Package und der Klasse, in der die neue Funktionalität imple-
mentiert ist. Eclipse generiert eine Klasse mit diesem Namen vom Typ org.eclipse.
jface.action.IAction.IObjectActionDelegate. Diese Klasse ist ein Action
Delegate, der von der Plug-In Action, die den Menüpunkt darstellt, aufgerufen wird. In der
Methode run des Action Delegate wird die Funktionalität implementiert. Als Package-
Namen schlägt Eclipse com.entwickler.eclipsebuch.soapDeployer.popup.
actions vor, was wir beibehalten. Als Klassennamen wählen wir DeployAction.

ACTION IS ENABLED FOR


Mit den Radio Buttons am Ende der Seite kann man festlegen, ob genau ein Objekt selek-
tiert sein muss oder auch mehrere selektiert sein dürfen, wenn der neue Menüpunkt im Kon-
textmenü erscheinen soll. Wir sind großzügig und erlauben die Selektion mehrerer Objekte
(MULTIPLE SELECTION). Aber auch wenn nur ein Objekt selektiert ist, soll der neue Menü-
punkt angezeigt werden. Dazu müssen wir gleich noch Hand an die von Eclipse generierte
Extension innerhalb des Plug-In Manifest anlegen.
Nach dem Betätigen des Buttons FINISH wird das Plug-In erzeugt. Als Erstes sehen wir uns
das Plug-In Manifest plugin.xml an1:

<?xml version="1.0" encoding="UTF-8"?>


<?eclipse version="3.0"?>
<plugin
id="com.en<twickler.eclipsebuch.soapDeployer"
name="SoapDeployer Plug-In"
version="1.0.0"
provider-name="com.entwickler.eclipsebuch"
class="com.entwickler.eclipsebuch.\
soapDeployer.SoapDeployerPlugn">
<runtime>
<library name="soapDeployer.jar">

1. Der Backslash (\) soll nur anzeigen, dass der Text in der nächsten Zeile fortgesetzt wird. Er darf nicht einge-
geben werden.

566
eclipse_v01.book Seite 567 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

<export
name="com.entwickler.eclipsebuch.soapDeployer.api.*"/>
</library>
</runtime>
<requires>
<import plugin="org.eclipse.ui"/>
<import plugin="org.eclipse.core.runtime"/>
</requires>
<extension
point="org.eclipse.ui.popupMenus">
<objectContribution
objectClass="org.eclipse.jdt.core.IJavaElement"
nameFilter="*.java"
<menu
label="Soap Deployer"
path="additions"
id="com.entwickler.eclipsebuch.soapDeployer.menu1">
<separator name="group1">
</separator>
</menu>
<action
label="Deploy as Soap Web Service..."
class="com.entwickler.eclipsebuch.\
soapDeployer.popup.actions.DeployAction"
menubarPath="com.entwickler.eclipsebuch.\
soapDeployer.menu1/group1"
enablesFor="multiple"
id="com.entwickler.eclipsebuch.\
soapDeployer.DeployAction">
</action>
</objectContribution>
</extension>
</plugin>

Damit der neue Menüpunkt für ein oder mehrere Java Files aufgerufen werden kann, müs-
sen wir das Attribut enablesFor auf '*' setzen.
Für die nachfolgende Implementierung des Plug-Ins benötigen wir Klassen und Interfaces
aus anderen Plug-Ins. Aus diesem Grund fügen wir auf der Seite DEPENDENCIES des Plug-In
Manifest Editor über den Button ADD... folgende Plug-Ins hinzu:
쐌 org.eclipse.ant.core
쐌 org.eclipse.core.resources
쐌 org.eclipse.core.runtime
쐌 org.eclipse.jdt.core
쐌 org.eclipse.ui

567
eclipse_v01.book Seite 568 Montag, 30. Januar 2006 12:02 12

Den Rahmen erzeugen

Darüber hinaus benötigen wir noch ein paar Jar Files:


쐌 Aus der Axis-Distribution (Verzeichnis lib) brauchen wir axis2-0.91.jar.
쐌 Da wir verschiedene XML Files (Deployment Descriptor und ein Ant-Skript) über
Dom4J1 erstellen wollen, benötigen wir dom4j-full.jar von http://www.dom4j.org/
download.html.
Diese Jar Files müssen im Plug-In Manifest deklariert werden. Dazu legen wir sie zunächst
in das Plug-In-Projektverzeichnis. Dann wählen wir im Plug-In Manifest Editor von plug-
in.xml den Button ADD im Abschnitt RUN-TIME LIBRARIES auf der Seite RUNTIME. Dort wäh-
len wir die genannten Plug-In aus (siehe Abb. 10.10).

Abb. 10.10: Deklaration der benötigten Libraries im Plug-In Manifest

Danach können wir über den Menüpunkt PDE TOOLS | UPDATE CLASS PATH... im Kontextmenü
des Plug-In-Projekts den Klassenpfad für unser Projekt neu erzeugen2 (siehe Abb. 10.11).

Abb. 10.11: Den Klassenpfad neu erzeugen

1. Mit der Java-Bibliothek DOM4J lassen sich XML-Dokumente sehr einfach erstellen.
2. Allerdings wird beim Abspeichern des Plug-In Manifest der Klassenpfad automatisch erzeugt.

568
eclipse_v01.book Seite 569 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

10.5 Die Funktionalität implementieren


Was der neue Menüpunkt tun soll, wird über die Methode run des Action Delegate (Klasse
DeployAction) implementiert. Ein Action Delegate implementiert das Interface org.
eclipse.jface.action.IAction.IObjectActionDelegate. Ein Action Delegate
wird nicht direkt durch einen Menüpubkt ausgelöst. Vielmehr ist die Plug-In Action der
eigentliche Menüpunkt. Sobald der Benutzer durch Auswahl des Menüpunkts die Plug-In Ac-
tion auslöst, ruft diese den Action Delegate auf. Ein Gerüst des Action Delegate ist von Eclipse
bereits generiert worden. Wir müssen nun die folgenden Methoden fertig implementieren:
쐌 setActivePart(IAction action, IWorkbenchPart targetPart)
Diese Methode wird unmittelbar nach Anklicken des Menüpunkts aufgerufen. Hier
übergibt der Extension Point den Bereich der grafischen Oberfläche, den unsere Exten-
sion für seine Ausgabe benutzen darf (IWorkbenchPart). Wir tun hier nichts weiter,
als den Parameter targetPart für die spätere Verwendung abzuspeichern. Wir benut-
zen später diesen Workbench Part, um eine SWT Shell zu erhalten. Diese Shell benötigt
man für die Anzeige von Dialogen über SWT.
쐌 selectionChanged(IAction action, ISelection selection)
Diese Methode wird immer dann aufgerufen, wenn der Benutzer Java Files im Package
Explorer selektiert. Sie bekommt alle selektierten Java Files übergeben (ISelection)
und merkt sich diese in einer Instanzvariablen. Dazu wird die übergebene Selection
(Parameter selection) über einen Iterator durchlaufen und alle Elemente werden in
einem Vektor (Instanzvariable this.selection) abgelegt, sofern sie vom Typ
org.eclipse.jdt.core.ICompilationUnit sind. ICompilationUnit ist ein
Subtyp von IJavaElement. Java Files sind von diesem Typ und wir werden später
Funktionalitäten dieses Typs benötigen:

Iterator it = ((IStructuredSelection) selection).iterator();


while (it.hasNext()) {
Object compilationUnit = it.next();
if(compilationUnit instanceof ICompilationUnit) {
this.selection.add((ICompilationUnit) compilationUnit);
}
}

쐌 run(IAction action)
In dieser Methode wird nun über die in der Methode selectionChanged gespeicher-
ten Objekte iteriert und die eigentliche Funktionalität für jedes Objekt aufgerufen. Wir
iterieren in dieser Methode über alle innerhalb der Methode selectionChanged (sie-
he unten) abgespeicherten Java Files und rufen für jede Klasse die Methode process-
CompilationUnit auf:

Iterator it = selection.iterator();
while (it.hasNext()) {
ICompilationUnit cu = (ICompilationUnit) it.next();
processCompilationUnit(cu);
}

569
eclipse_v01.book Seite 570 Montag, 30. Januar 2006 12:02 12

Die Funktionalität implementieren

Jede der oben genannten drei Methoden bekommt zusätzlich die Plug-In Action (Parameter
Action) übergeben. Wir wollen für die von uns erstellten Plug-In Delegates eine gemein-
same Basisklasse schaffen (SoapDeployerAction). Diese bietet zusätzlich zu den oben
aufgezählten Methoden noch die folgenden:
쐌 processCompilationUnit
Diese abtrakte Methode muss von der konkreten Unterklasse von SoapDeployer-
Action implementiert werden. Sie bekommt jeweils eine Referenz auf ein Java File
(ICompilationUnit) übergeben. Zum Beispiel kann in processCompilationUnit
die übergebene Java-Klasse als Web Service installiert werden (siehe Klasse Deploy-
Action weiter unten).
쐌 getShell
Diese Methode ermittelt eine SWT Shell aus dem aktuellen Workbench Part.
Was in der Methode processCompilationUnit geschieht, hängt von der konkreten Un-
terklasse von SoapDeployerAction ab. Im Fehlerfall wird die Exception ausgegeben, so
dass man sie über die Error Log View betrachten kann. Dafür benötigen wir eine kleine Me-
thode innerhalb der Plug-In-Klasse SoapDeployerPlugin:

public class SoapDeployerPlugin extends AbstractUIPlugin {

(...)

public void log(Throwable e) {


IStatus status =
new Status(IStatus.ERROR, ID, -1, "Exception", e);
getLog().log(status);
}
}

Die Implementierung von SoapDeployerAction sieht folgendermaßen aus:

package com.entwickler.eclipsebuch.soapDeployer.popup.actions;

import java.util.Iterator;
import java.util.Vector;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IObjectActionDelegate;
import org.eclipse.ui.IWorkbenchPart;

570
eclipse_v01.book Seite 571 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

public abstract class SoapDeployerAction implements


IObjectActionDelegate {

private Vector<ICompilationUnit> selection;


private IWorkbenchPart part;

public SoapDeployerAction() {
super();
}

protected abstract void processCompilationUnit(


ICompilationUnit cu) throws Exception;

public void setActivePart(IAction action,


IWorkbenchPart targetPart) {
part = targetPart;
}

protected Shell getShell() {


return part.getSite().getShell();
}

public void run(IAction action) {


try {
for(ICompilationUnit cu : selection) {
processCompilationUnit(cu);
}
} catch (Exception e) {
IStatus status = new Status(IStatus.ERROR,
SoapDeployerPlugin.ID, -1, "error while running", e);
Shell shell = getShell();
ErrorDialog.openError(shell,
"Error while running", "error while running", status);
SoapDeployerPlugin.getDefault().log(e);
}
}

public void selectionChanged(IAction action,


ISelection selection) {
this.selection = new Vector();
if (selection instanceof IStructuredSelection) {
IStructuredSelection structuredSelection =
(IStructuredSelection) selection;
Iterator it = structuredSelection.iterator();
while (it.hasNext()) {
Object compilationUnit = it.next();
this.selection.add(compilationUnit);
}
}
}
}

571
eclipse_v01.book Seite 572 Montag, 30. Januar 2006 12:02 12

Die Funktionalität implementieren

Die konkreten Unterklassen dieser Basisklasse müssen die abstrakte Methode process-
CompilationUnit implementieren. Der Action Delegate für die Hauptfunktionalität die-
ses Plug-Ins, das Deployment von Java-Klassen als Soap Web Service, benutzt die noch zu
implementierende Klasse SoapDeployer. Wir benötigen den bereits von Eclipse generier-
ten Code der Klasse DeployAction nicht, da DeployAction nur eine Methode besitzt:

package com.entwickler.eclipsebuch.soapDeployer.popup.actions;

import org.eclipse.jdt.core.ICompilationUnit;
import com.entwickler.eclipsebuch.soapDeployer.SoapDeployer;

public class DeployAction extends SoapDeployerAction {

protected void processCompilationUnit(ICompilationUnit cu)


throws Exception {
SoapDeployer soapDeployer = new SoapDeployer(getShell());
soapDeployer.deploy(cu);
}
}

In der Methode processCompilationUnit wird lediglich eine Instanz der Klasse Soap-
Deployer erzeugt, die dann das weitere Deployment für die Java-Klasse übernehmen
wird. Der Deployment-Vorgang soll in folgenden Schritten ablaufen:
쐌 Eingabe von Konfigurationsparametern wie z.B. Name des Web Service durch den Be-
nutzer über einen SWT-Dialog
쐌 Generierung von service.xml
쐌 Generierung eines Ant-Skripts zum Deployment des Service.
Bei Axis2 läuft das Deployment folgendermaßen ab:
– Die Java-Klasse, die den Service implementiert, wird zusammen mit der Datei ser-
vice.xml in ein Jar File gepackt.
– Die Endung des Jar File wird in 'aar' umbenannt.
– Das File wird in das Verzeichnis <Tomcat-Installationsverzeichnis>/webapps/
axis2/WEB-INF/services kopiert.
쐌 Ausführen des Ant-Skripts, falls entsprechende Konfigurationsparameter über den
SWT-Dialog gesetzt wurden.
Die einzelnen Schritte werden innerhalb der Methode deploy von SoapDeployer aufge-
rufen:

public class SoapDeployer {

Shell shell;

public SoapDeployer(Shell shell)


throws JavaModelException, CoreException {
this.shell = shell;
}

572
eclipse_v01.book Seite 573 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

public void deploy(ICompilationUnit cu)


throws Exception {
String messageString = = new StringBuffer();

/*
* Eingabe der Konfigurationsparameter durch den Benutzer
*/
ValueDialog valueDialog = new ValueDialog(
shell, cu);
valueDialog.open();
if (!valueDialog.isOk()) { return; }

/*
* Erstellen von service.xml
*/
String servicename = valueDialog
.getServiceName();
String scope = valueDialog.getScope();
Vector methodsToDeploy = valueDialog.getMethodsToDeploy();
File descriptor = makeDeploymentDescriptor(cu,
methodsToDeploy, servicename, scope);
messageString.append("- service.xml built\n");

/*
* Erstellen und Ausführen eines Ant-Skripts
* zum Deployment des Service
*/
if (valueDialog.isAnt()) {
String tomcatPath = valueDialog.getPath();
// Ant-Skript erstellen
File antDeployFile = makeAntDeployFile(cu,
servicename, tomcatPath);
if (antDeployFile != null) {
if (valueDialog.isRunAnt()) {
// Ant-Skript ausführen
AntUtil.runAnt(antDeployFile);
messageString.append("- run ant script '"
+ antDeployFile.getName()
+ "' to deploy service\n");
}
}
}

/*
* Refresh auf das Eclipse-Projekt ausführen
*/
IProject thisProject = cu.getResource()
.getProject();

573
eclipse_v01.book Seite 574 Montag, 30. Januar 2006 12:02 12

Eingabe der Konfigurationsparameter

thisProject.refreshLocal(
IResource.DEPTH_INFINITE, null);

/*
* Meldung ausgeben
*/
MessageDialog.openInformation(shell,
"Deploy service "
+ valueDialog.getServiceName(),
messageString.toString());
}

In den folgenden Abschnitten werden wir die einzelnen Schritte genauer betrachten.

10.6 Eingabe der Konfigurationsparameter


Die Klasse ValueDialog bekommt in ihrem Konstruktor eine Referenz auf eine SWT
Shell (org.eclipse.swt.widgets.Shell) und eine Referenz auf ein Java File (org.
eclipse.jdt.core.ICompilationUnit) übergeben:

public class ValueDialog {

private ICompilationUnit cu;


private Shell shell;
private String serviceName;
private String scope;
private String host;
private String port;
private String path;
private boolean ok;
private boolean registerFlag;
private boolean antFlag;
private boolean runAntFlag;
private Vector allMethodsVector = new Vector();
private HashMap<String, String> methodsToDeployHash;

public ValueDialog(Shell parent,


ICompilationUnit cu)
throws JavaModelException,
CoreException {
this.cu = cu;
shell = new Shell(parent,
SWT.DIALOG_TRIM | SWT.PRIMARY_MODAL);
shell.setLayout(new GridLayout());
}

...

574
eclipse_v01.book Seite 575 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

Die Eingabewerte des Dialogs werden in Instanzvariablen von ValueDialog gespeichert.


Die Methode open erzeugt einen SWT-Dialog wie in Abb. 10.12.

Abb. 10.12: Der Dialog zur Eingabe der Konfigurationsparameter

Folgende Konfigurationsparameter kann der Benutzer über diesen Dialog eingeben:


쐌 Name des Web Service (SERVICE NAME)
쐌 Eine Beschreibung des Web Service (DESCRIPTION).
쐌 Für jede Methode der Klasse kann ausgewählt werden, ob sie als Web Service installiert
werden soll.
쐌 Der Anwender kann angeben (MAKE ANT SCRIPT?), ob ein Ant-Skript zum Deployment
des Service generiert werden soll. Wenn ja, muss der Installationspfad von Tomcat an-
gegeben werden (PATH TO TOMCAT INSTALLATION). Zur Auswahl des Pfads kann über den
Button BROWSE... ein Dialog geöffnet werden (siehe Abb. 10.13).
쐌 Als Letztes kann der Anwender angeben, ob dieses Ant-Skript auch gleich gestartet
werden soll (RUN ANT SCRIPT?).

Abb. 10.13: Der BROWSE...-Dialog

575
eclipse_v01.book Seite 576 Montag, 30. Januar 2006 12:02 12

Eingabe der Konfigurationsparameter

Der Dialog wird innerhalb der Methode createWidgets erzeugt. Zu Beginn der Methode
wird zunächst ein Grid Layout mit zwei Spalten erzeugt:

private void createWidgets() throws Exception {


Composite composite = new Composite(shell, SWT.NULL);
composite.setLayoutData(new GridData(
GridData.FILL_HORIZONTAL));
GridLayout layout2 = new GridLayout();
layout2.numColumns = 2;
composite.setLayout(layout2);

...

Danach werden die einzelnen Eingabefelder erzeugt. Diese gehen wir nun im Einzelnen
durch.

SERVICE NAME
Das Eingabefeld SERVICE NAME ist ein einfaches Textfeld (org.eclipse.swt.widgets.
Text). Da die Werte des Dialogs in Persistent Properties abgespeichert werden sollen (sie-
he unten), kann die Instanzvariable serviceName bereits gefüllt sein. In diesem Fall wird
das Eingabefeld mit dem Inhalt von serviceName initialisiert. Über den Modify Listener
(org.eclipse.swt.events.ModifyListener) wird die Instanzvariable service-
Name mit dem Inhalt des Eingabefelds gefüllt:

Label serviceNameLabel = new Label(composite, SWT.RIGHT);


serviceNameLabel.setText("Service Name");
final Text serviceNameText = new Text(composite, SWT.BORDER);
GridData serviceNameGridData = new GridData();
serviceNameGridData.widthHint = 400;
serviceNameText.setLayoutData(serviceNameGridData);
if (serviceName != null) {
serviceNameText.setText(serviceName);
}
serviceNameText.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
serviceName = serviceNameText.getText();
}
});

DESCRIPTION
Das Eingabefeld für die Beschreibung des Web Service ist wie der Service-Name ein ein-
faches Textfeld. Aus diesem Grund wird es hier nicht im Einzelnen beschrieben.

576
eclipse_v01.book Seite 577 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

Methodennamen und -signaturen


Als Nächstes werden alle Methodennamen und -signaturen mit je einer Checkbox ange-
zeigt. Hier gilt es, ein paar Probleme zu lösen:
쐌 Wie kann man die Signatur einer Methode auslesen und sie in einen String umwandeln?
Wir können hier nicht die Reflection API von Java verwenden, denn dazu müsste sich die
Java-Klasse im Klassenpfad befinden. Das JDT bietet aber Möglichkeiten, die Elemente ei-
nes Java File (ICompilationUnit) auszulesen. Wir benötigen die Public-Klasse des Java
File und von der Public-Klasse alle öffentlichen Methoden, mit Ausnahme des Konstruk-
tors. Von diesen Methoden benötigen wir Namen und Signaturen im Klartext.
Dazu legen wir uns eine kleine Utility-Klasse (com.entwickler.eclipsebuch.soap-
Deployer.util.JavaUtil) an, die in statischen Methoden diese Funktionalitäten bietet.
쐌 Die Public-Klasse eines Java Files ermitteln
Eine Java-Klasse wird im JDT durch das Interface org.eclipse.jdt.core.IType re-
präsentiert. Über ICompilationUnit.getAllTypes kann man alle Klassen eines Java
File auslesen. Über IType.getFlags lässt sich nun feststellen, ob eine Klasse public ist.
Dabei hilft die Utility-Klasse org.eclipse.jdt.core.Flags mit ihrer statischen Me-
thode isPublic:

package com.entwickler.eclipsebuch.soapDeployer.util;
...
public class JavaUtil {
...
/**
* Liefert die statische Klasse eines Java File
* @param cu Java File
* @return Statische Methode
* @throws JavaModelException
*/
public static IType getPublicType(ICompilationUnit cu)
throws JavaModelException {
IType[] allTypes = cu.getAllTypes();
IType type = null;
for (int t = 0; t < allTypes.length; t++) {
if (Flags.isPublic((allTypes[t].getFlags()))) {
type = allTypes[t];
break;
}
}
return type;
}
...
}

577
eclipse_v01.book Seite 578 Montag, 30. Januar 2006 12:02 12

Eingabe der Konfigurationsparameter

쐌 Alle Public-Methoden einer Java-Klasse mit Ausxnahme des Konstruktors ermitteln


Eine Methode wird im JDT durch das Interface org.eclipse.jdt.core.IMethod re-
präsentiert. Über IType.getMethods erhält man alle Methoden einer Java-Klasse. Mit
IMethod.isConstructor kann man abfragen, ob die Methode ein Konstruktor ist. Über
IMethod.getFlags erfährt man, ob die Methode public ist:

/**
* Ermittle alle Public-Methoden einer Java-Klasse ausser
* dem Konstruktor
*
* @param type Java-Klasse
* @return Vector aller Methoden (IMethod)
* @throws JavaModelException
*/
public static Vector<IMethod> getPublicMethods(IType type)
throws JavaModelException {
IMethod[] methods = type.getMethods();
Vector<IMethod> possibleMethods = new Vector<IMethod>();
for (int m = 0; m < methods.length; m++) {
if (!methods[m].isConstructor()
&& Flags.isPublic(methods[m].getFlags())) {
possibleMethods.add(methods[m]);
}
}
return possibleMethods;
}

쐌 Namen und Signatur einer Methode im Klartext auslesen


Den Namen einer Methode erhält man über IMethod.getElementName. Den Typ des
Rückgabewerts einer Methode erhält man über IMethod.getReturntype. Ein String Ar-
ray mit den Namen der Parameter bekommt man über IMethod.getParameterNames
und IMethod.getParameterTypes liefert ein String Array mit den Typen der Parameter.
Bei den Typen hat man jedoch das Problem, dass sie in der Byte-Code-Notation geliefert
werden. Manche werden diese leidvoll aus Java Stack Traces kennen. Zum Beispiel er-
scheint der Typ double als »D« und der Typ java.lang.String als »QString;«. Doch
zum Glück gibt es die Utility-Klasse org.eclipse.jdt.core.Signature, die in ihrer
statischen Methode toString diese Bezeichnung in die uns geläufigen Namen (»double«
und »String«) umwandelt. Nun müssen wir die Einzelteile nur noch zu einem String zusam-
menfügen:

/**
* Liefert Name und Signatur einer Methode im Klartext
* @param method Methode
* @return String mit Name und Signatur
* @throws JavaModelException
*/

578
eclipse_v01.book Seite 579 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

public static String getNameAndSignature(IMethod method)


throws JavaModelException {
String name = method.getElementName();
StringBuffer nameAndSignature = new StringBuffer(
Signature.toString(method.getReturnType()) + " ");
nameAndSignature.append(name + "(");
String[] paramNames = method.getParameterNames();
String[] paramTypes = method.getParameterTypes();
for (int i = 0; i < paramNames.length; i++) {
nameAndSignature.append(Signature
.toString(paramTypes[i])
+ " " + paramNames[i]);
}
nameAndSignature.append(")");
return nameAndSignature.toString();
}

쐌 Wie speichert man die Methoden ab, die der Benutzer über den Dialog ausgewählt hat?
Wir könnten die vom Benutzer ausgewählten Methoden innerhalb der Klasse ValueDia-
log als ein Vector mit Elementen vom Typ IMethod abspeichern. Jedoch wollen wir später
(siehe unten) alle Konfigurationsparameter einschließlich der ausgewählten Methoden über
Persistent Properties abspeichern. Wenn der Benutzer den Dialog ein weiteres Mal öffnet,
soll die alte Auswahl der Methoden wiederhergestellt werden. Es kann jedoch sein, dass
eine der beim letzten Mal ausgewählten Methoden nicht mehr existiert. Aus diesem Grund
wählen wir folgendes Verfahren:
Lege Namen und Signatur der ausgewählten Methoden in einer Hashmap (java.util.
HashMap) ab. Beim Schließen des Dialogs wird diese Hashmap über die Persistent Proper-
ties abgespeichert. Beim erneuten Öffnen wird diese Hashmap aus den Persistent Properties
wiederhergestellt. Nun werden alle Public-Methoden ermittelt. Für jede Public-Methode
wird über Name und Signatur festgestellt, ob sie sich in der Hashmap befindet. Wenn ja,
wird diese Methode im Dialog ausgewählt, d.h., der Haken der Checkbox wird als Vorein-
stellung gesetzt. Die Hashmap wird in der Instanzvariablen methodsToDeployHash von
ValueDialog abgelegt.
Im Selection Listener der Checkbox wird die Signatur der ausgewählten Methode der
Hashmap hinzugefügt. Nimmt der Benutzer die Auswahl der Methode zurück, wird ent-
sprechend die Signatur aus der Hashmap entfernt.
Damit haben wir alles zusammen, um innerhalb der Methode createWidgets von Value-
Dialog die Checkboxes für die Auswahl der Methoden zu erzeugen und sie zu initialisieren:

// Alle Methoden als Checkboxes


IType type = JavaUtil.getPublicType(cu);
Vector<IMethod> publicMethods = JavaUtil.getPublicMethods(type);
Iterator it = publicMethods.iterator();

579
eclipse_v01.book Seite 580 Montag, 30. Januar 2006 12:02 12

Eingabe der Konfigurationsparameter

while (it.hasNext()) {
final IMethod method = (IMethod) it.next();
final String nameAndSignatureString = JavaUtil
.getNameAndSignature(method);
Label methodLabel = new Label(composite, SWT.RIGHT);
methodLabel.setText("Deploy "
+ nameAndSignatureString + "?");
final Button methodButton = new Button(composite,SWT.CHECK);
// Initialisiere die Checkbox
if (methodsToDeployHash
.containsKey(nameAndSignatureString)) {
methodButton.setSelection(true);
} else {
methodButton.setSelection(false);
}
methodButton
.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
if (methodButton.getSelection()) {
methodsToDeployHash.put(
nameAndSignatureString, method.getElementName());
} else {
methodsToDeployHash
.remove(nameAndSignatureString);
}
}
});
}

Wie man eine Hashmap in den Persistent Properties abspeichert bzw. aus ihnen ausliest, er-
fahren wir weiter unten.
MAKE ANT SCRIPT?, PATH, BROWSE... und RUN ANT SCRIPT?
Die Checkbox »Make Ant Script?« schaltet in ihrem Selection Listener das Textfeld
»Path«, den Button »Browse...« und die Checkbox »Run Ant Script?« ein und aus. Um das
Textfeld »Path« und den Button »Browse...« nebeneinander zu platzieren, wird ein unter-
geordnetes Grid Layout mit zwei Spalten erstellt. Der Button »Browse...« öffnet in seinem
Selection Adapter einen Directory Dialog (org.eclipse.swt.DirectoryDialog):

// Make Ant Script?


Label antLabel = new Label(composite, SWT.RIGHT);
antLabel.setText("Make Ant Script?");
final Button antButton = new Button(composite, SWT.CHECK);

// Path
Label pathLabel = new Label(composite, SWT.RIGHT);
pathLabel.setText("Path to tomcat installation");

580
eclipse_v01.book Seite 581 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

Composite subComposite = new Composite(composite, SWT.NULL);


subComposite.setLayoutData(new GridData(
GridData.FILL_HORIZONTAL));
GridLayout subLayout = new GridLayout();
subLayout.numColumns = 2;
subComposite.setLayout(subLayout);
final Text pathText = new Text(subComposite, SWT.BORDER);
GridData pathGridData = new GridData();
pathGridData.widthHint = 400;
pathText.setLayoutData(pathGridData);
if (path != null) {
pathText.setText(path);
}
pathText.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
path = pathText.getText();
}
});

// Browse...
final Button fileButton = new Button(subComposite, SWT.PUSH);
fileButton.setText("Browse...");
fileButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
DirectoryDialog dd = new DirectoryDialog(shell);
String path = dd.open();
if (path != null) {
pathText.setText(path);
}
}
});

// Run Ant Script?


Label runAntLabel = new Label(composite, SWT.RIGHT);
runAntLabel.setText("Run Ant Script?");
final Button runAntButton = new Button(composite, SWT.CHECK);
runAntButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
runAntFlag = runAntButton.getSelection();
}
});

/*
* Schalte das Eingabefeld 'Path' den Button 'Browse...'
* und die Checkbox 'Run Ant Script?' je nach Status der Checkbox
* 'Make Ant Script?' ein oder aus.
*/

581
eclipse_v01.book Seite 582 Montag, 30. Januar 2006 12:02 12

Eingabe der Konfigurationsparameter

antButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
antFlag = antButton.getSelection();
pathText.setEnabled(antFlag);
fileButton.setEnabled(antFlag);
runAntButton.setEnabled(antFlag);
}
});

// Initialisieren
antButton.setSelection(antFlag);
pathText.setEnabled(antFlag);
fileButton.setEnabled(antFlag);
runAntButton.setSelection(runAntFlag);

OK und CANCEL
Der Button »OK« setzt in seinem Selection Listener die Instanzvariable ok auf true, der
Button »Cancel« setzt sie auf false. Außerdem wird in beiden Selection Listeners der
Dialog geschlossen. Über die Methode isOk von ValueDialog kann die Instanzvariable ok
abgefragt werden. Zum Schluss wird der Button »OK« als Default-Button für den Dialog
gesetzt:

// OK
Button okButton = new Button(composite, SWT.PUSH);
okButton.setText("OK");
okButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
ok = true;
shell.close();
}
});

// Cancel
Button cancelButton = new Button(composite, SWT.PUSH);
cancelButton.setText("Cancel");
cancelButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
ok = false;
shell.close();
}
});
shell.setDefaultButton(okButton);

582
eclipse_v01.book Seite 583 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

10.6.1 Speichern und Laden der Konfigurationsparameter


Alle durch den Dialog erfassten Konfigurationsparameter sollen als Persistent Properties
im Java File gespeichert (Methode save) und wieder geladen (Methode load) werden. Ge-
steuert wird der Dialog in der Methode open. Dort befindet sich auch die zentrale Dispatch-
Schleife des Dialogs. Bevor der Dialog geöffnet wird, werden die Instanzvariablen aus den
Persistent Properties geladen (Methode load). Nach Schließen des Dialogs wird über die
Methode isOk geprüft, ob der Dialog mit »OK« beendet wurde. Wenn ja, werden die In-
stanzvariablen als Persistent Properties gespeichert (Methode save).

public void open() throws Exception {


load();
createWidgets();
shell.pack();
shell.open();
Display display = shell.getDisplay();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
if (isOk()) {
save();
}
}

Die Methode load füllt die Instanzvariablen aus den Persistent Properties. Dazu ruft sie
einzelne set-Methoden auf. Ebenso bedient sich die Methode save der get-Methoden,
um die Instanzvariablen in den Persistent Properties abzuspeichern.

private void load() throws JavaModelException, CoreException {


IResource res = cu.getResource();
setServiceName(res
.getPersistentProperty(SoapDeployerPlugin.SERVICENAMEQN));
setPath(res
.getPersistentProperty(SoapDeployerPlugin.PATHQN));
setScope(res
.getPersistentProperty(SoapDeployerPlugin.DESCRIPTIONQN));
setAntFlag(res
.getPersistentProperty(SoapDeployerPlugin.ANTQN));
setRunAntFlag(res
.getPersistentProperty(SoapDeployerPlugin.RUNANTQN));

String methodsToDeployStr = res


.getPersistentProperty(SoapDeployerPlugin.METHODSQN);
HashMap<String, String> methodsToDeployHash = ResourceUtil
.deserializeHashMap(methodsToDeployStr);
setMethodsToDeployHash(methodsToDeployHash);
}

583
eclipse_v01.book Seite 584 Montag, 30. Januar 2006 12:02 12

Eingabe der Konfigurationsparameter

private void save() throws CoreException {


IResource res = cu.getResource();
res.setPersistentProperty(
SoapDeployerPlugin.SERVICENAMEQN, getServiceName());
res.setPersistentProperty(SoapDeployerPlugin.PATHQN,
getPath());
res.setPersistentProperty(SoapDeployerPlugin.SCOPEQN,
getScope());
res.setPersistentProperty(SoapDeployerPlugin.ANTQN,
getAntFlag());
res.setPersistentProperty(SoapDeployerPlugin.RUNANTQN,
getRunAntFlag());

HashMap<String, String> methodsToDeploy =


getMethodsToDeployHash();
String methodsToDeployStr = ResourceUtil
.serializeHashMap(methodsToDeploy);
res.setPersistentProperty(SoapDeployerPlugin.METHODSQN,
methodsToDeployStr);
}

Etwas komplizierter ist es bei der Instanzvariablen methodsToDeployHash. Um sie über


Persistent Properties abzuspeichern, muss eine Hashmap (java.util.HashMap) in einen
String serialisiert werden können – und wieder zurück. Wir lagern diese Funktionalität in
eine kleine Utility-Klasse aus (com.entwickler.eclipsebuch.soapDeployer.
util.ResourceUtil). In der statischen Methode serialize wird eine Hashmap in ei-
nen String umgewandelt. Dazu werden alle Schlüssel aneinander gereiht, jeweils durch »§«
getrennt. Ebenso wird mit den Werten – passend zu den Schlüsseln – verfahren. Die beiden
Strings mit den Werten und den Schlüsseln werden durch ein »%%« getrennt aneinander
gehängt. In der statischen Methode deserialize wird ein derart aufgebauter String wie-
der in eine Hashmap umgewandelt. Dazu werden zunächst mit einem String Tokenizer
(java.util.StringTokenizer) die beiden Teilstrings für Schlüssel und Werte vonein-
ander getrennt (keysString und valuesString). In zwei weiteren String Tokenizers
(keysTokenizer und valuesTokenizer) werden dann die einzelnen Schlüssel und
Werte abgetrennt:

package com.entwickler.eclipsebuch.soapDeployer.util;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.StringTokenizer;

public class ResourceUtil {

584
eclipse_v01.book Seite 585 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

public static HashMap<String, String> deserializeHashMap(


String serialized) {
HashMap<String, String> map = new HashMap<String, String>();
if (serialized != null) {
StringTokenizer outerTokenizer = new StringTokenizer(
serialized, "%%");
String keysString;
String valuesString;
if (outerTokenizer.countTokens() == 2) {
keysString = outerTokenizer.nextToken();
valuesString = outerTokenizer.nextToken();
} else {
return map;
}
StringTokenizer keysTokenizer = new StringTokenizer(
keysString, "§");
StringTokenizer valuesTokenizer = new StringTokenizer(
valuesString, "§");
if (keysTokenizer.countTokens() == valuesTokenizer
.countTokens()) {
while (keysTokenizer.hasMoreElements()) {
String key = keysTokenizer.nextToken();
String value = valuesTokenizer.nextToken();
map.put(key, value);
}
}
}
return map;
}

public static String serializeHashMap(HashMap map) {


Set keys = map.keySet();
Iterator itKeys = keys.iterator();
StringBuffer keysSerialized = new StringBuffer();
StringBuffer valuesSerialized = new StringBuffer();
while (itKeys.hasNext()) {
String key = (String) itKeys.next();
String value = (String) map.get(key);
keysSerialized.append(key + "§");
valuesSerialized.append(value + "§");
}
String serialized = keysSerialized.toString() + "%%"
+ valuesSerialized.toString();
return serialized;
}
}

585
eclipse_v01.book Seite 586 Montag, 30. Januar 2006 12:02 12

Generierung von service.xml

10.7 Generierung von service.xml


Nachdem der Benutzer die Konfigurationsparameter erfasst hat, kann die Datei service.xml
generiert werden. Nachfolgend ein Beispiel für service.xml:

<?xml version="1.0" encoding="UTF-8"?>

<service name="Hello">
<description>Hello service</description>
<parameter
name="ServiceClass"
locked="xsd:false">com.entwickler.eclipsebuch.service.\
HelloService</parameter>
<operation name="sayHello">
<messageReceiver
class="org.apache.axis2.receivers.\
RawXMLINOutMessageReceiver"/>
</operation>
<operation name="sayGoodbye">
<messageReceiver
class="org.apache.axis2.receivers.\
RawXMLINOutMessageReceiver"/>
</operation>
</service>

Im Attribut name des Elements service wird der Name des Service angegeben. Danach
folgt ein Parameter (Element parameter) mit Namen ServiceClass. Er gibt die Java-
Klasse an, die den Web Service implementiert.
Zum Schluss werden alle Methoden aufgelistet, die der Web Service besitzt (Elemente
operation). Das Unterelement messageReceiver gibt an, ob die Methode eine Ausga-
be produziert (org.apache.axis2.receivers.RawXMLINOutMessageReceiver)
oder nicht (org.apache.axis2.receivers.RawXMLINOnlyMessageReceiver).
Erstellt wird service.xml in der Methode makeServiceXml der Klasse SoapDeployer.
makeServiceXml wird wiederum aus der Methode deploy aufgerufen (siehe Abschnitt
10.5). Die Methode bekommt das Java File (ICompilationUnit), einen Vector der Me-
thoden (IMethod), die als Funktionen des Web Service installiert werden sollen, den Na-
men des Web Service und die Beschreibung übergeben. Daraus wird über die Java-Biblio-
thek Dom4J1 der Deployment Descriptor erstellt:

public File makeServiceXml(


ICompilationUnit cu,
Vector methodsToDeploy,
String servicename, String descriptionStr)

1. Dom4J haben wir in Abschnitt 11.4 dem Klassenpfad hinzugefügt (dom4j-full.jar).

586
eclipse_v01.book Seite 587 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

throws CoreException, IOException {


String classname = JavaUtil.getPublicType(cu)
.getFullyQualifiedName();
IProject thisProject = cu.getResource().getProject();
/*
* Erzeuge den XML-Code
*/
Document document = DocumentHelper.createDocument();
/*
* Element 'service'
*/
Element service = document.addElement("service");
service.addAttribute("name", servicename);
// Element 'description
Element description = service.addElement("description");
description.addText(descriptionStr);
// Element 'parameter name="ServiceClass" ...'
Element serviceClass = service.addElement("parameter");
serviceClass.addAttribute("name", "ServiceClass");
serviceClass.addAttribute("locked", "xsd:false");
String classname =
JavaUtil.getPublicType(cu).getFullyQualifiedName();
serviceClass.addText(classname);
// Elemente 'operation'
Iterator mIt = methodsToDeploy.iterator();
while (mIt.hasNext()) {
IMethod method = (IMethod) mIt.next();
Element operation = service.addElement("operation");
operation.addAttribute("name", method.getElementName());
Element messageReceiver =
operation.addElement("messageReceiver");
messageReceiver.addAttribute("class",
"org.apache.axis2.receivers.RawXMLINOutMessageReceiver");
}
/*
* Schreibe den XML-Code in eine Datei
* Zunächst den Ordner <servicename>.deploy/META-INF
* erzeugen
*/
IFolder deployFolder =
thisProject.getFolder(servicename + ".deploy");
if(!deployFolder.exists()) {
deployFolder.create(false, true, null);
}
IFolder metaInfFolder =
thisProject.getFolder(servicename + ".deploy/META-INF");
if(!deployFolder.exists()) {
metaInfFolder.create(false, true, null);
}

587
eclipse_v01.book Seite 588 Montag, 30. Januar 2006 12:02 12

Generierung von service.xml

File serviceXml =
XmlUtil.writeXmlDocumentToFile(thisProject,
servicename + ".deploy/META-INF",
"service.xml", document);
return serviceXml;
}

Über die Klasse org.dom4j.DocumentHelper.createDocument wird ein XML-Do-


kument erstellt (org.dom4j.Document). An diesem Dokument können nun über die Me-
thode addElement von der Klasse Document XML-Elemente (org.dom4j.Element)
erstellt werden. Unterelemente werden über die Methode addElement der Klasse Ele-
ment erstellt. Attribute entstehen über die Methode addAttribute der Klasse Element.
Für den Parameter classname des Deployment Descriptor muss der voll qualifizierte
Name der Klasse, die als Web Service installiert werden soll, ermittelt werden. Dafür wird
die Public-Klasse des Java File über die statische Methode getPublicType der Utility-
Klasse JavaUtil ausgelesen. An ihr kann über die Methode getFullyQualifiedName
der Name ermittelt werden.
Am Ende der Methode makeServiceXml soll das XML-Dokument in einer Datei inner-
halb des aktuellen Eclipse-Projekts gespeichert werden. Dazu stellen wir die Klasse Xml-
Util mit der statischen Methode writeXmlDocumentToFile zur Verfügung. write-
XmlDocumentToFile bekommt ein Eclipse-Projekt (IProject), den Namen eines
Ordners innerhalb des Projekts, den Namen der Datei und das XML-Dokument übergeben.
Falls der Ordner nicht existiert, wird er angelegt. Falls die Datei bereits existiert, wird sie
zunächst gelöscht:

package com.entwickler.eclipsebuch.soapDeployer.util;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import org.dom4j.Document;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;

public class XmlUtil {

public static File writeXmlDocumentToFile(IProject project,


String foldername, String filename, Document document)
throws CoreException, IOException {
IFolder folder = project.getFolder(foldername);

588
eclipse_v01.book Seite 589 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

if (!folder.exists()) {
folder.create(false, true, null);
}
IFile descriptor = folder.getFile(filename);
if (descriptor.exists()) {
descriptor.delete(true, false, null);
}
IPath location = descriptor.getLocation();
File xmlFile = location.toFile();
OutputFormat outformat = OutputFormat.createPrettyPrint();
FileOutputStream fos = new FileOutputStream(xmlFile);
XMLWriter writer = new XMLWriter(fos, outformat);
writer.write(document);
writer.flush();
fos.close();
return xmlFile;
}
}

10.8 Generierung eines Ant-Skripts


Zum Deployment des Web Service müssen die implementierende Java-Klasse (Class File)
und service.xml in ein Archiv (Jar File mit der Endung aar) gepackt und in das Verzeichnis
<Tomcat-Installationsverzeichnis>/webapps/axis2/WEB-INF/services kopiert werden.
Dies soll mittels eines Ant-Skripts ausgeführt werden, das automatisch generiert wird.
Dabei werden alle Class Files des Package, in dem sich die implementierende Java-Klasse
befindet, in das Archiv gepackt. Befindet sich die Klasse z.B. in dem Package com.ent-
wickler.eclipsebuch.service und lautet das Installationsverzeichnis von Tomcat
c:\tomcat\tomcat5.5, so kann das Ant-Skript wie folgt aussehen1:

<?xml version="1.0" encoding="UTF-8"?>

<project default="deploy" basedir="..">


<target name="copy">
<copy todir="Hello.deploy">
<fileset dir="./bin"
includes="com/entwickler/eclipsebuch/service/*.class" />
</copy>
</target>

1. Der Backslash (\) soll nur anzeigen, dass der Text in der nächsten Zeile fortgesetzt wird. Er darf nicht ein-
gegeben werden. Das Skript setzt voraus, dass das sich das Ant-Skript in einem Projekt-Unterverzeichnis
befindet.

589
eclipse_v01.book Seite 590 Montag, 30. Januar 2006 12:02 12

Generierung eines Ant-Skripts

<target name="jar">
<jar destfile="testservice.aar" basedir="./Hello.deploy">
<fileset dir="." includes="./**" />
</jar>
</target>
<target name="install">
<copy
todir="C:/tomcat/tomcat5.5/webapps/axis2/WEB-INF/services
file="Hello.aar">
</target>
<target name="deploy" depends="copy, jar, install"/>
</project>

Wie der Deployment Descriptor wird das Ant-Skript über Dom4J erzeugt. Dies geschieht
innerhalb der Methode makeAntDeployFile der Klasse SopaDeployer. Der Code die-
ser Methode sieht wie folgt aus:

public File makeAntDeployFile(ICompilationUnit cu,


String servicename,
String tomcatPath)
throws JavaModelException, CoreException, IOException {
File antFile = null;
String packagePath = JavaUtil.getPackagePath(cu);
/*
* Erzeuge den XML-Code fuer das Ant-Build-File
*/
Document document = DocumentHelper.createDocument();
Element xmlProject = document.addElement("project");
xmlProject.addAttribute("default", "deploy");
xmlProject.addAttribute("basedir", "..");
// Target 'copy'
Element copyTarget = xmlProject.addElement("target");
copyTarget.addAttribute("name", "copy");
Element copyTask = copyTarget.addElement("copy");
copyTask.addAttribute("todir", servicename + ".deploy");
Element xmlFileset = copyTask.addElement("fileset");
xmlFileset.addAttribute("dir", "./bin");
xmlFileset.addAttribute("includes", packagePath + "/*.class");
// Target 'jar'
Element jarTarget = xmlProject.addElement("target");
jarTarget.addAttribute("name", "jar");
Element jarTask = jarTarget.addElement("jar");
jarTask.addAttribute("destfile", servicename + ".aar");
jarTask.addAttribute("basedir", "./" + servicename + ".deploy");
Element fileset = jarTask.addElement("fileset");
fileset.addAttribute("dir", ".");
fileset.addAttribute("includes", "./**");

590
eclipse_v01.book Seite 591 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

// Target 'install'
Element installTarget = xmlProject.addElement("target");
installTarget.addAttribute("name", "install");
Element copyTask2 = installTarget.addElement("copy");
copyTask2.addAttribute("todir",
tomcatPath + "/webapps/axis2/WEB-INF/services");
copyTask2.addAttribute("file", servicename + ".aar");
// Target 'deploy'
Element deployTarget = xmlProject.addElement("target");
deployTarget.addAttribute("name", "deploy");
deployTarget.addAttribute("depends", "copy, jar, install");
/*
* Schreibe das XML-Dokument in ein Ant-Build-File
*/
IProject project = cu.getResource().getProject();
antFile = XmlUtil.writeXmlDocumentToFile(project,
"ant", "deploy_" + servicename + ".xml", document);
return antFile;
}

Es wird eine statische Methode der Utility-Klasse JavaUtil benutzt:


쐌 getPackagePath
Diese Methode bekommt ein Java File (ICompilationUnit) übergeben und liefert
den relativen Pfad (ausgehend vom Eclipse-Projekt, in dem sich das Java File befindet)
zum Package des Java File zurück.
Die Implementierung dieser Methode nutzt die Tatsache, dass das übergeordnete Ele-
ment eines Java File das Package ist, in dem sich das Java File befindet. Tauscht man im
Namen des Package alle Punkte gegen Slashes ('/'), so erhält man den Pfad zum Java
File:

public static String getPackagePath(ICompilationUnit cu) {


String packagePath = null;
IJavaElement parent = cu.getParent();
if (parent.getElementType() == IJavaElement.PACKAGE_FRAGMENT) {
String packageName = parent.getElementName();
packagePath = packageName.replace('.', '/');
}
return packagePath;
}

591
eclipse_v01.book Seite 592 Montag, 30. Januar 2006 12:02 12

Ausführen des Ant-Skripts

10.9 Ausführen des Ant-Skripts


Zum Ausführen des generierten Ant-Skripts stellen wir eine kleine Utility-Klasse
(com.entwickler.eclipsebuch.util.AntUtil) zur Verfügung. Sie besitzt als einzi-
ge Methode die statische Methode runAnt, die das Default Target eines übergebenen Ant-
Skripts ausführt. Dazu besorgt sich die Methode runAnt den absoluten Pfad zum Ant-
Skript:

String buildfilepath = antDeployFile.getPath().toString();

Dann wird der Pfad dem Ant Runner (org.eclipse.ant.core.AntRunner) überge-


ben:

antRunner.setBuildFileLocation(buildfilepath);

Am Schluss wird über die Methode run am Ant Runner das Ant-Skript gestartet:

package com.entwickler.eclipsebuch.soapDeployer.util;

import java.io.File;
import org.eclipse.ant.core.AntRunner;
import org.eclipse.core.runtime.CoreException;

public class AntUtil {

public static void runAnt(File antDeployFile)


throws CoreException {
AntRunner antRunner = new AntRunner();
String buildfilepath = antDeployFile.getPath()
.toString();
antRunner.setBuildFileLocation(buildfilepath);
antRunner.run();
}
}

10.10 Das fertige Plug-In


Jetzt haben wir alles zusammen1. Wir wollen nun den am Anfang dieses Kapitels beschrie-
benen Web Service erstellen. Er bekommt einen Text übergeben und gibt ihn mit einem
vorangestellten »Hallo« wieder zurück2.

1. Das komplette Plug-In-Projekt befindet sich auf der CD im Verzeichnis Kapitel10.


2. Die ausgetauschten XML-Formate sind in Abschnitt 11.1 dargestellt. Wir spendieren dem Service noch eine
zweite Methode, die ein Text mit vorangestelltem »Auf Wiedersehen« zurückgibt.

592
eclipse_v01.book Seite 593 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

Wir starten die Eclipse Application mit einem leeren Workspace. Dort legen wir ein neues
Projekt mit Namen deploytest und in dem Projekt ein Package mit Namen com.entwick-
ler.eclipsebuch.helloworldservice an1.
In diesem Package legen wir eine Java-Klasse mit Namen HelloWorldService an. Diese
Klasse besitzt nur zwei Methoden (sayHello und sayGoodbye), die den übergebenen
String mit vorangestelltem »Hallo« bzw. »Auf Wiedersehen« zurückgeben:

package com.entwickler.eclipsebuch.helloworldservice;

import org.apache.axis2.om.OMAbstractFactory;
import org.apache.axis2.om.OMElement;
import org.apache.axis2.om.OMFactory;
import org.apache.axis2.om.OMNamespace;

public class HelloService {

private String getText(OMElement element) {


OMElement first = element.getFirstElement();
String text = first.getText();
return text;
}

private OMElement makeRespond(String methodName, String text) {


OMFactory fac = OMAbstractFactory.getOMFactory();
OMNamespace omNs = fac.createOMNamespace(
"http://entwickler.com/eclipsebuch", "eclipsebuch");
OMElement response = fac.createOMElement(methodName, omNs);
OMElement answerText = fac.createOMElement("text", omNs);
answerText.addChild(fac.createText(answerText, text));
response.addChild(answerText);
return response;
}

public OMElement sayHello(OMElement element) {


String text = getText(element);
return makeRespond("sayHello", "Hallo " + text);
}

public OMElement sayGoodbye(OMElement element) {


String text = getText(element);
return makeRespond("sayGoodbye", "Auf Wiedersehen " + text);
}
}

1. Dem Klassenpfad müssen nun einige Jar Files aus der Axis2-Distribution hinzugefügt werden. Siehe hierzu
das Projekt auf der beiliegenden CD.

593
eclipse_v01.book Seite 594 Montag, 30. Januar 2006 12:02 12

Das fertige Plug-In

Nun rufen wir im Kontextmenü des Java File HelloWorldService.java den Menüpunkt DE-
PLOY AS SOAP WEB SERVICE im Untermenü SOAP DEPLOYER auf (siehe Abb. 10.6).

Dann öffnet sich der Dialog zur Eingabe der Konfigurationsparameter. Wir füllen ihn wie
in Abb. 10.12 aus.
Kurze Zeit nach dem Betätigen von OK teilt uns das Plug-In mit, was es alles getan hat (sie-
he Abb. 10.14)

Abb. 10.14: Die Arbeit ist getan

Nun sehen wir, dass das Ant-Skript deploy_HelloWorldService.xml und ein Verzeichnis mit
Namen HelloWorldService.deploy entstanden sind. HelloWorldService.deploy ist das
Deployment-Verzeichnis. Es enthält die implementierende Klasse und service.xml. Das
Verzeichnis liegt gepackt in dem Web Service-Archiv HelloWorldService.aar (siehe Abb.
10.15).

Abb. 10.15: Die generierten Dateien

Das Web Service-Archiv (HelloWorldService.aar) ist außerdem in das Zielverzeichnis der


Axis-Installation kopiert worden (siehe Abb. 10.16).
Zu guter Letzt sehen wir über die URL http://localhost:8080/axis2/listServices.jsp, dass
der neue Web Service bei Apache Axis registriert worden ist (siehe Abb. 10.17).
Will man den Web Service wieder loswerden, so muss man nur die Datei HelloWorldSer-
vice.aar aus dem Zielverzeichnis von Axis2 (siehe Abb. 10.16) wieder löschen.

594
eclipse_v01.book Seite 595 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

Abb. 10.16: Das Web Service-Archiv wurde in das Zielverzeichnis der Axis-Installation
kopiert

Abb. 10.17: Der Service HelloWorldService ist registriert

595
eclipse_v01.book Seite 596 Montag, 30. Januar 2006 12:02 12

Ein Extension Point für die Generierung von Clients

10.11 Ein Extension Point für die Generierung von Clients


Doch was nützt der schönste Web Service ohne Client, der ihn aufrufen kann. Wir hoffen
deshalb, dass andere einen solchen Client erstellen. Oder noch besser: Wir hoffen, dass an-
dere gleich einen kompletten Generator für Clients von Web Services verfassen. Dafür wol-
len wir einen Extension Point anbieten. Über diesen Extension Point wird der Client-Gene-
rator mit den benötigten Daten aufgerufen. Dabei soll für jede Methode des Web Service
ein eigener Client generiert werden1.
Doch woher kennen wir das Schema des XML-Dokuments, das der Web Service erwartet?
Üblicherweise wird diese Struktur in Form von XML Schema in dem WSDL2-Dokument
des Web Service beschrieben. Ein Client-Generator könnte nun auf dem WSDL-Dokument
aufsetzen. Wir wollen einen einfacheren Weg wählen und von einer festen Struktur ausge-
hen. Über diese Struktur können beliebig viele Parameter übergeben werden. Pro Parameter
existiert jeweils ein XML-Element.
Wird die Methode »xyz« des Web Service z.B. mit den zwei Parametern »p1« (ein String)
und »p2« (ein Integer) aufgerufen, so wird dem Web Service folgendes XML-Dokument
übergeben3:

<eclipsebuch:xyz
xmlns:eclipsebuch="http://entwickler.com/eclipsebuch">
<eclipsebuch:p1>Fritz</eclipsebuch:p1>
<eclipsebuch:p2>12345</eclipsebuch:p2>
</eclipsebuch:sayHello>

Da aus einer Signatur einer Methode der Klasse, die einen Web Service für Axis implemen-
tiert, nicht hervorgeht, welche Parameter sie erwartet, wollen wir diese Information als Java
5 Annotations an der Methode ablegen und dem Client-Generator zur Verfügung stellen.
Ebenso wird der Typ des Rückgabewerts über Annotations an der Methode abgelegt. Gäbe
die Methode »xyz« einen Rückgabewert vom Typ java.lang.String zurück, so würde
sie wie folgt annotiert4:

@Parameters(names = {"p1", "p2"},


types = {"java.lang.String", "java.lang.Integer"})
@Return(type = "java.lang.String")

public OMElement xyz(OMElement element) {


(...)
}

1. Jede Methode eines Web Service bietet eigene Funktionalität an. Insofern ist es sinnvoll, für jede Methode
einen eigenen Test-Client zu generieren.
2. WSDL bedeutet Web Service Definition Language. Ein WSDL-Dokument beschreibt einen Web Service
bezüglich Protokoll und Definition der ein- und ausgehenden XML-Dokumente.
3. Der Einfachheit wegen trägt die Struktur keine Informationen über den Typ.
4. Zum besseren Verständnis werden hier Namen und Typen der Parameter in zwei Arrays abgelegt. Außerdem
werden hier nur die zwei Typen »String« und »Integer« unterstützt.

596
eclipse_v01.book Seite 597 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

So könnte die Methode sayHello wie folgt annotiert werden:

@Parameters(name = {"text"}, type = {"java.lang.String"})


@Return(type = "java.lang.String")
public OMElement sayHello(OMElement element) {
String text = getText(element);
return makeRespond("sayHello", "Hallo " + text);
}

Doch um die Annotations einer Java-Methode innerhalb unseres Plug-Ins auslesen zu kön-
nen, müssen wir etwas tiefer in die Code-Analyse mit dem JDT einsteigen.

10.12 Einschub: Code-Analyse über AST


Zur Analyse von Java-Code stellt Eclipse den Abstract Syntax Tree (AST) bereit. AST
stellt einen Parser (org.eclipse.jdt.core.dom.ASTParser) zur Verfügung, der ein
Java File in seine Bestandteile zerlegt und einen Syntaxbaum erstellt:
Über1

ASTParser astParser = ASTParser.newParser(AST.JLS3);1

kann man sich einen AST Parser besorgen. Sei javaFile eine Referenz auf ein Java File
(ICompilationUnit2), so kann man mit

astParser.setSource(javaFile);
CompilationUnit cu = (CompilationUnit) astParser.createAST(null);

den Syntaxbaum erzeugen. Mit

List types = cu.types()

erhält man die Java-Klassen (nur nichtanonyme) des Java File. Eine Java-Klasse ist vom
Typ TypeDeclaration. An ihm kann man Attribute (getFields) und Methoden (get-
Methods) abfragen. Aber AST geht noch weiter. An einer Methode (MethodDeclara-
tion) lassen sich der Körper (getBody) und an diesem alle Anweisungen (statements)
erfragen. Je nach Art der Anweisung (IfStatement, ForStatement, ReturnState-
ment, ...) können Teile der Anweisung wie Körper, logischer Ausdruck etc. erfragt werden.

1. Die Konstante AST.JLS3 steht für Java 5.


2. ICompilationUnit ist ein von IJavaElement abgeleitetes Interface.

597
eclipse_v01.book Seite 598 Montag, 30. Januar 2006 12:02 12

Eine View zur Anzeige des Syntaxbaums

10.13 Eine View zur Anzeige des Syntaxbaums


Um AST besser kennen zu lernen, wollen wir eine AST View mit einem Tree Viewer er-
stellen, über den man sich den Syntaxbaum eines Java File ansehen kann. Wir erzeugen da-
für ein einfaches Plug-In-Projekt mit Namen com.entwickler.eclipsebuch.astview1. Dem
Plug-In spendieren wir eine View und einen Menüpunkt im Kontextmenü von Java Files.
Über diesen Menüpunkt kann der Syntaxbaum des Java File in der View angezeigt werden
(siehe Abb. 10.18).

Abb. 10.18: Die AST View und der neue Menüpunkt

Das Plug-In Manifest sieht wie folgt aus2:

<?xml version="1.0" encoding="UTF-8"?>


<?eclipse version="3.0"?>
<plugin
id="com.entwickler.eclipsebuch.astview"
name="AstView Plug-In"
version="1.0.0"

1. Das Plug-In Projekt befindet sich auf der CD im Verzeichnis Kapitel11.


2. Der Backslash (\) soll nur anzeigen, dass der Text in der nächsten Zeile fortgesetzt wird. Er darf nicht einge-
geben werden.

598
eclipse_v01.book Seite 599 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

provider-name="ENTWICKLER"
class="com.entwickler.eclipsebuch.astview.AstViewPlugin">

<runtime>
<library name="AstView.jar">
<export name="*"/>
</library>
</runtime>
<requires>
<import plugin="org.eclipse.ui"/>
<import plugin="org.eclipse.core.runtime"/>
<import plugin="org.eclipse.jdt.core"/>
</requires>

<extension
point="org.eclipse.ui.views">
<category
name="AST"
id="com.entwickler.eclipsebuch.astview">
</category>
<view
name="AST View"
icon="icons/sample.gif"
category="com.entwickler.eclipsebuch.astview"
class="com.entwickler.eclipsebuch.astview.AstView"
id="com.entwickler.eclipsebuch.astview.AstView">
</view>
</extension>
<extension
point="org.eclipse.ui.popupMenus">
<objectContribution
objectClass="org.eclipse.jdt.core.ICompilationUnit"
nameFilter="*.java"
id="com.entwickler.eclipsebuch.\
astview.objectContribution1">
<action
label="Show AST in AST View"
class="com.entwickler.eclipsebuch.\
astview.ShowASTAction"
menubarPath="addition"
enablesFor="1"
id="com.entwickler.eclipsebuch.astview.action1">
</action>
</objectContribution>
</extension>
</plugin>

599
eclipse_v01.book Seite 600 Montag, 30. Januar 2006 12:02 12

Eine View zur Anzeige des Syntaxbaums

Damit die Action com.entwickler.eclipsebuch.astview.ShowASTAction, die


den neuen Menüpunkt implementiert, auf die View zugreifen kann, speichern wir eine Re-
ferenz auf die View in der Plug-In-Klasse:

package com.entwickler.eclipsebuch.astview;

import org.eclipse.core.runtime.IPluginDescriptor;
import org.eclipse.ui.plugin.AbstractUIPlugin;

public class AstViewPlugin extends AbstractUIPlugin {

private static AstViewPlugin plugin;


private AstView astView = null;

public AstView getAstView() {


return astView;
}

public void setAstView(AstView astView) {


this.astView = astView;
}

public AstViewPlugin() {
super();
plugin = this;
}

public static AstViewPlugin getDefault() {


return plugin;
}
}

Die Action ShowAstAction merkt sich in der Methode selectionChanged das selek-
tierte Java File:

public void selectionChanged(IAction action,


ISelection selection) {
if (selection instanceof IStructuredSelection) {
Iterator it = ((IStructuredSelection) selection)
.iterator();
if (it.hasNext()) {
icu = (ICompilationUnit) it.next();
}
}
}

600
eclipse_v01.book Seite 601 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

In der Methode run wird an der View AstView die Methode showAst aufgerufen. Da-
durch wird der Syntaxbaum eines Java File in der View angezeigt:

package com.entwickler.eclipsebuch.astview;

import java.util.Iterator;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.IObjectActionDelegate;
import org.eclipse.ui.IWorkbenchPart;

public class ShowASTAction implements IObjectActionDelegate {

private ICompilationUnit icu = null;

public ShowASTAction() {
super();
}

public void setActivePart(IAction action,


IWorkbenchPart targetPart) {
}

public void run(IAction action) {


if (icu != null) {
AstView astView = AstViewPlugin.getDefault()
.getAstView();
if (astView != null) {
astView.showAst(icu);
}
}
}

...

Innerhalb der View benötigen wir einen Tree Viewer, der den Syntaxbaum anzeigen soll.
Als Erstes implementieren wir den Content Provider. Als Model speichern wir das Java File
(ICompilationUnit), dessen Syntaxbaum angezeigt werden soll. In der Methode get-
Elements instanziieren wir den AST Parser, erzeugen den Syntaxbaum und geben die
nichtanonymen Klassen des Java File zurück:

601
eclipse_v01.book Seite 602 Montag, 30. Januar 2006 12:02 12

Eine View zur Anzeige des Syntaxbaums

class ViewContentProvider implements


IStructuredContentProvider, ITreeContentProvider {
private ICompilationUnit invisibleRoot;
private ASTParser astParser = null;
public Object[] getElements(Object parent) {
if (astParser == null) {
astParser = ASTParser.newParser(AST.JLS2);
}
if (invisibleRoot != null) {
astParser.setSource(invisibleRoot);
CompilationUnit cu = (CompilationUnit) astParser
.createAST(null);
Object[] types = cu.types().toArray();
return types;
}
return null;
}

...

Für die Methode getChildren des Content Providers, die jeweils die Kinder eines Ele-
ments des Tree Viewers ermittelt, haben wir zwei Möglichkeiten:
쐌 Wir behandeln jede Klasse explizit und hangeln uns so durch den Syntaxbaum:

public Object[] getChildren(Object parent) {


Vector children = new Vector();
if (parent instanceof TypeDeclaration) {
TypeDeclaration td = (TypeDeclaration) parent;
children.addAll(Arrays.asList(td.getMethods()));
}
if (parent instanceof MethodDeclaration) {
MethodDeclaration md = (MethodDeclaration) parent;
children.add(md.getReturnType());
children.addAll(md.parameters());
children.add(md.getBody());
}
if (parent instanceof Block) {
Block body = (Block) parent;
children.addAll(body.statements());
}
if (parent instanceof ...)
...
}

602
eclipse_v01.book Seite 603 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

...

return children.toArray();
}

Bei den vielen Klassen des AST kann das aber sehr lang dauern. Aus diesem Grund ent-
scheiden wir uns für einen anderen Weg:
쐌 Wir rufen generisch über Java Reflection an den Objekten aus dem AST alle Methoden
auf.
Dafür erzeugen wir die Utility-Klasse com.entwickler.eclipsebuch.astview.
AstUtil mit der zentralen Methode callAllAstMethods. Die Methode bekommt ein
beliebiges Objekt und einen Vektor übergeben. Sie ruft die Methoden des Objekts auf und
fügt alle zurückgegebenen Objekte in den Vektor ein. Dabei werden bei Arrays oder Col-
lections die einzelnen Elemente in den Vektor eingefügt (siehe Methode addToVector).
Jedoch werden nur die Methoden aufgerufen, die an AST-Klassen (aus dem Package
org.eclipse.jdt.core.dom, siehe Methode isAstClass) definiert sind. So werden
z.B. Methoden, die von der Mutter aller Klassen Object nur geerbt wurden, nicht aufge-
rufen. Außerdem werden die Namen der aufgerufenen Methoden in den Vektor eingefügt.
Diese werden später im Tree Viewer angezeigt.

package com.entwickler.eclipsebuch.astview;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.Vector;

public class AstUtil {

public static boolean isAstClass(Class c) {


Package p = c.getPackage();
String name = p.getName();
return name.equals("org.eclipse.jdt.core.dom");
}

public static void callAllAstMethods(Vector v,


Object instance) {
Class c = instance.getClass();
Method[] methods = c.getMethods();
/*
* rufe alle Methoden auf, die zu einer AST-Klassse
* gehoeren, die ein Rückgabewert vom Typ Object
* haben, und die keinen Parameter benoetigen
*/

603
eclipse_v01.book Seite 604 Montag, 30. Januar 2006 12:02 12

Eine View zur Anzeige des Syntaxbaums

for (int i = 0; i < methods.length; i++) {


Method m = methods[i];
Class dc = m.getDeclaringClass();
if (!isAstClass(dc)) {
continue;
}
Class retType = m.getReturnType();
// Ist der Rueckgabewert vom Typ Object?
if (Object.class.isAssignableFrom(retType)) {
try {
// Benoetigt die Methode keinen Parameter?
if (m.getParameterTypes().length == 0) {
// Aufruf der Methode
Object ret = m.invoke(instance, new Object[0]);
if (ret != null) {
// Fuege den Methodennamen in den Vektor ein
v.add("--- " + m.getName() + " ---");
addToVector(v, ret);
}
}
} catch (Exception e) {
}
}
}
}
/*
* Fuege ein Object einem Vektor hinzu
* Falls das Object ein Array oder eine Collection ist,
* so fuege alle Elemente dem Vektor hinzu.
*/
public static void addToVector(Vector v, Object o) {
if (o.getClass().isArray()) {
Collection c = Arrays.asList((Object[]) o);
addToVector(v, c);
} else if (o instanceof Collection) {
v.addAll((Collection) o);
} else {
v.add(o);
}
}
}

604
eclipse_v01.book Seite 605 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

Mit der Utility-Klasse AstUtil hangelt sich die Methode getChildren innerhalb des
Content Provider des Tree Viewer über die AST-Methoden1 an den AST-Objekten2 durch
den Syntaxbaum:

public Object[] getChildren(Object parent) {


if(!AstUtil.isAstClass(parent.getClass())) {
return null;
}
Vector children = new Vector();
AstUtil.callAllAstMethods(children, parent);
return children.toArray();
}

Zur Anzeige der AST-Objekte im Tree Viewer benötigen wir noch einen Label Provider für
den Tree Viewer. Bei AST-Objekten werden die Klasse und das Objekt als String (Method
toString) angezeigt. Bei allen anderen Objekten wird nur das Objekt als String angezeigt:

class ViewLabelProvider extends LabelProvider {

public String getText(Object obj) {


if (obj == null) { return "<null>"; }
if(!AstUtil.isAstClass(obj.getClass())) {
return obj.toString();
}
return "<" + obj.getClass().getName() + "> " + obj.toString();
}

public Image getImage(Object obj) {


return null;
}
}

Nachfolgend der Rest der Klasse AstView:

package com.entwickler.eclipsebuch.astview;

import java.util.Vector;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jface.viewers.IStructuredContentProvider;

1. »AST-Methode« soll heißen, dass die Methode in einer Klasse aus dem Package
com.eclipse.jdt.core.dom definiert ist.
2. »AST-Objekt« soll heißen, dass die Klasse des Objekts aus dem Package com.eclipse.jdt.core.dom ist.

605
eclipse_v01.book Seite 606 Montag, 30. Januar 2006 12:02 12

Eine View zur Anzeige des Syntaxbaums

import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.part.ViewPart;

public class AstView extends ViewPart {

private TreeViewer viewer;

class ViewContentProvider implements


IStructuredContentProvider, ITreeContentProvider {

private ICompilationUnit invisibleRoot;


private ASTParser astParser = null;

public Object[] getElements(Object parent) {


...
}

public void inputChanged(Viewer v, Object oldInput,


Object newInput) {
if (newInput instanceof ICompilationUnit) {
invisibleRoot = (ICompilationUnit) newInput;
}
}

public void dispose() {}

public Object getParent(Object child) {


return null;
}

public Object[] getChildren(Object parent) {


...
}

public boolean hasChildren(Object parent) {


Object[] childs = getChildren(parent);
return (childs != null && childs.length > 0);
}
}

class ViewLabelProvider extends LabelProvider {


...
}

606
eclipse_v01.book Seite 607 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

public AstView() {
AstViewPlugin.getDefault().setAstView(this);
}

public void createPartControl(Composite parent) {


viewer = new TreeViewer(parent, SWT.MULTI
| SWT.H_SCROLL | SWT.V_SCROLL);
viewer.setContentProvider(new ViewContentProvider());
viewer.setLabelProvider(new ViewLabelProvider());
}

public void setFocus() {


viewer.getControl().setFocus();
}

public void showAst(ICompilationUnit icu) {


viewer.setInput(icu);
viewer.refresh();
}
}

Nun können wir den Run-time Workspace aufrufen und den Syntaxbaum unserer Web-Ser-
vice-Klasse HelloWorldService in der AST View anzeigen (siehe Abb. 10.19).

Abb. 10.19: Syntaxbaum in der AST View

607
eclipse_v01.book Seite 608 Montag, 30. Januar 2006 12:02 12

Die Erzeugung von Java-Code über AST

10.14 Die Erzeugung von Java-Code über AST


Über AST kann eine CompilationUnit auch neu aufgebaut werden. Dazu existiert die
Klasse org.eclipse.jdt.core.dom.AST, über die Knoten eines Syntaxbaums erzeugt
werden können. Wir wollen einen Syntaxbaum für den nachfolgenden Code erzeugen:

package com.entwickler.eclipsebuch.astgenerator;
public class Eclipsebuch {
public static void main(String[] args){
System.out.println("Eclipsebuch");
}
}

Dieser Code kann wie folgt erzeugt werden:

package com.entwickler.eclipsebuch.astview;

import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ExpressionStatement;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.PackageDeclaration;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;

public class AstGenerator {

public static void main(String[] args) {


AST ast = AST.newAST(AST.JLS3);
CompilationUnit compilationUnit = ast
.newCompilationUnit();

// package com.entwickler.eclipsebuch.astgenerator;
PackageDeclaration packageDeclaration = ast
.newPackageDeclaration();
packageDeclaration
.setName(ast
.newName("com.entwickler.eclipsebuch.astgenerator"));
compilationUnit.setPackage(packageDeclaration);

608
eclipse_v01.book Seite 609 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

// public class Eclipsebuch


TypeDeclaration typeDeclaration = ast
.newTypeDeclaration();
typeDeclaration.setInterface(false);
Modifier classModifierPublic = ast.newModifier
(ModifierKeyword.PUBLIC_KEYWORD);
typeDeclaration.modifiers().add(classModifierPublic);
typeDeclaration.setName(ast
.newSimpleName("Eclipsebuch"));

// public static void main(String[] args)


MethodDeclaration methodDeclaration = ast
.newMethodDeclaration();
methodDeclaration.setConstructor(false);
Modifier methodModifierPublic = ast.newModifier
(ModifierKeyword.PUBLIC_KEYWORD);
Modifier methodModifierStatic = ast.newModifier
(ModifierKeyword.STATIC_KEYWORD);
methodDeclaration.modifiers().add(methodModifierPublic);
methodDeclaration.modifiers().add(methodModifierStatic);
methodDeclaration.setName(ast.newSimpleName("main"));
methodDeclaration.setReturnType2(ast
.newPrimitiveType(PrimitiveType.VOID));
SingleVariableDeclaration singleVariableDeclaration = ast
.newSingleVariableDeclaration();
singleVariableDeclaration.setType(ast.newArrayType(ast
.newSimpleType(ast.newSimpleName("String"))));
singleVariableDeclaration.setName(ast
.newSimpleName("args"));
methodDeclaration.parameters().add(
singleVariableDeclaration);
Block block = ast.newBlock();

// System.out.println("Eclipsebuch")
MethodInvocation methodInvocation = ast
.newMethodInvocation();
QualifiedName qualifiedName = ast.newQualifiedName(ast
.newSimpleName("System"), ast.newSimpleName("out"));
methodInvocation.setExpression(qualifiedName);
methodInvocation.setName(ast.newSimpleName("println"));
StringLiteral stringLiteral = ast.newStringLiteral();
stringLiteral.setLiteralValue("Eclipsebuch");
methodInvocation.arguments().add(stringLiteral);
ExpressionStatement expressionStatement = ast
.newExpressionStatement(methodInvocation);
block.statements().add(expressionStatement);
methodDeclaration.setBody(block);

609
eclipse_v01.book Seite 610 Montag, 30. Januar 2006 12:02 12

Die Erzeugung von Java-Code über AST

typeDeclaration.bodyDeclarations().add(
methodDeclaration);
compilationUnit.types().add(typeDeclaration);
}

Um aus einem Syntaxbaum Sourcecode zu erzeugen gibt es die Klasse org.eclipse.


jdt.core.dom.ASTVisitor, über die man gemäß dem Visitor Pattern den gesamten
Syntaxbaum traversieren kann. An jedem Knoten kann dann der Sourcecode zu diesem
Knoten ausgegeben werden. Doch ist die Klasse ASTVisitor abstrakt und die Standard-
Implementierung (org.eclipse.jdt.core.dom.DefaultASTVisitor) traversiert
durch den Baum, ohne etwas zu tun.
Es existiert jedoch eine Implementierung von ASTVisitor (org.eclipse.jdt.core.
dom.NaiveASTFlattener), die aus einem Syntaxbaum den entsprechenden Sourcecode
erzeugen kann. Sie ist jedoch mit Vorsicht zu genießen, denn es wird nicht gewährleistet,
dass immer der korrekte Sourcecode erzeugt wird. Der Javadoc-Kommentar zur Klasse lau-
tet:
»Internal AST visitor for serializing an AST in a quick and dirty fashion. For various
reasons the resulting string is not necessarily legal Java code; and even if it is legal Java
code, it is not necessarily the string that corresponds to the given AST. Although useless for
most purposes, it's fine for generating debug print strings.«
Wir ignorieren dies und benutzen diese Klasse trotzdem. Sie ist jedoch nicht öffentlich, so
dass man sie nicht direkt nutzen kann. Ruft man aber an der Klasse org.eclipse.jdt.
core.dom.CompilationUnit die Methode toString() auf, so wird implizit der
NaiveASTFlattener benutzt. So können wir folgendermaßen den Code des Syntax-
baums ausgeben:

public class AstGenerator {

...

/*
* Erzeuge Code und schreibe ihn auf die Konsole
*/
String result = compilationUnit.toString();
System.out.println(result);
}
}

Sei root die Wurzel einer Package-Struktur (IPackageFragmentRoot), so könnte man


folgendermaßen aus dem String result ein Java File erzeugen:

IPackageFragment newFragment = root


.createPackageFragment(
"com.entwickler.eclipsebuch.astgenerator", false,
new NullProgressMonitor());

610
eclipse_v01.book Seite 611 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

ICompilationUnit icu = newFragment


.createCompilationUnit("Eclipsebuch.java", result,
true, new NullProgressMonitor());

Inwieweit sich der NaiveASTFlattener jedoch auch für komplexe Java-Klassen eignet,
ist nicht klar. Der Autor ist für jeden Hinweis aus der Leserschaft dankbar.

10.15 Ein Extension Point für die Generierung von Clients


(Fortsetzung)
Die gewonnenen Erkenntnisse über AST wollen wir nun nutzen, um die Annotationen von
Java-Methoden auszulesen. Doch vorher wollen wir noch ein paar Gedanken über unseren
Extension Point für einen Client-Generator machen. Dafür überlegen wir uns zunächst,
welche Daten solch ein Client-Generator benötigt:
쐌 Das Eclipse-Projekt, in dem der Client generiert werden soll
쐌 Das Java File (IJavaElement), in dem sich die Klasse befindet, die den Web Service
implementiert
쐌 Die Methode (IMethod) des Web Service
쐌 Den Namen und Typen der Parameter, die der Web Service benötigt
쐌 Den Typ des Rückgabewerts1
Zur Übergabe dieser Daten definieren wir das Interface IClientGenerator:

package com.entwickler.eclipsebuch.soapDeployer.api;

import java.util.Map;

import org.eclipse.core.resources.IProject;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IMethod;

public interface IClientGenerator {

public String generateClient(


IProject project,
ICompilationUnit cu,
String servicename,
IMethod method,

1. Die Definition von Parametern und Rückgabewert einer Web-Service-Methode ist bei einem Web Service vom
Typ Soap Document eigentlich nicht üblich. Schließlich können hier beliebige XML-Dokumente zum Service
geschickt werden und der Service kann mit beliebigen XML-Dokumenten antworten. Ein Client-Generator
benötigte damit die WSDL-Beschreibungen der XML-Dokumente. Um das Beispiel einfach zu halten, über-
geben wir ähnlich Soap RPC nur die Parameter der Web-Service-Methode und die Methode antwortet mit
einem Rückgabewert.

611
eclipse_v01.book Seite 612 Montag, 30. Januar 2006 12:02 12

Ein Extension Point für die Generierung von Clients (Fortsetzung)

Map<String, String> parameter,


String returnType) throws Exception;
}

Jede Extension unseres Extension Point muss eine Instanz einer Klasse angeben, die das In-
terface IClientGenerator implementiert. Die Implementierung des Extension Point ruft
dann für jede Methode des Web Service die Methode generateClient an dieser Instanz
auf. Eine Extension für diesen Extension Point könnte etwa so aussehen1:

<extension
point="com.entwickler.eclipsebuch.soapDeployer.generator">
<client
class="com.entwickler.eclipsebuch.soapClientGenerator.\
SoapClientGeneratorSWT">
</client>
</extension>

Zur Erstellung des Extension Point müssen wir zunächst den Button ADD auf der Seite EX-
TENSION POINTS des Plug-In Manifest Editors unseres Plug-Ins betätigen. Als ID wählen wir
generator, als Namen Generator. Unter Beibehaltung des Namensvorschlags für die XML-
Schema-Datei drücken wir ohne Umweg auf den Button FINISH (siehe Abb. 10.20).

Abb. 10.20: Die Erstellung des Extension Point für Client-Generatoren

Damit landen wir im XML-Schema-Editor (siehe Abb. 10.21). Die Definition für das Ele-
ment extension ist bereits angelegt. Über den Button NEW ELEMENT erzeugen wir ein wei-

1. Der Backslash (\) soll nur anzeigen, dass der Text in der nächsten Zeile fortgesetzt wird. Er darf nicht einge-
geben werden.

612
eclipse_v01.book Seite 613 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

teres Element, das erst einmal den Namen new_element1 trägt. In den Properties dieses
Elements können wir den Namen in client ändern.
Ebenso erzeugen wir über den Button NEW ATTRIBUTE ein Attribut für dieses Element und
nennen es class. Die Property Kind des Attributs class sollte auf java stehen. Damit ist
festgelegt, dass der Wert dieses Attributs der Name einer Java-Klasse ist. Eclipse wird da-
durch später beim Erstellen einer Extension für den Extension Point Generator einen Klas-
senbrowser für das Füllen des Attributs class anbieten.
Nun müssen wir noch definieren, dass client ein Unterelement von extension ist. Dazu
wählen wir im linken Teil (EXTENSION POINT ELEMENTS) das Element extension aus. Im
rechten Teil (ELEMENT GRAMMAR) müsste jetzt SEQUENCE erscheinen1. Mit NEW | REFERENCE
| CLIENT definieren wir, dass das Element extension aus einer Sequenz von Elementen
client besteht. Wie viele es minimal sein müssen und maximal sein dürfen, wird über die
Properties minOccurs und maxOccurs festgelegt. Wählt man maxOccurs größer als 1,
darf eine Extension mehrere Klassen, die das Interface IClientGenerator implementie-
ren, anbieten. So könnte z.B. eine Extension mehrere verschiedene Clients generieren.

Abb. 10.21: Der Schema Editor für den Extension Point Generator

Damit ist die Definition des Extension Point com.entwickler.eclipsebuch.soapDeployer.ge-


nerator fertig.

10.16 Die Implementierung des Extension Point


Nun müssen wir uns überlegen, wie wir die Extensions unseres Extension Point aufrufen
können. Laut Schnittstelle IClientGenerator (siehe Abschnitt 10.15) bekommen die
Extensions Namen und Typen der Parameter sowie den Typ des Rückgabewerts der Metho-
de des Web Service übergeben. Diese Informationen werden als Annotationen an der Me-
thode der Klasse hinterlegt, die den Web Service implementiert:

1. Falls SEQUENCE nicht erscheint, so wählen wir im Kontextmenü NEW | COMPOSITOR | SEQUENCE.

613
eclipse_v01.book Seite 614 Montag, 30. Januar 2006 12:02 12

Die Implementierung des Extension Point

@Parameters(names = {"text"},
types = {"java.lang.String"})
@Return(type = "java.lang.String")
public OMElement sayHello(OMElement element) {
String text = getText(element);
return makeRespond("sayHello", "Hallo " + text);
}

Wir wollen uns nun ansehen, wie die Annotationen im AST abgelegt sind und untersuchen
die Methode sayHello in der AST View (siehe Abb. 10.22):

Abb. 10.22: Die Methode sayHello in der AST View

Wir sehen, dass die Methode modifiers von MethodDeclaration die Annotationen als
Objekte vom Typ NormalAnnotation zurückliefert. Wir klappen die erste Annotation
(@Parameters) auf (siehe Abb. 10.23).

Abb. 10.23: Die Annotation @Parameters in der AST View

614
eclipse_v01.book Seite 615 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

Die Methode values liefert zwei Objekte vom Typ MemberValuePair. Wir klappen das
erste auf. Die Methode getValue liefert uns den Wert {"text"} als Objekt vom Typ
ArrayInitializer. Wir klappen auch getValue auf (siehe Abb. 10.24).

Abb. 10.24: ArrayInitializer in der AST View

Über die Methode expressions erhalten wir ein Objekt vom StringLiteral, das wie-
derum über die Methode getLiteralValue seinen Inhalt text zurückliefert.
Nun haben wir noch ein Problem zu lösen: In der Klasse SoapDeployer, wo wir die Er-
zeugung der Clients über das Interface IClientGenerator (siehe Abschnitt 10.15) auf-
rufen wollen, liegt die Methode des Web Service zunächst als Objekt vom Typ IMethod
vor. In einem AST werden Methoden jedoch als Objekt vom Typ MethodDeclaration
dargestellt. Wir müssen also die Methode im AST (MethodDeclaration) heraussuchen,
die einer IMethod entspricht, um dann die Annotationen auslesen zu können. Wir werden
dafür der Klasse AstUtil die Hilfsmethode getAnnotationsForMethod spendieren,
die eine IMethod übergeben bekommt, nach der passenden MethodDeclaration sucht
und deren Annotationen zurückliefert:

/**
* Liefert alle Annotations zu einer Methode
* @param cu
* @param method
* @return
*/
public static List<Annotation> getAnnotationsForMethod(
CompilationUnit cu, IMethod method) {
List<Annotation> annotations = new ArrayList<Annotation>();
MethodDeclaration methodDeclaration =
searchForMethodInCompilationUnit(cu, method);
if(methodDeclaration != null) {
List modifiers = methodDeclaration.modifiers();
if(modifiers != null) {
for(Object modifier : modifiers) {

615
eclipse_v01.book Seite 616 Montag, 30. Januar 2006 12:02 12

Die Implementierung des Extension Point

if(modifier instanceof Annotation) {


annotations.add((Annotation) modifier);
}
}
}
}
return annotations;
}

Die Suche nach der passenden MethodDeclaration findet in der Methode Ast-
Util.searchForMethodInCompilationUnit statt, wo alle Methoden des AST mit der
IMethod verglichen werden:

/**
* Sucht nach einer Methode
* (definiert durch IMethod) in einer CompilationUnit
* @param cu
* @param method
* @return MethodDeclaration oder null
*/
public static MethodDeclaration searchForMethodInCompilationUnit(
CompilationUnit cu, IMethod method) {
TypeDeclaration publicClass = getPublicClass(cu);
MethodDeclaration[] methods = publicClass.getMethods();
for(int m = 0; m < methods.length; m++) {
if(compareIMethodWithMethodDeclaration(method, methods[m])) {
return methods[m];
}
}
return null;
}

Der Vergleich zwischen einer MethodDeclaration mit einer IMethod findet wiederum
in der Methode AstUtil.compareIMethodWithMethodDeclaration statt. Eine Me-
thodDeclaration stimmt genau dann mit einer IMethod überein, wenn die Namen der
Methoden, die Reihenfolgen und Typen der Parameter und die Typen der Rückgabewerte
übereinstimmen:

/**
* Prüft, ob eine MethodDeclaration
* und eine IMethod dieselbe Signatur besitzen
* @param imethod
* @param methodDeclaration
* @return
*/

616
eclipse_v01.book Seite 617 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

public static boolean compareIMethodWithMethodDeclaration(


IMethod imethod, MethodDeclaration methodDeclaration) {
try {
// Namen vergleichen
String name1 =
methodDeclaration.getName().getFullyQualifiedName();
String name2 = imethod.getElementName();
if(!name1.equals(name2)) {
return false;
}
// Typ der Methode vergleichen
String returnType1 =
((SimpleType) methodDeclaration.getReturnType2()).
getName().getFullyQualifiedName();
String returnType2Q = imethod.getReturnType();
String returnType2 = Signature.toString(returnType2Q);
if(!returnType1.equals(returnType2)) {
return false;
}

// Parameter vergleichen
List parameters1 = methodDeclaration.parameters();
String[] parameters2Q = imethod.getParameterTypes();
if(parameters1.size() != parameters2Q.length) {
return false;
}
int i = 0;
for(Object p : parameters1) {
SingleVariableDeclaration var1 =
(SingleVariableDeclaration) p;
SimpleType type1 = (SimpleType) var1.getType();
String parameter1 = type1.getName().getFullyQualifiedName();
String parameter2 = Signature.toString(parameters2Q[i]);
if(!parameter1.equals(parameter2)) {
return false;
}
i++;
}
return true;
} catch (JavaModelException e) {
SoapDeployerPlugin.getDefault().log(e);
return false;
}
}

617
eclipse_v01.book Seite 618 Montag, 30. Januar 2006 12:02 12

Die Implementierung des Extension Point

Nun haben wir alles zusammen, um den Extension Point implementieren zu können. Die
Implementierung findet in zwei Stufen statt:
쐌 Auslesen aller Extensions
In der Methode initGeneratorClasses der Klasse SoapDeployer wird jedes
Attribut class aller Extensions ausgelesen:

IExtensionPoint generatorEp = Platform


.getExtensionRegistry().getExtensionPoint(
SoapDeployerPlugin.ID + ".generator");
IExtension[] generatorExtensions = generatorEp
.getExtensions();

쐌 Über die Methode createExecutableExtension am Interface IConfiguration-


Element kann eine Instanz der Klasse erzeugt werden, die als Wert des Attributs
class angegeben ist. Alle so gewonnenen Instanzen werden in der Instanzvariablen
clientGeneratorClasses von SoapDeployer abgespeichert.

for (int j = 0; j < elements.length; j++) {


if (elements[j].getName().equals("client")) {
Object generatorClass = elements[j]
.createExecutableExtension("class");
if (generatorClass instanceof IClientGenerator) {
clientGeneratorClasses.add(
(IClientGenerator) generatorClass);
}
}
}

쐌 Die komplette Methode initGeneratorClasses sieht damit wie folgt aus:

private void initGeneratorClasses() {


clientGeneratorClasses = new Vector<IClientGenerator>();
IExtensionPoint generatorEp = Platform
.getExtensionRegistry().getExtensionPoint(
SoapDeployerPlugin.ID + ".generator");
if (generatorEp != null) {
IExtension[] generatorExtensions = generatorEp
.getExtensions();
for (int i = 0; i < generatorExtensions.length; i++) {
IConfigurationElement[] elements = generatorExtensions[i]
.getConfigurationElements();
for (int j = 0; j < elements.length; j++) {
if (elements[j].getName().equals("client")) {
try {
Object generatorClass = elements[j]
.createExecutableExtension("class");

618
eclipse_v01.book Seite 619 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

if (generatorClass instanceof IClientGenerator) {


clientGeneratorClasses.add(
(IClientGenerator) generatorClass);
}
} catch (CoreException e) {
SoapDeployerPlugin.getDefault().log(e);
}
}
}
}
}
}

쐌 Aufruf der Generatoren


Nachdem über initGeneratorClasses die Instanzen der Generatoren erstellt wor-
den sind, werden sie für jede Methode (IMethod) des Web Service aufgerufen. Dies ge-
schieht innerhalb der Methode generateClients von SoapDeployer.
Dort werden zunächst über AstUtil.getAnnotationsForMethod alle Annota-
tionen einer IMethod ausgelesen. Bei jeder Annotation wird nun geprüft, ob sie vom
Typ @Parameters oder vom Typ @Return ist. Im ersten Fall werden die Werte des
Attributs names in die Liste nameList und die Werte des Attributs types in die Liste
typeList geschrieben. Aus den Werten von names und types wird dann die für
IClientGenerator (siehe Abschnitt 10.15) benötigte Map mit Namen und Typen der
Parameter der Methode des Web Service aufgebaut. Der Inhalt des Attributs type der
Annotation @Return wird in die Variable returnType geschrieben.

private String generateClients(


ICompilationUnit cu, Vector methods,
String servicename, String host, String port) throws Exception {
String messageString = "";
IProject thisProject = cu.getResource().getProject();
Iterator it = methods.iterator();
if (clientGeneratorClasses == null) {
initGeneratorClasses();
}
ASTParser parser = ASTParser.newParser(AST.JLS3);
parser.setSource(cu);
CompilationUnit compilationUnit = (CompilationUnit) parser
.createAST(null);
while (it.hasNext()) {
IMethod method = (IMethod) it.next();
/*
* Annotationen auslesen
* @Return in 'returnType' schreiben
*/

619
eclipse_v01.book Seite 620 Montag, 30. Januar 2006 12:02 12

Die Implementierung des Extension Point

Map<String, String> parameters =


new HashMap<String, String>();
String returnType = null;
List<Annotation> annotations =
AstUtil.getAnnotationsForMethod(compilationUnit, method);
for(Annotation annotation : annotations) {
if(annotation.getTypeName().getFullyQualifiedName().
equals(Parameters.class.getSimpleName())) {
// @Parameters auslesen und in Map 'parameters' schreiben
List<String> nameList = new ArrayList<String>();
List<String> typeList = new ArrayList<String>();
List values = ((NormalAnnotation) annotation).values();
for(Object v : values) {
MemberValuePair memberValuePair = (MemberValuePair) v;
ArrayInitializer arrayInitializer =
(ArrayInitializer) memberValuePair.getValue();
String name =
memberValuePair.getName().getFullyQualifiedName();
for(Object expr : arrayInitializer.expressions()) {
StringLiteral stringLiteral = (StringLiteral) expr;
String value = stringLiteral.getLiteralValue();
if(name.equals("names")) {
// Array 'names' auslesen
nameList.add(value);
} else if(name.equals("types")) {
// Array 'types' auslesen
typeList.add(value);
}
}
}
if(nameList.size() != typeList.size()) {
throw new IllegalArgumentException(
"number of names not equal number of types");
}
for(int i = 0; i < nameList.size(); i++) {
parameters.put(nameList.get(i), typeList.get(i));
}
}
else if(annotation.getTypeName().getFullyQualifiedName().
equals(Return.class.getSimpleName())) {
// @Return auslesen
List values = ((NormalAnnotation) annotation).values();
for(Object v : values) {
MemberValuePair memberValuePair = (MemberValuePair) v;
StringLiteral value =
(StringLiteral) memberValuePair.getValue();
returnType = value.getLiteralValue();

620
eclipse_v01.book Seite 621 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

}
}
}
for (IClientGenerator generatorClass :
clientGeneratorClasses) {
// Aufruf des Client-Generators
String name = ((IClientGenerator) generatorClass)
.generateClient(thisProject, cu,
servicename, method,
parameters, returnType);
if (name != null) {
messageString += "- client " + name + " built\n";
}
}
}
return messageString;
}

Die Methode generateClients wird wiederum innerhalb der Methode deploy von
SoapDeployer aufgerufen:

public void deploy(ICompilationUnit cu) throws Exception {


String messageString = "";

...

/*
* Erstellen von Clients, falls entsprechende Plug-Ins
* vorhanden sind
*/
String host = valueDialog.getHost();
String port = valueDialog.getPort();
messageString += generateClients(cu, methodsToDeploy,
servicename, host, port);

...

10.17 Ein Generator für SWT Clients


Da wir nicht warten wollen, bis jemand eine Extension für unseren neuen Extension Point
schreibt, werden wir nun einen eigenen Client Generator erstellen. Der Einfachheit halber
soll so wenig Java Code wie möglich generiert werden. Der Client selbst soll möglichst ge-
nerisch sein.

621
eclipse_v01.book Seite 622 Montag, 30. Januar 2006 12:02 12

Ein Generator für SWT Clients

Die nachfolgende Klasse Client_HelloWorldService_sayHello1 implementiert ei-


nen SWT Client für die Methode sayHello des Web Service HelloWorldService:

package sample.client;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.axis2.Constants;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.clientapi.Call;
import org.apache.axis2.om.OMAbstractFactory;
import org.apache.axis2.om.OMElement;
import org.apache.axis2.om.OMFactory;
import org.apache.axis2.om.OMNamespace;
import org.eclipse.jface.window.ApplicationWindow;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;

public class Client_HelloWorldService_sayHello


extends ApplicationWindow {

/**
* Basisadresse
*/
private static final String baseAddress =
"http://127.0.0.1:8080/axis2/services/";

1. Der etwas ungewöhnliche Name mit Unterstrichen soll andeuten, dass diese Methode generiert wurde. Der
Name der Klasse wird folgendermaßen zusammengesetzt:
Client_<Name des Service>_<Name der Methode>

622
eclipse_v01.book Seite 623 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

/**
* Name des Web Service
*/
private String serviceName = null;

/**
* Name der aufgerufenen Methode
*/
private String methodName = null;

/**
* Eingabewerte
*/
private Map<String, String> fields =
new HashMap<String, String>);

/**
* Namen der Parameter des Web Service
*/
private List<String> paramNames = null;

/**
* SWT-Textfeld für den Rückgabewert
*/
private Text resultText;

/**
* SWT-Textfeld für den Fehlertext
*/
private Text errorText;

/**
* Gesamter Fensterinhalt
*/
private Composite composite;

public ClientHelloWorldServiceSayHello() {
super(null);
}

public void setMethodName(String methodName) {


this.methodName = methodName;
}

public void setParamNames(


List<String> paramNames) {
this.paramNames = paramNames;
}

623
eclipse_v01.book Seite 624 Montag, 30. Januar 2006 12:02 12

Ein Generator für SWT Clients

public void setServiceName(String serviceName) {


this.serviceName = serviceName;
}

/**
* Erzeuge ein Feld
*
* @param name
* Name des Feldes
*/
private void createField(final String name) {
Label label = new Label(composite, SWT.RIGHT);
label.setText(name);
final Text text = new Text(composite,
SWT.BORDER);
GridData gridData = new GridData();
gridData.widthHint = 400;
text.setLayoutData(gridData);
text.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
// Schreibe den Inhalt des Feldes in die Hashtable
fields.put(name, text.getText());
}
});
}

/**
* Erzeuge den Fensterinhalt
*
* @param parent
*/
protected Control createContents(
Composite parent) {
composite = new Composite(parent, SWT.NULL);
composite.setLayoutData(new GridData(
GridData.FILL_HORIZONTAL));
GridLayout layout2 = new GridLayout();
layout2.numColumns = 2;
// Felder für Parameter
composite.setLayout(layout2);
if (paramNames != null) {
for (String name : paramNames) {
createField(name);
}
}
// Feld für Rückgabewert
Label resultLabel = new Label(composite,
SWT.RIGHT);

624
eclipse_v01.book Seite 625 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

resultLabel.setText("Result");
resultText = new Text(composite, SWT.BORDER);
GridData resultGridData = new GridData();
resultGridData.widthHint = 400;
resultText.setLayoutData(resultGridData);
resultText.setText("");
resultText.setEnabled(false);
// Feld für Fehlermeldung
Label errorLabel = new Label(composite,
SWT.RIGHT);
errorLabel.setText("Error");
errorText = new Text(composite, SWT.BORDER
| SWT.MULTI);
GridData errorGridData = new GridData();
errorGridData.widthHint = 400;
errorGridData.heightHint = 100;
errorText.setLayoutData(errorGridData);
errorText.setText("");
errorText.setEnabled(false);
// Button 'Send'
Button sendButton = new Button(composite,
SWT.PUSH);
sendButton.setText("Send");
sendButton
.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(
SelectionEvent e) {
errorText.setText("");
resultText.setText("");
OMFactory fac = OMAbstractFactory
.getOMFactory();
OMNamespace omNs = fac
.createOMNamespace(
"http://entwickler.com/eclipsebuch",
"eclipsebuch");
OMElement request = fac
.createOMElement(methodName, omNs);
for (String name : paramNames) {
OMElement param = fac
.createOMElement(name, omNs);
param.addChild(fac.createText(
param, fields.get(name)));
request.addChild(param);
}

try {
Call call = new Call();

625
eclipse_v01.book Seite 626 Montag, 30. Januar 2006 12:02 12

Ein Generator für SWT Clients

EndpointReference endpointReference =
new EndpointReference(
baseAddress + serviceName + "/"
+ methodName);
call.setTo(endpointReference);
call.setTransportInfo(
Constants.TRANSPORT_HTTP,
Constants.TRANSPORT_HTTP,
false);

// Blocking invocation
OMElement result = call
.invokeBlocking("sayHello", request);

OMElement first = result


.getFirstElement();
resultText.setText(first.getText());
} catch (Throwable t) {
t.printStackTrace();
errorText.setText(t.getMessage());
}
}
});
return composite;
}

public static void main(String[] args) {


ClientHelloWorldServiceSayHello swt =
new ClientHelloWorldServiceSayHello();

List<String> paramNames = new ArrayList<String>();


paramNames.add("text");
swt.setParamNames(paramNames);
swt.setMethodName("sayHello");
swt.setServiceName("HelloWorldService");

swt.setBlockOnOpen(true);
swt.open();
Display.getCurrent().dispose();
}
}

626
eclipse_v01.book Seite 627 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

Zunächst werden innerhalb der Methode main die Namen der Paramter über eine Liste, so-
wie Name der Methode und Name des Service übergeben:

List<String> paramNames = new ArrayList<String>();


paramNames.add("text");
swt.setParamNames(paramNames);
swt.setMethodName("sayHello");
swt.setServiceName("HelloWorldService");

Daraus wird innerhalb der Methode createContents ein SWT-Dialog konstruiert (siehe
Abb. 10.25).

Abb. 10.25: Ein Client für den HelloWorldService

Jedes Feld wird über die Methode createField erzeugt. Dazu bekommt createField
den Namen des Felds übergeben:

private void createField(final String name) {

Die über die Felder eingegebenen Werte werden innerhalb der Hash Table fields gespei-
chert. Dies geschieht innerhalb des Modify Listener eines jeden Felds:

text.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
// Schreibe den Inhalt des Feldes in die Hashtable
fields.put(name, text.getText());
}
});

Innerhalb des Selection-Adapters des Buttons SEND wird der Web Service aufgerufen und
das Ergebnis sowie eventuelle Fehlermeldungen werden in den Feldern RESULT bzw. ERROR
des SWT-Dialogs ausgegeben:

627
eclipse_v01.book Seite 628 Montag, 30. Januar 2006 12:02 12

Ein Generator für SWT Clients

sendButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
errorText.setText("");
resultText.setText("");
OMFactory fac = OMAbstractFactory.getOMFactory();
OMNamespace omNs = fac.createOMNamespace(
"http://entwickler.com/eclipsebuch", "eclipsebuch");
OMElement request = fac
.createOMElement(methodName, omNs);
for (String name : paramNames) {
OMElement param = fac.createOMElement(name, omNs);
param.addChild(fac.createText(param, fields.get(name)));
request.addChild(param);
}

try {
Call call = new Call();
EndpointReference endpointReference =
new EndpointReference(
baseAddress + serviceName + "/" + methodName);
call.setTo(endpointReference);
call.setTransportInfo(
Constants.TRANSPORT_HTTP,
Constants.TRANSPORT_HTTP,
false);

// Blocking invocation
OMElement result = call.invokeBlocking("sayHello", request);

OMElement first = result.getFirstElement();


resultText.setText(first.getText());
} catch (Throwable t) {
t.printStackTrace();
errorText.setText(t.getMessage());
}
}
});

Der obige SWT Client besitzt nur zwei Stellen, die pro Methode des Web Service neu ge-
neriert werden müssen:
쐌 Der Name der Klasse
쐌 Die Methode main, innerhalb der Namen und Typen der Parameter des Web Service
übergeben werden müssen
Wir wollen den Code nicht über AST, sondern über das Java Element API generieren. Dazu
erstellen wir ein Java File SWTTemplate.java mit einer Template-Klasse namens xxx. Die

628
eclipse_v01.book Seite 629 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

Template-Klasse enthält den festen Code, der nicht generiert werden muss, d.h. alles bis auf
die Methode main:

package sample.clients;

import java.lang.reflect.Constructor;
...

public class xxx extends ApplicationWindow {

(...)

Für die Generierung des Clients muss nun der Name der Klasse geändert und die Methode
main erzeugt werden.

10.18 Das SWT-Client-Generator-Plug-In


Wir erzeugen uns ein Plug-In mit Namen com.entwickler.eclipsebuch.soapClientGenera-
tor1, das als einzige Extension eine Extension für den Extension Point com.entwickler.
eclipsebuch.soapDeployer.generator implementiert. Dort erstellen wir mit FILE | NEW | FILE
eine Datei mit Namen SWTTemplate.java. In diese Datei legen wir die Template-Klasse
xxx.
Da wir in diesem Plug-In eine Extension für den Extension Point com.entwickler.eclipse-
buch.soapDeployer.generator implementieren wollen, benötigen wir Klassen aus diesem
Plug-In. Darüber hinaus benötigen wir noch ein paar der Standard-Plug-Ins von Eclipse.
Insgesamt müssen wir folgende Plug-Ins mit dem Button ADD... auf der Seite DEPENDENCIES
des Plug-In Manifest Editors hinzufügen:
쐌 com.entwickler.eclipsebuch.soapDeployer
쐌 org.eclipse.core.resources
쐌 org.eclipse.jdt.core
쐌 org.eclipse.ui
쐌 org.eclipse.core.runtime

10.19 Die Erzeugung von Java Code über das Java Element API
Bei der Implementierung der Java-Klasse com.entwickler.eclipsebuch.soap-
ClientGenerator.SoapClientGeneratorSWT, die den SWT Clients generiert, gehen
wir in folgenden Schritten vor:
쐌 SoapClientGenerator muss das Interface IClientGenerator implementieren
(siehe Abschnitt 10.15).

1. Das Plug-In-Projekt befindet sich auf der CD im Verzeichnis Kap_10.

629
eclipse_v01.book Seite 630 Montag, 30. Januar 2006 12:02 12

Die Erzeugung von Java Code über das Java Element API

쐌 Die Methode generateClient navigiert als Erstes zur Wurzel des Package, das das
Java File enthält, das den Web Service implementiert:

public String generateClient(


IProject project,
ICompilationUnit cu,
String servicename,
IMethod method,
Map<String, String> parameter,
String returnType)
throws JavaModelException, IOException {
IJavaElement parent = cu.getParent();
while (parent.getElementType() !=
IJavaElement.PACKAGE_FRAGMENT_ROOT) {
parent = parent.getParent();
}
IPackageFragmentRoot root = (IPackageFragmentRoot) parent;

쐌 Dort wird nun ein neues Package mit Namen sample.clients angelegt:

IPackageFragment newFragment = root


.createPackageFragment("sample.clients", false,
new NullProgressMonitor());

쐌 Als Nächstes erzeugen wir ein Java File mit einer Java-Klasse mit Namen
Client_<Name des Web Service>_<Name der Methode des Web Service>, die den
Code der Template-Klasse xxx enthält. Dazu lesen wir zunächst den Inhalt der Datei
SWTTemplate.java in einen String Buffer ein:

InputStream is = SoapClientGeneratorPlugin.getDefault()
.openStream(new Path("SWTTemplate.java"));
InputStreamReader isr = new InputStreamReader(is);
StringBuffer content = new StringBuffer();
char[] buffer = new char[1000];
int count;
do {
count = isr.read(buffer);
content.append(buffer, 0, count);
} while (count == 1000);

쐌 Dann erzeugen wir in dem oben erzeugten Package ein Java File (ICompilation-
Unit) mit dem Code, den wir aus der Datei SWTTemplate.java gelesen haben:

ICompilationUnit icu = newFragment


.createCompilationUnit(classname + ".java", content
.toString(), true, new NullProgressMonitor());

630
eclipse_v01.book Seite 631 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

쐌 Aus dem eben erzeugten Java File lesen wir nun die Java-Klasse xxx aus:

IType xxxType = icu.getType("xxx");

쐌 Nun erzeugen wir den Code für die main-Methode zunächst in einem String Buffer:

String classname = "Client_" + servicename + "_"


+ method.getElementName();
StringBuffer body = new StringBuffer(
"public static void main(String[] args) "
+ "throws Exception {\n" + classname
+ " client = new " + classname + "();\n");
body.append("List<String> paramNames =
new ArrayList<String>();");
for(String name : parameter.keySet()) {
body.append("paramNames.add(\"" + name + "\");");
}
body.append("client.setParamNames(paramNames);");
body.append("client.setMethodName(\"" +
method.getElementName) + "\");");
body.append("client.setMethodName(\""
+ method.getElementName() + "\");\n");
body.append("client.setServiceName(\"" +
servicename + "\");");
body.append("client.setBlockOnOpen(true);\n");
body.append("client.open();\n");
body.append("Display.getCurrent().dispose();\n}\n");

쐌 Mit dem Code im String Buffer erzeugen wir nun die Methode main an der Klasse xxx:

xxxType.createMethod(body.toString(), null, true,


new NullProgressMonitor());

쐌 Zum Schluss ändern wir den Namen der Klasse xxx in ihren endgültigen Klassen-
namen:

xxxType.rename(classname, true, new NullProgressMonitor());

쐌 Die Methode generateClient gibt den Namen der neu generierten Klasse zurück:

return classname + ".java";


}

631
eclipse_v01.book Seite 632 Montag, 30. Januar 2006 12:02 12

Die Erzeugung von Java Code über das Java Element API

Bevor wir das neue Plug-In austesten, müssen wir eine Extension für den Extension Point
com.entwickler.eclipsebuch.soapDeployer.generator erzeugen. Das komplette Plug-In
Manifest sieht folgendermaßen aus1:

<?xml version="1.0" encoding="UTF-8"?>


<?eclipse version="3.0"?><plugin
id="com.entwickler.eclipsebuch.soapClientGenerator"
name="SoapClientGenerator Plug-In"
version="1.0.0"
provider-name="com.entwickler.eclipsebuch"
class="com.entwickler.eclipsebuch.\
soapClientGenerator.SoapClientGeneratorPlugin">

<runtime>
<library name="soapClientGenerator.jar"/>
</runtime>
<requires>
<import plugin="org.eclipse.core.resources"/>
<import plugin="org.eclipse.jdt.core"/>
<import plugin="com.entwickler.eclipsebuch.soapDeployer"/>
<import plugin="org.eclipse.ui"/>
</requires>

<extension
point="com.entwickler.eclipsebuch.soapDeployer.generator"
<client class="com.entwickler.eclipsebuch.\
soapClientGenerator.SoapClientGeneratorSWT">
</client>
</extension>
</plugin>

Nun können wir die Eclipse Application mit dem bereits in Abschnitt 10.15 benutzten
Workspace starten2.
Wir rufen wie in Abb. 10.14 nochmals den Menüpunkt DEPLOY AS SOAP WEB SERVICE... auf.
Nachdem wir den Button OK betätigt haben (siehe Abb. 10.12), bekommen wir mitgeteilt,
dass ein Client erzeugt worden ist (siehe Abb. 10.26).

1. Der Backslash (\) soll nur anzeigen, dass der Text in der nächsten Zeile fortgesetzt wird. Er darf nicht einge-
geben werden.
2. Der komplette Test-Workspace befindet sich auf der CD im Verzeichnis Kapitel11/test-workspace.

632
eclipse_v01.book Seite 633 Montag, 30. Januar 2006 12:02 12

10 – Plug-In-Entwicklung für das JDT

Abb. 10.26: Der Client ist erzeugt: die Behauptung

Im Package Explorer sehen wir nun, dass ein neues Package sample.clients mit den
Java Files Client_HelloWorldService_sayHello.java und Client_Hello-
WorldService_sayGoodbye.java entstanden ist (siehe Abb. 10.25).

Abb. 10.27: Die Clients sind erzeugt: der Beweis

Gestartet werden die Clients über RUN | RUN AS | SWT APPLICATION. Wenn wir nun den
Client Client_HelloWorldService_sayHello starten und »Welt« im Feld NAME ein-
geben und den Button SEND betätigen, so erhalten wir »Hallo Welt« als Ergebnis des Web
Service im Feld RESULT angezeigt (siehe Abb. 10.28).

Abb. 10.28: Der neue Web Service und sein Client in Aktion

10.20 So ganz nebenbei zum Schluss


Wer die Soap-Nachrichten sehen will, die zwischen Client und Server ausgetauscht werden,
kann die Anwendung tcpmon aufrufen. Dazu erzeugt man sich eine Run Configuration für
die Klasse org.apache.axis2.util.tcpmon. Als Argumente (Seite ARGUMENTS, Ein-
gabefeld PROGRAM ARGUMENTS) übergibt man:

11000 localhost 8080

633
eclipse_v01.book Seite 634 Montag, 30. Januar 2006 12:02 12

So ganz nebenbei zum Schluss

Schickt man nun die Soap-Nachrichten an den Port 11000, so werden diese in der Anwen-
dung tcpmon angezeigt und an den Port 8080 weitergeleitet. Ebenso werden die Antwort-
nachrichten in der Anwendung angezeigt. Dazu ändern wir in den generierten Test-Clients
eine Zeile ab, indem wir »8080« gegen »11000« austauschen:

private static final String baseAddress =


"http://127.0.0.1:11000/axis2/services/";

Wenn wir nun unseren Test-Client starten ,können wir den Austausch der Soap-Nachrich-
ten beobachten (siehe Abb. 10.29)1.

Abb. 10.29: Die Anwendung tcpmon zeigt den Soap-Verkehr an

1. tcpmon ist geeignet jeglichen TCP-Daten-Verkehr anzuzeigen. Man kann sich damit z.B. auch den HTML/
HTTP-Verkehr ansehen.

634
eclipse_v01.book Seite 635 Montag, 30. Januar 2006 12:02 12

11 Eclipse als Rich Client Platform

von Martin Lippert in Zusammenarbeit mit Markus Völter und Bernd Kolb

11.1 Kapitelüberblick

11.1.1 Themenüberblick
Eclipse ist mehr als eine erweiterbare und zugleich integrierte Entwicklungsumgebung. Die
Basis-Plug-In-Technologie von Eclipse kann genutzt werden, um jegliche Art von Anwen-
dungen zu entwickeln. Über die Basis-Plug-In-Technologie von Eclipse hinaus ist es ab der
Version 3.0 möglich, Teile von Eclipse für allgemeine, nicht IDE-artige Anwendungen, so
genannte Rich Clients, zu verwenden. Dazu zählen beispielsweise die Workbench oder
auch der Update-Mechanismus1. Das Eclipse-Projekt nennt diese Zusammenstellung von
generischen Mechanismen und Plug-Ins die Eclipse Rich Client Platform.
In diesem Kapitel wollen wir einen Einblick in die Entwicklung von Anwendungen auf
Basis der Eclipse Rich Client Platform geben und zeigen, wie Anwendungssysteme ins-
besondere für größere Unternehmen von der Eclipse-Technologie profitieren können.

11.1.2 Kapitelstruktur
Im ersten Teil dieses Kapitels widmen wir uns der Plug-In-Basistechnologie von Eclipse.
Sie erlaubt es, Anwendungen aus einer Reihe von Plug-Ins zusammenzusetzen und so gro-
ße Systeme aus kleinen wiederverwendbaren Teilen aufzubauen. Wir werden dies anhand
eines kleinen Beispielprogramms illustrieren. Am Ende des Kapitels sind eine Reihe von
Tipps und Tricks sowie mögliche Fallen in Zusammenhang mit der Plug-In-Technologie
zusammengetragen.
Darauf aufbauend zeigen wir, welche zusätzlichen Möglichkeiten die Eclipse Rich Client
Platform ab der Eclipse Version 3.0 bietet. Auch zu diesem Thema bedienen wir uns eines
kleinen Beispielprogramms. Über eine einfache Beispielanwendung hinaus werden wir uns
mit der Frage beschäftigen, welche Oberflächenbibliotheken mit der Rich Client Platform
verwendet werden können.

11.1.3 Ziele des Kapitels


Das primäre Ziel des Kapitels ist es, den weit verbreiteten Eindruck von Eclipse zu wider-
legen, es würde sich dabei ausschließlich um eine erweiterbare Java-IDE handeln. Statt-
dessen zeigen wir, dass die Eclipse-Technologie als Basis für die generelle Anwendungs-
entwicklung viele Vorteile bietet. Dazu geben wir einen Einblick in die verschiedenen

1. Prinzipiell war dies auch schon vor der Version 3.0 möglich. Allerdings beinhaltet die Workbench der Version
2.1 stets IDE-spezifische Elemente, wie beispielsweise das Projekt-Menü, welches in einer Nicht-IDE-
Anwendung wenig sinnvoll ist.

635
eclipse_v01.book Seite 636 Montag, 30. Januar 2006 12:02 12

Anwendungen auf Basis der Plug-In-Technologie

beteiligten Komponenten. Unser Ziel ist es nicht, diese Komponenten in allen Details aus-
zuführen. Vielmehr wollen wir den Einstieg in diese Technologie erleichtern und möglichst
vielen Lesern nahe legen.

11.2 Anwendungen auf Basis der Plug-In-Technologie


In diesem Abschnitt widmen wir uns zunächst der reinen Plug-In-Technologie, so wie sie
von Eclipse implementiert wird. Dieser Mechanismus kann unabhängig von der grafischen
Workbench eingesetzt werden. Wir zeigen anhand eines kurzen Beispiels, wie diese Plug-
In-Technologie genutzt werden kann, um gängige Business-Anwendungen zu entwickeln.

11.2.1 Motivation
Das komplette Eclipse-SDK zeigt vorbildlich, wie ein großes System aus einer Vielzahl
von kleinen Bestandteilen zusammengesetzt werden kann und trotzdem an der Oberfläche
einen integrierten Eindruck vermittelt. Hinzu kommt, dass das System relativ einfach und
flexibel von unterschiedlichen Teams erweitert wird. Ähnliche Anforderungen werden an
moderne und große Enterprise-Anwendungen gestellt. Daher liegt es nahe zu untersuchen,
ob und inwieweit die Eclipse zugrunde liegenden Mechanismen für die Entwicklung großer
Enterprise-Anwendungen genutzt werden können.
Es sind mehrere Vorteile, die wir auf die Entwicklung von Enterprise-Anwendungen über-
tragen. Zu den wichtigsten zählen:
쐌 Strukturierung des Systems: Der Plug-In-Mechanismus von Eclipse bietet eine Mög-
lichkeit, große Systeme in kleine Bestandteile zu verteilen. Eine solche Strukturierung
existiert in den meisten großen Enterprise-Anwendungen per se (beispielsweise mittels
Layers, Produktbereichen oder Ähnlichem). Sie wird aber in den seltensten Fällen kon-
struktiv umgesetzt. Mit dem Plug-In-Mechanismus haben wir die Möglichkeit, die
Struktur des kompletten Systems konstruktiv auszudrücken. Das bedeutet, dass die Ab-
hängigkeiten zwischen den einzelnen Teilen des Systems explizit definiert werden kön-
nen. Zudem sichert die Runtime von Eclipse zu, dass die definierten Abhängigkeiten
zur Laufzeit eingehalten werden.
쐌 Flexible Erweiterungsmöglichkeiten: Der Plug-In-Mechanismus von Eclipse bietet mit
dem Extension/Extension-Point-Mechanismus eine elegante und einfache Komponen-
tentechnologie. Sie erlaubt es, das System an vordefinierten Stellen zu erweitern, ohne
das komplette System anpassen zu müssen. Dies kann sowohl auf Ebene des UI gesche-
hen als auch darunter.
쐌 Schrittweiser Aufbau: Indem Plug-Ins selbst Extension-Points definieren können, be-
steht die Möglichkeit, eine an der Anwendung orientierte Komponentenarchitektur
schrittweise aufzubauen.

636
eclipse_v01.book Seite 637 Montag, 30. Januar 2006 12:02 12

11 – Eclipse als Rich Client Platform

11.2.2 Sidebar: Bundles versus Plug-Ins


Gegenüber der Version 2.1 verwendet Eclipse seit der Version 3.0 eine komplett neue Run-
time. Sie basiert auf der OSGi-Spezifikation1 und erlaubt es, Plug-Ins dynamisch zu instal-
lieren und zu aktivieren als auch zur Laufzeit des Systems zu deaktivieren und zu deinstal-
lieren (siehe auch Kapitel 9.10). Der Benutzer muss nicht mehr die Workbench neu starten,
wenn er mit dem Update-Manager neue Plug-Ins installiert hat.
Mit der OSGi-basierten Runtime ist Eclipse automatisch um das neue Konzept der Bundles
bereichert worden. Ein Bundle ist das Äquivalent zum Eclipse Plug-In innerhalb der OSGi-
Spezifikation. Bundles dienen in Eclipse letztendlich dazu, das Konzept der Plug-Ins tech-
nisch umzusetzen.
Obwohl seit Eclipse 3.0 die Plug-In-Runtime auf einer OSGi-Implementation beruht, kön-
nen mit Eclipse weiterhin »normale« Eclipse-Plug-Ins entwickelt und genutzt werden. Die
Runtime von Eclipse sorgt dafür, dass die Plug-Ins zur Laufzeit in OSGi-Bundles konver-
tiert werden und so von der OSGi-Runtime verwaltet werden können. Das Programmier-
modell für Plug-Ins ändert sich dadurch nicht2. Deshalb werden wir in den folgenden Ab-
schnitten weiter über Plug-Ins sprechen.

11.2.3 Hello World


Ein einfaches Beispielprogramm soll zeigen, wie prinzipiell eine Anwendung mit dem
Plug-In-Mechanismus von Eclipse entwickelt werden kann.
Gängige Java-Applikationen besitzen einen Einstiegspunkt, über den diese gestartet wer-
den. Normalerweise gibt es dazu eine main-Methode, die von der Java-VM angesprochen
wird. Schreiben wir eine Anwendung hingegen auf Basis der Plug-In Runtime, wird beim
Start des Systems anstelle der main-Methode die Eclipse Runtime von der VM gestartet3.
Wie bekommen wir die Runtime aber dazu, anstelle der normalen Eclipse-IDE-Workbench
unsere Applikation zu starten?
Zunächst verwendet die Eclipse Runtime den Extension-Point/Extension-Mechanismus,
um die vorhandene Applikation zu identifizieren. Dazu bietet die Eclipse Runtime den
Extension-Point »applications« an. Dieser ist im Plug-In org.eclipse.core.runtime
definiert. Für diesen Extension Point ist eine eigene Extension im Rahmen eines zu definie-
renden Plug-In zu erstellen.
Der Extension-Point »applications« definiert für die Erweiterungen das Interface IPlat-
formRunnable. Um eine passende Extension zu definieren, verwenden wir die entspre-
chenden Wizards, die uns der plugin.xml- bzw. der Manifest-Editor zur Verfügung stellt.
Mit dem Editor definieren wir zunächst die neue Extension des Extension-Point »applica-

1. OSGi steht für Open Services Gateway Initiative, siehe auch http://www.osgi.org.
2. Abgesehen von der Tatsache, dass Plug-Ins ab Eclipse 3.0 prinzipiell damit umgehen können müssen, dass sie
selbst und andere Plug-Ins dynamisch installiert und deinstalliert werden können. Wir werden in einem späte-
ren Abschnitt genauer betrachten, was das bedeutet.
3. Wir werden noch sehen, wie wir die Eclipse Runtime im Detail starten.

637
eclipse_v01.book Seite 638 Montag, 30. Januar 2006 12:02 12

Anwendungen auf Basis der Plug-In-Technologie

tions«. Damit uns der Extension-Wizard diesen Extension-Point anbietet, muss unser Plug-
In natürlich das org.eclipse.core.runtime-Plug-In importieren.
Selektieren wir die neue Extension, können wir auf der rechten Seite des Editors der neuen
Applikation eine ID und einen Namen geben. Wir sollten beide Felder ausfüllen. Das macht
es uns später einfach, die Anwendung zu starten.
Die Extension ist allerdings noch nicht komplett fertig. Wir müssen der Extension-Defini-
tion noch passende Elemente hinzufügen. Wir verwenden dazu das Kontextmenü und er-
zeugen ein Unterelement application, welches ein Unterelement run bekommt. Für das
run-Element können wir nun innerhalb des Properties-Bereichs auf der rechten Seite durch
einen Klick auf den »class«-Hyperlink eine passende Klasse für die Erweiterung generie-
ren. Durch den Klick auf den »class«-Hyperlink erscheint der gängige New-Class-Wizard,
der bereits das IPlatformRunnable-Interface eingetragen hat. Die erzeugte Klasse My-
Application sieht dann ungefähr so aus:

package com.entwickler.eclipsebuch.testapplication;

import org.eclipse.core.runtime.IPlatformRunnable;

public class MyApplication implements IPlatformRunnable {


public MyApplication() {
}
public Object run(Object args) throws Exception {
return null;
}
}

Die neue Abhängigkeit zum Plug-In org.eclipse.core.runtime ist von dem Editor
automatisch in die entsprechende Manifest.mf-Datei eingetragen worden. Die Extension-
und Extension-Point-Definitionen werden allerdings weiterhin in der plugin.xml-Datei de-
klariert. Deshalb erzeugt der Editor bei Bedarf automatisch eine entsprechende Datei. Für
unser Beispiel sieht die plugin.xml-Datei nun folgendermaßen aus:

<?xml version="1.0" encoding="UTF-8"?>


<?eclipse version="3.0"?>
<plugin

<extension
id="myapplication"
name="myapplication"
point="org.eclipse.core.runtime.applications">
<application>
<run

class="com.entwickler.eclipsebuch.testapplication.MyApplication">
</run>

638
eclipse_v01.book Seite 639 Montag, 30. Januar 2006 12:02 12

11 – Eclipse als Rich Client Platform

</application>
</extension>

</plugin>

Die neue Extension ist in die plugin.xml-Datei eingefügt worden und verweist auf die
neu generierte Klasse.
Um die Hello-World-Applikation mit ein wenig Leben zu füllen, müssen wir nun die run-
Methode der Extension entsprechend anpassen:

/**
* @see IPlatformRunnable#run
*/
public Object run(Object args) throws Exception {
System.out.println("Hello, World");
return EXIT_OK;
}

Diese Hello-World-Applikation müssen wir noch so einstellen, dass sie anstelle der Work-
bench aufgerufen wird. Dazu starten wir Eclipse entweder von der Kommandozeile aus
oder innerhalb von Eclipse mittels einer Launch-Konfiguration. In der Kommandozeile
müssen wir den folgenden Kommandozeilenparameter angeben:

-application testapplication.myapplication

Die Anwendung wird dabei mit der ID der application-Extension angegeben, der die ID
des Plug-Ins vorangestellt wird. In unserem Fall also: testapplication für das Plug-In
und myapplication für die Extension. Wenn wir eine Runtime-Workbench-Launch-Kon-
figuration verwenden, können wir direkt unsere Anwendung aus der Choicebox »Run an
application« auf dem »Main«-Tab auswählen.
Jetzt können wir in Eclipse eine Runtime-Workbench starten und sehen die »HelloWorld«-
Ausgabe in der Konsole. Bevor wir dem Beispiel eine Swing-Oberfläche hinzufügen, be-
trachten wir die Plug-Ins, die zum Starten der Hello-World-Applikation benötigt werden. In
der Standardkonfiguration werden alle Plug-Ins der verwendeten Eclipse-Installation ver-
wendet. Wenn wir unsere Business-Anwendung irgendwann einmal an den Kunden auslie-
fern, soll nicht die komplette Eclipse-IDE-Installation enthalten sein. Wir ändern also un-
sere Launch-Konfiguration und selektieren nur die folgenden Plug-Ins (neben dem Plug-In
unserer Applikation):
쐌 org.eclipse.core.runtime
쐌 org.eclipse.osgi
Diese Plug-In-Zusammenstellung reicht aus, um die Eclipse Runtime der Version 3.1 zu
starten und die gewünschte »Hello, World«-Ausgabe zu erzielen.

639
eclipse_v01.book Seite 640 Montag, 30. Januar 2006 12:02 12

Anwendungen auf Basis der Plug-In-Technologie

Im nächsten Schritt wollen wir die Anwendung direkt von der Konsole aus und nicht mehr
aus Eclipse heraus starten. Dazu ist die folgende Zeile einzugeben:

javaw -cp startup.jar org.eclipse.core.launcher.Main


-application testapplication.myapplication

Allerdings müssen wir vorher das Plug-In in das Plug-In-Verzeichnis der Eclipse-Installa-
tion kopieren.
Im nächsten Schritt wollen wir die einfache Consolen-Anwendung durch eine Anwendung
ersetzen, die ein grafisches UI basierend auf Swing besitzt. Dazu verändern wir im ersten
Schritt die run-Methode unserer Applikation folgendermaßen:

public Object run(Object args) throws Exception {


JFrame frame = new JFrame("Hello World");
JButton exitButton = new JButton("Exit");
frame.getContentPane().add(exitButton);
frame.pack();
frame.show();
return EXIT_OK;
}

Beim Start der Workbench sehen wir allerdings kein Fenster. Stattdessen ist die Applikation
sofort wieder beendet. Was ist passiert?
Die Antwort findet sich im Design des Interface IPlatformRunnable von Eclipse. Die
Methode run wird zum Starten der Applikation ausgeführt. Kehrt der Kontrollfluss aus die-
ser Methode zurück, beendet Eclipse die laufende Runtime. Das Fenster wird also erzeugt
und auch angezeigt. Allerdings beendet Eclipse anschließend die Applikation und das
Fenster verschwindet sofort wieder.
Wir müssen also dafür sorgen, dass das Ende der run-Methode erst erreicht wird, wenn wir
die Applikation wirklich beenden wollen. Dazu nutzen wir ein Lock-Object innerhalb der
run-Methode:

public Object run(Object args) throws Exception {


final Object lock = new Object();
JFrame frame = new JFrame("Hello World");
JButton exitButton = new JButton("Exit");
exitButton.setActionCommand("exit");
exitButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
synchronized(lock) {
lock.notify();
}
}
});

640
eclipse_v01.book Seite 641 Montag, 30. Januar 2006 12:02 12

11 – Eclipse als Rich Client Platform

frame.getContentPane().add(exitButton);
frame.pack();
frame.show();

synchronized(lock) {
lock.wait();
}

return EXIT_OK;
}

Mit diesem einfachen Lock-Mechanismus erreichen wir, dass die run-Methode erst dann
das return-Statement erreicht, wenn das Lock-Objekt mit notify benachrichtigt wurde.
So kann die Swing-Applikation in ihrem Thread das Ende der Eclipse-run-Methode steu-
ern.

11.2.4 Strukturierung von Anwendungen


Nachdem wir gesehen haben, dass sich Anwendungen auf Basis des Plug-In-Mechanismus
von Eclipse entwickeln lassen, untersuchen wir die Auswirkungen dieser Technologie auf
Enterprise-Anwendungen. Schließlich stellt sich die Frage, wie Enterprise-Anwendungen
aus Plug-Ins zusammengesetzt werden könnten.
Wir nehmen uns wieder das Eclipse-System selbst als Vorbild. Dort erkennen wir beispiels-
weise, dass die Grundkonzepte und Grundbegriffe, aus denen die Anwendung zusammen-
gesetzt werden soll, in eigenen Plug-Ins definiert sind. So wird beispielsweise festgelegt,
dass die Oberfläche einer Eclipse-Anwendung im Wesentlichen aus Views und Editoren
besteht, die in Perspektiven angeordnet werden können. Die IDE-spezifischen Teile von
Eclipse basieren auf Resources, die jeweils für den konkreten Einsatzkontext spezialisiert
werden.
Das Übertragen dieser Kernidee auf Enterprise-Anwendungen führt zu der Überlegung, aus
welchen grundsätzlichen Bestandteilen sich das System zusammensetzen soll. Damit defi-
nieren wir die Grundbegriffe der Architektur der Enterprise-Anwendung. Beispielsweise
könnten wir für ein Bankensystem grundlegende Begriffe wie Bankprodukt, Person oder
Konto identifizieren. Definieren wir entsprechende Spezialisierungen wie Bauspardarle-
hen, Kreditkunde oder Girokonto, können wir auch dazu passende UIs und aufgabenbezo-
gene Werkzeuge finden, während für das allgemeine Konzept einer Person sogar schon ein
allgemeiner Personensucher im System realisiert ist.
Das Verteilen der skizzierten Konzepte auf verschiedene Plug-Ins führt beispielsweise zum
folgenden Aufbau des Systems:
쐌 Produkt-Plug-In: Definiert die Grundkonzepte für Bankprodukte und bietet einen Pro-
duktordner an, mit dem alle verfügbaren Produkte nach verschiedenen Kategorien sor-
tiert angeschaut und durchsucht werden können.
쐌 Personen-Plug-In: Definiert die Grundkonzepte für eine Person im Rahmen einer Bank
und liefert fertige Implementierungen für einen Personensucher.

641
eclipse_v01.book Seite 642 Montag, 30. Januar 2006 12:02 12

Anwendungen auf Basis der Plug-In-Technologie

쐌 Konten-Plug-In: Definiert die Grundkonzepte für ein Konto in der Bank.


쐌 Arbeitsplatz-Plug-In: Definiert den Rahmen für einen grafischen Arbeitsplatz, an dem
der Mitarbeiter in der Bank mit den verschiedenen Werkzeugen (oder Views/Editors)
arbeiten kann.
쐌 Bauspar-Plug-In: Liefert Erweiterungen für die Person, definiert eine Reihe speziali-
sierter Produkte und erlaubt es, Produkte zu einer Bausparfinanzierung zusammenzu-
setzen.
쐌 …
Das Beispiel erhebt keinen Anspruch auf fachliche Integrität und könnte in einem Banken-
projekt natürlich völlig anders aussehen. Wir wollen mit dem Beispiel nur die grundsätzli-
chen Möglichkeiten der Plug-In-Technologie illustrieren. In dem skizzierten Fall könnten
beispielsweise auch Verbundpartner mit ihren Produkten sehr einfach in das System inte-
griert werden. An der Oberfläche würde sich das System stets wie aus einem Guss präsen-
tieren.
Neben der Strukturierung des Systems profitieren wir von weiteren Vorteilen der Plug-In-
Technologie von Eclipse. Besonders interessant für größere Enterprise-Anwendungen ist,
dass sich für jedes Plug-In exakt definieren lässt, welche Klassen und Interfaces zur ver-
öffentlichten Schnittstelle gehören und damit von anderen Plug-Ins verwendet werden dür-
fen. Die klare Unterscheidung zwischen der öffentlichen API eines Plug-In und seiner in-
ternen Implementation fördert den klaren modularen Aufbau von Systemen und verhindert
nicht gewünschte Abhängigkeiten. Es ist anderen Plug-Ins nicht mehr möglich, auf die in-
terne Implementation eines Plug-In zuzugreifen. Dies führt das Kapselungsprinzip konse-
quent fort und erlaubt es uns, jenseits von Klassen öffentliche Schnittstellen und interne Im-
plementationen voneinander zu trennen. Die interne Implementation wird vor anderen
Plug-Ins verborgen und kann so deutlich einfacher verändert werden, ohne unerwünschte
Seiteneffekte zu verursachen.
Darüber hinaus unterstützt die Eclipse-IDE diese Art der Kapselung mit zusätzlichen Op-
tionen. Der Entwickler bekommt so vom Compiler sofort bei der Arbeit im Code-Editor
Feedback darüber, ob ein unerlaubter Zugriff auf Interna eines Plug-In stattfindet oder
nicht.

11.2.5 Tipps, Tricks und mögliche Fallen


Der Aufbau von Business-Anwendungen aus Plug-Ins auf Basis der Eclipse Runtime er-
zwingt es, dass die Anwendung bestimmte Regeln und Rahmenbedingungen einhält. Die
Regeln werden durch die Eclipse Runtime definiert und müssen eingehalten werden. Wir
listen im Folgenden die wichtigsten Regeln auf und stellen eine Reihe typischer Probleme
und dazu passender Lösungen vor.
Class-Loading: Eclipse erzeugt für jedes Plug-In einen eigenen Classloader. Damit er-
reicht die Eclipse Runtime, dass die Namensräume verschiedener Plug-Ins klar voneinan-
der getrennt und selbst mehrere Versionen unterschiedlicher Bibliotheken von unterschied-
lichen Plug-Ins verwendet werden können, ohne dass sich diese Libraries gegenseitig
stören. Die Eclipse Runtime wird daher auch manchmal als Class-Loader-Framework be-
zeichnet.

642
eclipse_v01.book Seite 643 Montag, 30. Januar 2006 12:02 12

11 – Eclipse als Rich Client Platform

Die speziellen Classloader haben normalerweise für die Plug-Ins keine besonderen Aus-
wirkungen. Allerdings müssen die Plug-Ins darauf ausgelegt sein, statt von den Standard-
ClassLoadern des JDK von einem speziellen Classloader geladen zu werden. Zugriffe auf
die Methode ClassLoader.getSystemClassLoader sollten daher innerhalb eines
Plug-In nicht stattfinden (da der SystemClassLoader nicht den Code der einzelnen Plug-Ins
laden kann).
Für selbst entwickelte Plug-Ins ist es normalerweise auch nicht nötig, auf einen Classloader
explizit zuzugreifen. Müssen spezielle Klassen dediziert geladen werden, kann dies in
Eclipse 3.1 auch über die Bundles geschehen.
Ein Problem kann aber dann wieder auftreten, wenn Third-Party-Libraries eingesetzt wer-
den. Verwendet eine Third-Party-Library direkt den SystemClassLoader und wird diese
Library selbst in ein Plug-In verpackt, kann der SystemClassLoader nicht mehr auf die
Klassen der Library zugreifen. Ein expliziter Aufruf der Library auf den SystemClass-
Loader, um eine eigene Klasse zu laden, wird fehlschlagen. Ob ein solcher Fall eintritt,
hängt natürlich stark davon ab, wie die Library implementiert ist und wozu sie den System-
ClassLoader nutzt. Wenn man Zugriff auf den Quellcode der Library hat, lässt sich das Pro-
blem in der Regel sehr einfach beheben, indem der Aufruf des SystemClassLoader durch
einen Aufruf des eigenen Plug-In-Classloader ersetzt wird.
Muss die Third-Party-Library per Reflection Objekte von Klassen anderer Plug-Ins erzeu-
gen (typische Beispiele hierfür sind Persistenz-Libraries wie Hibernate oder JDO), könnte
man das Problem im ersten Schritt dadurch versuchen zu lösen, dass das Library-Plug-In
als von den entsprechenden anderen Plug-Ins abhängig deklariert wird. Allerdings führt
dies in der Regel zu zyklischen Abhängigkeiten, da die anderen Plug-Ins im System ja ge-
rade diese Library verwenden möchten. Wir müssen also eine andere Lösung finden, um es
der Library zu erlauben, per Reflection die Klassen anderer Plug-Ins zu nutzen.
Seit der Eclipse-Version 3.1 bietet die Runtime das so genannte Buddy-Loading an. Mit
einem entsprechenden Eintrag im Manifest lässt sich definieren, dass das Library-Plug-In
den Buddy-Loading-Mechanismus nutzen möchte. Andere Plug-Ins können dann als Bud-
dies von dem Library-Plug-In deklariert werden. Damit ist es dem Library-Plug-In erlaubt,
auf die Klassen der als Buddies eingetragenen Plug-Ins zuzugreifen. Alternativ dazu kön-
nen auch andere Strategien für das Buddy-Loading ausgewählt werden, um es dem Plug-In
zu erlauben, global auf alle Klassen aus allen Plug-Ins im System zuzugreifen, wobei die
erste Variante softwaretechnisch sauberer erscheint. Mehr zum Thema Buddy-Loading fin-
det sich auch in der Eclipse-Hilfe unter Platform Plug-in Developer Guide/Reference/
Other reference information/Third party libraries and classloading.
Swing Custom Look&Feels: Der Look&Feel-Mechanismus in Swing ist ein Beispiel da-
für, wie das Classloading von Eclipse zu Problemen führen kann. Innerhalb der Swing-
Libraries werden die Look&Feels direkt über den Classloader per Reflection erzeugt. Wäh-
rend es bei den Standard-Look&Feels noch keine Probleme bereitet (da sie im System-
ClassPath liegen), verursachen selbst definierte Look&Feels Probleme, wenn sie innerhalb
eines Plug-In implementiert werden. In diesem Fall muss dem Swing-Mechanismus mitge-
teilt werden, mit welchem Classloader die Klassen des Look&Feel geladen werden können.

643
eclipse_v01.book Seite 644 Montag, 30. Januar 2006 12:02 12

Anwendungen auf Basis der Plug-In-Technologie

Dies kann über die folgende Zeile geschehen:

UIManager.put("ClassLoader", getClass().getClassLoader());

Durch Aufruf von getClass().getClassLoader() erhält man den ClassLoader der


aktuellen Klasse. Befindet sich diese Zeile in genau dem Plug-In, welches auch das
Look&Feel liefert, kann der ClassLoader auch die Klassen des Look&Feel anziehen.
Serialisierung: Der Standard-Serialisierungsmechanismus von Java wird häufig verwen-
det, um möglichst einfach Daten persistent abzulegen oder Kopien von Objekten zu erstel-
len. Werden die Objekte aus einem Stream eingelesen, erzeugt der ObjectInputStream die
entsprechenden Objekte und liest deren Daten aus dem Stream ein. Dazu muss der Object-
InputStream jedoch wissen, wo die entsprechenden Klassen zu finden sind. Befinden sich
beispielsweise in dem Stream serialisierte Objekte mehrerer unterschiedlicher Plug-Ins,
wird der ObjectInputStream beim Einlesen der Objekte eine ClassNotFoundException
auslösen. Die entsprechenden Klassen werden nicht gefunden, da sie nicht im System-
ClassPath liegen.
Um dieses Problem zu lösen, gibt es zwei prinzipielle Möglichkeiten:
1. Wir erweitern die Klasse ObjectInputStream und überschreiben die resolve-
Class-Methode, indem wir beispielsweise die gesuchte Klasse selbst laden. Dazu un-
tersuchen wir für jedes installierte Plug-In, ob es die angegebene Klasse laden kann. Der
Vorteil dieser Lösung liegt darin, dass der erweiterte ObjectInputStream einfach zu
implementieren und zu verwenden ist. Der Nachteil dieser Lösung ist, dass es für den
erweiterten Stream unbekannt ist, von welchem Plug-In das serialisierte Objekt stammt.
Er muss also versuchen, ein passendes Plug-In zu finden. Sind mehrere Plug-Ins instal-
liert, die die betreffende Klasse laden können, kann der erweiterte InputStream nicht
entscheiden, von welchem Plug-In die entsprechende Klasse geladen werden soll.
2. Eine etwas kompliziertere Möglichkeit sieht vor, nicht nur den ObjectInputStream zu
überschreiben, sondern auch den ObjectOutputStream anzupassen. Die Idee dabei ist,
zu jeder Klasse die Information, von welchem Plug-In die Klasse stammt, mit in den
Stream zu schreiben (über eine redefinierte Methode annotateClass). Der speziali-
sierte ObjectInputStream kann diese zusätzlichen Informationen auswerten und weiß
damit genau, von welchem Plug-In er die gesuchte Klasse laden muss.
Etwas schwieriger wird diese Situation, wenn der Serialisierungsmechanismus von anderen
Systemteilen verwendet wird, die man selbst nicht ändern kann. Ein Beispiel dafür ist der
beliebte RMI-Mechanismus. Die Serialisierung wird von ihm verwendet, um Parameter-
und Rückgabeobjekte über die Leitung zu transportieren. Dort muss man auf andere Tricks
umsteigen und über den RMIClassLoaderSpi einen spezialisierten Classloader in den RMI-
Mechanismus einhängen. Der spezialisierte Classloader kann dann, wie bei der ersten Lö-
sungsvariante für das Serialisierungsproblem, versuchen, die benötigten Klassen über die
Plug-Ins zu finden.

644
eclipse_v01.book Seite 645 Montag, 30. Januar 2006 12:02 12

11 – Eclipse als Rich Client Platform

11.3 Die Eclipse Rich Client Platform

11.3.1 Motivation
Während die reine Plug-In Runtime schon seit der ersten Version von Eclipse ohne die
IDE-spezifischen Funktionalitäten verwendet werden kann, enthielten die UI-Anteile von
Eclipse vor der Version 3 IDE-spezifische Merkmale. Man konnte zwar die Workbench von
Eclipse (der eigentliche UI-Rahmen) durch Deinstallieren möglichst vieler Plug-Ins soweit
wie möglich entkernen, einzelne Eigenschaften der Workbench ließen sich damit allerdings
noch nicht entfernen. Das Projektmenü war beispielsweise fest in der Workbench ver-
ankert. Allerdings möchte man die Anwender einer normalen Business-Anwendung nicht
unbedingt mit einem Projects-Menü und einem »Rebuild All«-Eintrag beglücken.
Dies hat sich seit der Version 3.0 von Eclipse verändert. Die grundlegenden Plug-Ins sind
komplett von IDE-spezifischen Konzepten befreit worden. Dazu gehört beispielsweise
auch die generische Workbench mit ihren Konzepten wie Views und Editoren. Diese Plat-
form wird im Eclipse-Projekt die Eclipse Rich Client Platform genannt.

...
Help Update Text
(optional) (optional) (optional)
IDE

UI (Generic Workbench)
Resources
(optional)
JFace

SWT

Runtime (OSGi)

Abb. 11.1: Die Bestandteile der Eclipse Platform (Rich Client Platform speziell umrandet)

Mit der Rich Client Platform ist es möglich, deutlich mehr Funktionen der Eclipse Platform
für die Entwicklung genereller Business-Anwendungen wiederzuverwenden. Obwohl wir
hier nicht alle Möglichkeiten durchspielen können, zeigen wir anhand eines kleinen Bei-
spiels, wie man prinzipiell die Eclipse Rich Client Platform verwenden kann.

11.3.2 Hello World


Wir starten mit dem kleinen Beispiel wieder bei der Applikation, die von der Eclipse Run-
time gestartet wird. Dazu realisieren wir, wie oben bereits beschrieben, eine Klasse, die
IPlatformRunnable implementiert und lassen das Plug-In den applications-Exten-
sion-Point der Runtime erweitern. Im nächsten Schritt implementieren wir die run-Metho-
de, indem wir die generische Workbench erzeugen und starten.

645
eclipse_v01.book Seite 646 Montag, 30. Januar 2006 12:02 12

Die Eclipse Rich Client Platform

package com.entwickler.eclipsebuch.rcp;

import org.eclipse.core.runtime.IPlatformRunnable;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.application.WorkbenchAdvisor;

public class RCPApplication implements IPlatformRunnable {

public RCPApplication() {
}
public Object run(Object args) throws Exception {
WorkbenchAdvisor workbenchAdvisor = new RcpWorkbenchAdvisor();
Display display = PlatformUI.createDisplay();
try {
int returnCode = PlatformUI.createAndRunWorkbench(display,
workbenchAdvisor);
if (returnCode == PlatformUI.RETURN_RESTART) {
return EXIT_RESTART;
} else {
return EXIT_OK;
}
}
finally {
display.dispose();
}
}
}

Wir konzentrieren uns auf den hervorgehobenen Teil des Codes. Die restlichen Implemen-
tationen dienen hier nur einer kompletten Darstellung.
Die Workbench selbst kann erzeugt und gestartet werden, indem aus der PlatformUI-
Klasse die entsprechende Methode aufgerufen wird. Diese erwartet zwei Parameter:
쐌 Ein Display: Stellt das Fenster für die SWT-Oberfläche zur Verfügung und kann einfach
über die PlatformUI erzeugt werden.
쐌 Einen Workbench-Advisor: Dieser muss selbst implementiert werden. Er dient dazu,
die Workbench zu konfigurieren. Der spezielle Workbench-Advisor muss die abstrakte
Klasse WorkbenchAdvisor erweitern.

package com.entwickler.eclipsebuch.rcp;

import org.eclipse.ui.application.IWorkbenchWindowConfigurer;
import org.eclipse.ui.application.WorkbenchAdvisor;

646
eclipse_v01.book Seite 647 Montag, 30. Januar 2006 12:02 12

11 – Eclipse als Rich Client Platform

public class RcpWorkbenchAdvisor extends WorkbenchAdvisor {


public String getInitialWindowPerspectiveId() {
return "rcp.perspective1";
}
}

Der Workbench-Advisor muss die abstrakte Methode getInitialWindowPerspec-


tiveId aus der Klasse WorkbenchAdvisor implementieren. Hier wird die ID derjenigen
Perspektive zurückgeliefert, die zu Beginn angezeigt werden soll. In diesem Beispiel haben
wir eine eigene Perspektive definiert. Wir können dies sehr schnell und einfach erledigen,
indem wir uns die plugin.xml-Datei anschauen und auf dem Extension-Tab eine neue
Perspective-Extension definieren und die dazu passende Klasse generieren lassen. Hieraus
entsteht eine leere Implementation einer Perspektive.

package com.entwickler.eclipsebuch.rcp;

import org.eclipse.ui.IPageLayout;
import org.eclipse.ui.IPerspectiveFactory;

public class RcpPerspective implements IPerspectiveFactory {

public RcpPerspective() {
}
public void createInitialLayout(IPageLayout layout) {
}
}

Wichtig ist, dass in der plugin.xml-Datei die Perspektive mit der gleichen ID versehen
wird, mit der wir die Perspektive im Code der Klasse RCPWorkbenchAdvisor identifiziert
haben.
Die plugin.xml-Datei sieht jetzt folgendermaßen aus:

<?xml version="1.0" encoding="UTF-8"?>


<plugin
<extension
id="rcpappl"
name="rcpappl"
point="org.eclipse.core.runtime.applications">
<application>
<run
class="com.entwickler.eclipsebuch.rcp.RCPApplication">
</run>
</application>
</extension>

647
eclipse_v01.book Seite 648 Montag, 30. Januar 2006 12:02 12

Die Eclipse Rich Client Platform

<extension
id="rcpper"
name="rcpper"
point="org.eclipse.ui.perspectives">
<perspective
name="rcp.perspective1"
class="com.entwickler.eclipsebuch.rcp.RcpPerspective"
id="rcp.perspective1">
</perspective>
</extension>
</plugin>

In unserer Implementation des IPlatformRunnable-Interface haben wir zu guter Letzt


noch ein dispose auf dem erzeugten Display aufgerufen. Dieser Aufruf ist unbedingt not-
wendig, da wir dem Display mitteilen müssen, dass die reservierten Ressourcen wieder
freigegeben werden können. Da das SWT eine direkte native Anbindung an das Fenstersys-
tem realisiert, müssen auch die entsprechenden Ressourcen explizit wieder freigegeben
werden. Wird die Anwendung ohne diesen Aufruf beendet, bleiben die entsprechenden
Ressourcen im Betriebssystem reserviert und werden nie wieder freigegeben. Typischer-
weise endet dies irgendwann damit, dass das Betriebssystem keine Handles mehr vergeben
kann und neu gebootet werden muss.
Jetzt kann das Rich-Client-Beispiel starten. Dazu definieren wir eine neue Launch-Konfi-
guration im Run-Menü von Eclipse. Als Applikation wählen wir aus der Choicebox wieder
unsere Anwendung aus. Von der Kommandozeile aus müssten wir die Applikation als Ar-
gument übergeben:

-application rcp.rcpappl

Zusätzlich wählen wir die nötige Zusammenstellung von Eclipse-Plug-Ins aus, die wir be-
nötigen, um unser Beispiel zu starten:
쐌 org.eclipse.core.commands
쐌 org.eclipse.core.expressions
쐌 org.eclipse.core.runtime
쐌 org.eclipse.help
쐌 org.eclipse.jface
쐌 org.eclipse.osgi
쐌 org.eclipse.swt
쐌 org.eclipse.swt.win32
쐌 org.eclipse.ui
쐌 org.eclipse.ui.workbench
Starten wir die neu definierte Launch-Konfiguration, sehen wir eine leere Workbench, in
die eigene anwendungsspezifische Views, Editoren und Ähnliches implementiert werden
können.

648
eclipse_v01.book Seite 649 Montag, 30. Januar 2006 12:02 12

11 – Eclipse als Rich Client Platform

11.3.3 Workbench
Die Workbench zeichnet sich durch eine Reihe von Eigenschaften aus. Sie dient vor allem
als Container für Views und Editoren, welche sicherlich den Hauptbestandteil einer jeden
RCP-Anwendung ausmachen. Neben den Views und Editors bietet uns die Workbench
noch weitere Standardfunktionalitäten an. Dazu zählen Menüs, eine Statuszeile, eine Cool-
bar (manchmal auch Toolbar genannt) und Ähnliches. Diese zusätzlichen Eigenschaften
wollen wir uns ebenfalls zunutze machen.
Um die zusätzlichen Eigenschaften des Workbench-Fensters zu konfigurieren, verwendet
der WorkbenchAdvisor eine weitere Advisor-Klasse, einen so genannten Workbench-
WindowAdvisor. Dazu überschreiben wir zunächst die Methode createWorkbench-
WindowAdvisor in unserem bereits vorhandenen WorkbenchAdvisor, indem wir ein Ob-
jekt unserer eigenen WorkbenchWindowAdvisor-Implementierung zurückgeben:

public class RcpWorkbenchAdvisor extends WorkbenchAdvisor {


...
public WorkbenchWindowAdvisor
createWorkbenchWindowAdvisor(IWorkbenchWindowConfigurer
configurer) {
return new RcpWorkbenchWindowAdvisor(configurer);
}
}

Unsere eigene Implementation des WorkbenchWindowAdvisor sieht dann zunächst folgen-


dermaßen aus:

public class RcpWorkbenchWindowAdvisor extends


WorkbenchWindowAdvisor {
public RcpWorkbenchWindowAdvisor(IWorkbenchWindowConfigurer
configurer) {
super(configurer);
}
}

Wichtig ist, dass wir den Parameter des Konstruktors an die Oberklasse weiterreichen müs-
sen. Über diesen WorkbenchWindowConfigurer können wir später die gewünschten Eigen-
schaften der Workbench einstellen. Beispielsweise wollen wir vor dem Öffnen des Fensters
festlegen, dass sowohl die Statuszeile als auch die Coolbar und das Menü zu sehen sein soll.
Dazu überschreiben wir die preWindowOpen-Methode und schalten die entsprechenden
Eigenschaften der Workbench ein:

public class RcpWorkbenchWindowAdvisor extends


WorkbenchWindowAdvisor {
...
public void preWindowOpen() {
getWindowConfigurer().setShowCoolBar(true);

649
eclipse_v01.book Seite 650 Montag, 30. Januar 2006 12:02 12

Die Eclipse Rich Client Platform

getWindowConfigurer().setShowStatusLine(true);
getWindowConfigurer().setShowMenuBar(true);
}
}

11.3.4 Menüs
Im nächsten Schritt erweitern wir das Beispiel um eine Menüleiste mit einigen Menüein-
trägen. Dazu müssen wir wieder einen entsprechenden Advisor implementieren. In unse-
rem WorkbenchWindowAdvisor überschreiben wir die Methode createActionBarAd-
visor und geben wiederum ein Objekt unserer eigenen Implementation zurück:

public class RcpWorkbenchWindowAdvisor extends


WorkbenchWindowAdvisor {
...
public ActionBarAdvisor
createActionBarAdvisor(IActionBarConfigurer configurer) {
return new RcpActionBarAdvisor(configurer);
}
}

In unserer eigenen Implementierung des ActionBarAdvisor können wir nun unsere Actions
für die Menü- und Toolbar-Einträge definieren. Um Actions zu erzeugen, überschreiben
wir die Methode makeActions:

public class RcpActionBarAdvisor extends ActionBarAdvisor {


private IWorkbenchAction quitAction;
private IWorkbenchAction aboutAction;

public RcpActionBarAdvisor(IActionBarConfigurer configurer) {


super(configurer);
}
protected void makeActions(IWorkbenchWindow window) {
quitAction = ActionFactory.QUIT.create(window);
register(quitAction);
aboutAction = ActionFactory.ABOUT.create(window);
register(aboutAction);
}
}

Als Beispiel nutzen wir hier zunächst bereits von Eclipse zur Verfügung gestellte Actions:
die Exit-Action, die die Workbench beendet, sowie die About-Action, die den About-Dia-
log anzeigt. Wir erzeugen beide Actions über die ActionFactory und registrieren diese
Actions bei unserem RcpActionBarAdvisor. Dieses Registrieren ist notwendig, damit die
Workbench für die Actions die entsprechenden Key-Bindings verwalten kann.

650
eclipse_v01.book Seite 651 Montag, 30. Januar 2006 12:02 12

11 – Eclipse als Rich Client Platform

Nachdem wir die einzelnen Actions erzeugt haben, können wir uns dem eigentlichen Menü
widmen. Dazu überschreiben wir die Methode fillMenuBar:

public class RcpActionBarAdvisor extends ActionBarAdvisor {


...
protected void fillMenuBar(IMenuManager menuBar) {
MenuManager fileMenu = new MenuManager("&File");
menuBar.add(fileMenu);
fileMenu.add(aboutAction);
fileMenu.add(new Separator());
fileMenu.add(quitAction);
}
}

In der Methode fillMenuBar erzeugen wir zunächst ein MenuManager für das Menü mit
dem Namen »File«. Anschließend fügen wir unsere bereits erzeugten Actions hinzu.
Als Menüeinträge erwartet die Eclipse-Plattform generell so genannte Actions. Eine Action
ist jenseits der Menüs ein grundlegendes Konzept in der Eclipse-Plattform. Es wird auch
verwendet, um beispielsweise Icons in Toolbars zu realisieren. Für Actions gibt es entspre-
chende Interfaces und Oberklassen.
Diese nutzen wir im nächsten Schritt, um selbst definierte Actions zu implementieren und
in die Menüs einzubinden. Um dem File-Menü einen selbst definierten Eintrag hinzuzufü-
gen, müssen wir zunächst einmal eine Action implementieren. Dazu erben wir im einfachs-
ten Fall von der Oberklasse Action und überschreiben die run- die getID und die get-
Text-Methode.

package com.entwickler.eclipsebuch.rcp;

import org.eclipse.jface.action.Action;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.ui.PlatformUI;

public class SayHelloAction extends Action {


public String getId() {
return "myactionid";
}
public String getText() {
return "MyAction";
}

public void run() {


MessageDialog.openInformation(PlatformUI.getWorkbench()
.getActiveWorkbenchWindow().getShell(), "Title",
"This is my message!");
}
}

651
eclipse_v01.book Seite 652 Montag, 30. Januar 2006 12:02 12

Die Eclipse Rich Client Platform

Die getID-Methode gibt eine eindeutige ID der Action zurück. Dies wird von der Work-
bench erwartet. Die getText-Methode gibt den Namen der Action zurück. Dies ist auch
der Name, der später im Menü angezeigt wird. Die run-Methode implementiert die eigent-
liche Aktion, die ausgeführt werden soll. In diesem Beispiel nutzen wir einen einfachen In-
formationsdialog.
Als Nächstes müssen wir die Action noch in das File-Menü eintragen. Dazu gehen wir zu-
rück in die entsprechende Methode in unserem ActionBarAdvisor und ergänzen den Ein-
trag in der makeActions-Methode und in der fillMenuBar-Implementierung:

public class RcpActionBarAdvisor extends ActionBarAdvisor {


private IWorkbenchAction quitAction;
private IWorkbenchAction aboutAction;
private SayHelloAction sayHelloAction;
...
protected void makeActions(IWorkbenchWindow window) {
quitAction = ActionFactory.QUIT.create(window);
register(quitAction);
aboutAction = ActionFactory.ABOUT.create(window);
register(aboutAction);
sayHelloAction = new SayHelloAction();
register(sayHelloAction);
}
protected void fillMenuBar(IMenuManager menuBar) {
MenuManager fileMenu = new MenuManager("&File");
menuBar.add(fileMenu);
fileMenu.add(aboutAction);
fileMenu.add(sayHelloAction);
fileMenu.add(new Separator());
fileMenu.add(quitAction);
}
}

Ähnlich wie Menüs können wir auch die Toolbar befüllen. Dazu überschreiben wir in un-
serem ActionBarAdvisor die Methode fillCoolBar nach dem gleichen Muster:

public class RcpActionBarAdvisor extends ActionBarAdvisor {


...
protected void fillCoolBar(ICoolBarManager coolBar) {
ToolBarManager manager = new ToolBarManager();
manager.add(quitAction);
coolBar.add(manager);
}
}

Wir dürfen allerdings nicht vergessen, in der preWindowOpen-Methode die Coolbar sicht-
bar zu schalten.

652
eclipse_v01.book Seite 653 Montag, 30. Januar 2006 12:02 12

11 – Eclipse als Rich Client Platform

11.3.5 Views und Editors


Nachdem wir eine Workbench mit einigen Menüeinträgen und einer Toolbar auf den Bild-
schirm bekommen haben, wollen wir unsere Anwendung mit Leben füllen. Dazu stehen
uns hauptsächlich die Eclipse-Konzepte Views und Editors zur Verfügung.
Wir beginnen mit einem TreeView, der uns eine Reihe von Personen anzeigen soll. Wenn
wir eine Person auswählen, soll sich im nächsten Schritt ein entsprechender Editor für die
Personendaten öffnen. Aber beginnen wir mit dem ersten Schritt, dem TreeViewer.
In den vorangegangenen Kapiteln haben wir bereits gesehen, wie einfache Viewer mit der
PDE in Eclipse implementiert werden können. Da sich Viewer für eine Rich-Client-An-
wendung nicht prinzipiell von Viewern für eine IDE-Ergänzung unterscheiden, können wir
unsere bereits gewonnenen Erkenntnisse über den Bau von Viewern für den Personen-Tree-
View wiederverwenden.

public class TreeView extends ViewPart {

private Tree tree;


private TreeViewer tv;

public void createPartControl(Composite parent) {


tree = new Tree(parent, SWT.SINGLE);
tv = new TreeViewer(tree);
tv.setLabelProvider(new TreeDemoLabelProvider());
tv.setContentProvider(new TreeDemoContentProvider());
tv.setInput(Data.persons);
tv.setAutoExpandLevel(2);
tv.addDoubleClickListener(new DoubleClicked());
}
}

Als Input geben wir dem Viewer ein Array von Personen. Als Content-Provider setzen wir
einen speziellen Provider ein. Der dient dazu, die View mit Informationen über Personen zu
füttern.
Darüber hinaus wollen wir bei einem Doppelklick auf ein Element des TreeViewer einen
entsprechenden Editor öffnen. Dazu haben wir ein Exemplar des als Inner-Class implemen-
tierten DoubleClick-Listener erzeugt und am TreeViewer registriert.

public class DoubleClicked implements IDoubleClickListener {


public void doubleClick(DoubleClickEvent event) {
if (event.getSelection() instanceof IStructuredSelection) {
IStructuredSelection selection = (IStructuredSelection)
event.getSelection();
Person p = (Person) selection.getFirstElement();
if ( !p.isOrdner() || !p.isReadOnly()) {
openEditor(p);

653
eclipse_v01.book Seite 654 Montag, 30. Januar 2006 12:02 12

Die Eclipse Rich Client Platform

}
}
event.getSelection();
}
}

Den entsprechenden Editor zu öffnen, ist nun Aufgabe der Methode openEditor.

private void openEditor(Person data) {


boolean activated = false;
IWorkbenchPage wb =
DemoPlugin.getDefault().getWorkbench().
getActiveWorkbenchWindow().getActivePage();
IEditorReference[] editors = wb.getEditorReferences();
for (int i = 0; i < editors.length; i++) {
if (editors[i].getEditor(false) instanceof PersonEditor) {
PersonEditor ed = (PersonEditor)
editors[i].getEditor(false);
Object o = ed.getObject();
if (o != null) {
if (o.equals(data)) {
wb.activate(ed);
activated = true;
break;
}
}
}
}
if (!activated) {
try {
wb.openEditor(
new PersonEditorInput(data),
PersonEditor.class.getName());
} catch (PartInitException e) {
e.printStackTrace();
}
}
}

Zunächst wollen wir sicherstellen, dass wir den Editor für eine Person nicht mehrfach öff-
nen. Stattdessen wollen wir den passenden Editor aktivieren, wenn er bereits offen ist. Dazu
bekommen wir über die ActivePage des aktiven Workbench-Windows die EditorRefe-
rences der aktuell sichtbaren Editors. Über die Editor-Referenzen können wir iterieren und
prüfen, ob es sich einerseits um einen PersonEditor handelt und andererseits der Person-
Editor die gefragte Person bereits editiert.

654
eclipse_v01.book Seite 655 Montag, 30. Januar 2006 12:02 12

11 – Eclipse als Rich Client Platform

Finden wir keinen schon offenen Editor für die Person, muss ein neuer Editor geöffnet wer-
den. Dies kann nicht direkt geschehen, sondern muss über die WorkbenchPage passieren.
Dazu kann die Workbench-Page angewiesen werden, einen neuen Editor mit dem entspre-
chenden EditorInput zu erzeugen und zu aktivieren.

11.4 Weitere Themen

11.4.1 Swing und/oder SWT


Während es bei der Verwendung des reinen Plug-In-Mechanismus irrelevant ist, mit wel-
chem UI-Toolkit die Oberflächen der Plug-Ins entwickelt werden, basiert die Eclipse Rich
Client Platform auf SWT. Sowohl die generische Workbench als auch die Schnittstellen für
beispielsweise Views und Editors verwenden SWT-spezifische Typen, um die Oberfläche
darzustellen oder einzubetten (siehe Beispielcode oben).
Seit der Eclipse-Version 3 gibt es die Möglichkeit, Swing-Komponenten in vorhandene
SWT-Oberflächen zu integrieren. Damit ist es prinzipiell möglich, Views mit Swing-Ober-
flächen zu gestalten und in der Workbench darzustellen.
Betrachten wir dazu ein kurzes Beispiel:

Display display = new Display();


Shell shell = new Shell(display);
shell.setText("SWT and AWT Example");
Composite statusComp = new Composite(shell, SWT.EMBEDDED);
Frame frame = SWT_AWT.new_Frame(statusComp);
JButton button = new JButton("OK");
frame.add(button);
GridLayout layout = new GridLayout();
shell.setLayout(layout);
shell.open();

Um eine Swing-Komponente einzubetten, muss zunächst ein SWT-Composite mit dem Typ
SWT.EMBEDDED erzeugt werden. Anschließend kann mit der Brücke SWT_AWT über
die Methode new_frame ein AWT-kompatibles Frame erzeugt werden. Das AWT-Frame-
Objekt dient als Behälter für alle möglichen AWT- und Swing-Komponenten.
Es stellt sich allerdings die Frage, ob es sinnvoll ist, AWT/Swing und SWT gemischt zu
verwenden. Die Praxis zeigt, dass das Aussehen der Toolkits unterschiedlich sein kann. Das
führt dazu, dass die Oberfläche innerhalb der Workbench nicht mehr einheitlich aussieht,
sondern einen zusammengestückelten Eindruck macht. Es sollte also sorgfältig abgewogen
werden, ob und wie stark man die Toolkits vermischt.

655
eclipse_v01.book Seite 656 Montag, 30. Januar 2006 12:02 12

Weitere Themen

11.4.2 Eclipse im Client-Server-Umfeld


Die Eclipse Platform ist in allererster Linie eine Plattform für interaktive Client-Anwen-
dungen. Sie ist zunächst entwickelt worden, um den Bau von integrierten Entwicklungsum-
gebungen zu unterstützen. Dabei trat die Frage des serverseitigen Einsatzes von mit der
Plattform entwickelten Anwendungen nicht auf. Heute verwenden wir die Eclipse Rich
Client Platform, um jegliche Art von interaktiven Business-Anwendungen zu entwickeln.
Damit müssen wir uns der Frage stellen, welchen Platz die Eclipse Platform in der Entwick-
lung verteilter Client-Server-Anwendungen einnimmt.
Der heute übliche klassische Ansatz für Client-Server-Systeme sieht den Einsatz eines
Application-Servers vor. Ein Application-Server hat neben vielen anderen auch die Aufgabe,
mehrere serverseitige Anwendungen oder Anwendungsteile gleichzeitig nebeneinander zu
verwalten. Dazu dient in der Regel das Container-Konzept. Unter der Haube verwendet der
Application-Server dazu, neben einigen anderen Techniken, auch selbst definierte Class-
Loader, um die verschiedenen Anwendungen voneinander abgegrenzt laufen zu lassen.
Das zeigt, dass Application-Server und die Eclipse Platform teilweise ähnliche Techniken
verwenden, um ähnliche Ziele zu erreichen. Das bedeutet aber auch, dass sich die Techno-
logien nicht ohne weiteres miteinander kombinieren lassen. Schließlich möchte beispiels-
weise jeder Teil »sein« Classloading nutzen, um Anwendungsteile zu laden und zu verwal-
ten. Obwohl es technisch möglich ist, beispielsweise Tomcat als Webserver oder JBoss als
Application-Server auf Basis der Eclipse-Plattform zu starten und zu verwenden, scheint
dies nicht unbedingt ratsam, da die Technologien miteinander konkurrieren.
IBM geht deshalb den Weg, die Eclipse Rich Client Platform ausschließlich zur Entwick-
lung des Clients einzusetzen. Auf dem Server kommt ein gängiger Application-Server zum
Einsatz, gegebenenfalls ergänzt um einen Portal-Server. Mit dem Portal-Server kann ge-
steuert werden, welche Teile der Anwendungslandschaft innerhalb des Rich-Clients zum
Einsatz kommen sollen.
Möchte man dennoch Eclipse-spezifische Mechanismen wie Extensions und Extension-
Points auch auf dem Server nutzen, kann man versuchen, die entsprechende API von
Eclipse auch ohne spezielles Classloading und Plug-In-Technologie einzusetzen.
Martin Büchi hat dies mit Eclipse 2.1 demonstriert1. Er hat die Implementation der Eclipse-
Runtime so weit angepasst, dass sie auch ohne Classloading- und Plug-In-Mechanismus
genutzt werden kann. Damit ist es möglich, mit dem gleichen Mechanismus sowohl die
Client- als auch die Server-Seite einer Business-Anwendung zu implementieren. Leider
liegt eine solche Implementation für Version 3 noch nicht vor.2
Darüber hinaus existiert in letzter Zeit ein starker Trend, die Eclipse-Runtime auch auf dem
Server einzusetzen (beispielsweise aus dem Eclipse Open Healthcare Project heraus). Auf

1. siehe http://www.choicemaker.com/EclipsePlugins.htm
2. In Eclipse 3.2 wird es eine refaktorisierte Variante des Extension-Point-Mechanismus geben, die es erlaubt,
den Extension-Point-Mechanismus auch ohne OSGi-Runtime zu verwenden. Diese Variante lässt sich bei-
spielsweise einsetzen, um Extension-Points innerhalb eines Application Servers zu verwenden, wenn die
OSGi-Runtime innerhalb des Servers nicht hochgefahren werden kann.

656
eclipse_v01.book Seite 657 Montag, 30. Januar 2006 12:02 12

11 – Eclipse als Rich Client Platform

Mailing-Listen lässt sich auch erkennen, dass bereits Anstrengungen unternommen wer-
den, einen OSGi-Container für Apache Geronimo zu implementieren. Dies würde bedeu-
ten, die Basisinfrastruktur für Eclipse-Plug-In-Anwendungen auch innerhalb eines Appli-
cation-Servers zur Verfügung zu haben, was ein einheitliches Programmiermodell sowohl
für den Client- als auch den Server-Anteil einer Enterprise-Anwendung ermöglichen wür-
de. Es ist also bereits heute abzusehen, dass die Komponententechnologie von Eclipse auch
für serverseitige Anwendungen (oder Anwendungsteile) in Zukunft zur Verfügung stehen
wird.

11.4.3 Eclipse in der Projektpraxis


Die Eclipse Platform ist bereits in verschiedenen Projekten als Client eingesetzt worden.
Ein großes Projekt im Automobilbereich hat dies schon mit Eclipse 2.1 vor der Einführung
der Rich Client Platform durchgeführt. Wir gehen an dieser Stelle kurz auf die gewonnenen
Erfahrungen ein. Die Entscheidung hat sich als absolut richtig herausgestellt, was auch von
mehreren unabhängigen (technischen, wirtschaftlichen und strategischen) Gutachten bestä-
tigt wurde.
쐌 Generell ist es eine gute Idee, nicht mit Plain Swing oder SWT anzufangen. Eine Ober-
fläche mit einer vorgegebenen Struktur wie die Eclipse Workbench ist ein guter Start.
Man kann früh im Projekt eine vernünftige Oberflächenstruktur vorweisen.
쐌 Weiterhin ist es oft von Vorteil, wenn die Anwendung das Windows Look&Feel besitzt.
Außerdem gibt es gewisse Problem bei der Swing-Technologie, die durch die Verwen-
dung von SWT gelöst werden. Diese Aussage gilt prinzipiell auch für Eclipse auf ande-
ren Betriebssystemen: Die Verwendung von nativen Widgets stellt sicher, dass sich die
Oberfläche gut in das Betriebssystem integriert.
쐌 Aus Sicht des Entwicklers spricht auch einiges für SWT/Eclipse. Durch die Verwen-
dung von Eclipse als Plattform brauchen sich die Entwickler nicht um die grundlegende
GUI-Philosophie zu kümmern, die Workbench bringt dies bereits mit.
쐌 Auch die Entwicklung mit SWT kann einfacher und konsistenter als mit Swing sein,
weil SWT eine einfache API für die klassische 80%-Lösung bietet.
쐌 Obwohl SWT/Eclipse relativ neu ist, läuft es vergleichsweise stabil. Der Erfahrung
nach hat SWT/Eclipse deutlich weniger Bugs (oder »unintuitive Features«) als Swing.
Die Kinderkrankheiten sind eliminiert.
Es gibt aber auch durchaus den einen oder anderen Nachteil, der sich bei der Verwendung
von SWT/Eclipse herausstellt. Diese sollen hier auch kurz erwähnt werden:
쐌 SWT/Eclipse bieten deutlich weniger Widgets an als Swing. Insbesondere der 3rd-Par-
ty-Markt für fortgeschrittene GUI-Elemente ist lange nicht so vielfältig wie für Swing.
Auch die Entwicklung eigener oder die Erweiterung vorhandener Widgets ist nicht so
einfach, da die Widgets native Betriebssystemressourcen darstellen. Besteht im GUI
also die Anforderung, eigene spezielle Widgets zu entwickeln oder Widgets zuzukau-
fen, ist bei SWT/Eclipse Vorsicht geboten.
쐌 Die Auswahl an GUI Buildern ist deutlich begrenzter. Man muss sich also darauf ein-
stellen, zumindest Teile des GUI »manuell« zu programmieren.

657
eclipse_v01.book Seite 658 Montag, 30. Januar 2006 12:02 12

Weitere Themen

쐌 Auch die Dokumentation ist nicht so ausführlich wie für Swing. Dies liegt zum einen am
geringeren Alter von SWT/Eclipse, zum anderen aber auch schlicht an der Tatsache, dass
man nicht so viel dokumentieren muss, weil SWT deutlich einfacher und konsistenter ist.
Nichtsdestotrotz muss dieses Manko in die Entscheidung mit einbezogen werden.
쐌 Als letzter Punkt sei noch genannt, dass es natürlich relativ wenig Entwickler mit
nennenswerter SWT/Eclipse-Erfahrung auf dem Markt gibt. Man kann also davon aus-
gehen, dass man sich das nötige Know-how nicht unbedingt »einkaufen« kann. Eine ge-
wisse Einarbeitung ist nötig. Dies stellt natürlich ein Projektrisiko dar und sollte berück-
sichtigt werden.
Im Folgenden beschäftigen wir uns noch mit einigen Vorurteilen und deren Entkräftungen.
Dies kann helfen, das Management davon zu überzeugen, Eclipse als Client-Plattform zu
verwenden.
쐌 Eclipse gehört IBM; wir wollen uns nicht von IBM abhängig machen. Eclipse wurde ur-
sprünglich von IBM entwickelt und dann der Open Source-Gemeinde übergeben. Es
sind damit nicht nur Entwickler von IBM beteiligt.
쐌 Eclipse ist kein Mainstream. Die Zahl der Projekte, die Eclipse als Rich Client einset-
zen, steigt stetig. Eine ganze Reihe großer Firmen setzt strategisch auf die Eclipse-Platt-
form. Dazu gehört neben IBM (für die neuen IDEs, aber auch z.B. für den Lotus-Client)
beispielsweise auch SAP.
쐌 Eclipse ist nicht »100% pure Java«. Das stimmt. Es werden bei SWT ja die nativen
Widgets des Betriebssystems verwendet. Allerdings sind die praktischen Auswirkun-
gen minimal. Es wird lediglich eine betriebssystemspezifische dynamische Bibliothek
(DLL, .so) benötigt, diese ist Teil der Plattforminstallation. Die auf Eclipse basierenden
Anwendungen (Plug-Ins, Features, Extensions, …) sind natürlich völlig portabel. Es
muss lediglich die für das Betriebssystem passende Bibliothek mit ausgeliefert werden.
쐌 Eclipse ist zu groß! Die Eclipse-Plattform ohne die Java-spezifischen Plug-Ins benötigt
je nach Installation zwischen 10 und 15 MB. Dies ist weniger als das aktuelle JRE und
deutlich weniger als das JDK. Insofern stellt dies in den allermeisten Fällen kein wirk-
liches Problem dar.
쐌 Das Deployment ist zu kompliziert! Wie das JDK, kann auch die Eclipse-Plattform mit-
tels einfacher »xcopy-Installation« installiert werden. Es sind keine Windows-Konfigu-
rationen anzupassen. Die Installation von Plug-Ins kann dann entweder auch einfach
durch Kopieren in den plugin-Ordner erfolgen oder mittels des Eclipse-eigenen Update-
Mechanismus dynamisch über das Netz erfolgen.
쐌 Eclipse eignet sich nur als Plattform für IDEs. Wie in diesem Kapitel hoffentlich klar
geworden ist, eignet sich Eclipse spätestens seit der Version 3.0 auch hervorragend als
Client-Plattform für Nicht-IDE-Anwendungen.

658
eclipse_v01.book Seite 659 Montag, 30. Januar 2006 12:02 12

11 – Eclipse als Rich Client Platform

11.4.4 Ausblick
Die Eclipse-Plattform bietet eine gute Basis zur Entwicklung komplexer, gut strukturierter
Rich Clients. Das von Eclipse vorgegebene Programmiermodell, die systematischen Erwei-
terungsmöglichkeiten basierend auf Extensions sowie der integrierte Update-Mechanismus
stellen eine solide Basis dar. Einer Verwendung der Rich Client Platform in größerem Rah-
men steht nichts mehr im Wege.
Auch wenn man die grafische Workbench nicht verwendet, bietet die Runtime mit dem
Plug-In-Mechanismus ein gut durchdachtes Plug-In-Framework. Dieses kann durchaus als
Basis für komponentenbasierte Anwendungen dienen.
Ab Eclipse 3.0 setzt die Runtime intern auf OSGi auf und stellt eine Plug-In-basierte Lauf-
zeitplattform für kleine und eingebettete Systeme dar. Durch die interne Umstellung von
Eclipse auf die OSGi Runtime eignet sich Eclipse nun potenziell auch als Plattform für
Java-basierte Embedded-Systeme. Das entsprechende Eclipse-Projekt eRCP implementiert
darüber hinaus weite Teile der Workbench und des SWT für kleine Devices, so dass auch
Anwendungen für beispielsweise den Nokia Communicator mit der embedded-Variante der
RCP implementiert werden können.

11.5 Zusammenfassung
Dieses Kapitel hat den Zweck, aufzuzeigen, in welchen Ausbaustufen Eclipse und SWT zur
Entwicklung nicht-IDE-artiger Clients verwendet werden können. Dabei gibt es eine ganze
Reihe interessanter Alternativen. Wir wollten außerdem eine Argumentationshilfe bieten,
um die Idee von Eclipse Rich Clients in den Projektalltag einzubringen.
Weitere Informationen zur Eclipse Rich Client Platform finden sich unter:
쐌 http://www.eclipse.org/rcp/: Die Homepage der Rich Client Platform mit vielen weite-
ren Links und Beispielen für RCP-Anwendungen
쐌 http://www.eclipsefaq.org/chris/faq/: Die passende Homepage zum Eclipse-FAQ-Buch,
enthält viele nützliche Tipps für RCP-Anwendungen
쐌 http://eclipsewiki.editme.com/: Wiki zum Thema Eclipse, ebenfalls mit einer eigenen
Rubrik zum Thema RCP-Entwicklung

659
eclipse_v01.book Seite 660 Montag, 30. Januar 2006 12:02 12
eclipse_v01.book Seite 661 Montag, 30. Januar 2006 12:02 12

12 Refactoring to Eclipse

von Matthias Lübken und Martin Lippert

12.1 Kapitelüberblick

12.1.1 Themenüberblick
Einerseits können neue und eigenständige Anwendungen auf Basis des Plug-In-Modells
von Eclipse oder gar der Rich-Client-Platform entwickelt werden. Andererseits stellt sich
die Frage, wie wir mit Anwendungen oder Anwendungsteilen umgehen, die bereits existie-
ren und ohne Eclipse-Plug-Ins oder RCP entwickelt wurden. An diesem Punkt setzt dieses
Kapitel an.
Im Kapitel 2 wurden bereits Refactorings beschrieben, die in Eclipse selbst integriert sind
und die Entwickler unterstützen, Klassen, Methoden und einzelne Codeblöcke umzustruk-
turieren. Wir stellen eine Reihe von Refactorings vor, die Entwicklern dabei helfen, beste-
hende interaktive Anwendungen auf das Eclipse-Modell umzustellen. Diese Refactorings
bewegen sich im Gegensatz zu denen aus Kapitel 2 demnach nicht auf der Ebene elemen-
tarer objektorientierter Konstrukte, sondern fokussieren auf Plug-Ins als Komponenten
einer Anwendung. Da es sich um »echte« Refactorings handelt, verändern die einzelnen
Refactorings nicht die eigentliche Funktionalität der Anwendung. Es wird lediglich die
interne Struktur der Anwendung verändert, um sie in das Plug-In- und RCP-Gerüst von
Eclipse einzupassen.
Die einzelnen Refactorings werden jeweils anhand einfacher Beispiele illustriert. Den
Sourcecode für die Beispiele zu diesem Kapitel finden Sie im Verzeichnis für das Kapitel
auf der beigefügten CD.

12.1.2 Kapitelstruktur
Die einzelnen Refactorings sind in jeweils eigenen Abschnitten dargestellt. Jedes Refac-
toring ist dabei in sich abgeschlossen und kann losgelöst von den anderen Refactorings
angewendet werden. Die Beschreibung der einzelnen Refactorings orientiert sich an der
Struktur, die auch Martin Fowler in seinem Refactoring-Buch1 verwendet.
Die Reihenfolge, in der die einzelnen Refactorings in den folgenden Abschnitten darge-
stellt werden, stellt einen möglichen Pfad für die Umstellung einer kompletten Anwendung
dar. Einige Refactorings aus diesem Kapitel können aber auch losgelöst von den anderen
eingesetzt werden.

1. Martin Fowler: Refactoring – Improving the Design of Existing Code, Addison-Wesley, 1999.

661
eclipse_v01.book Seite 662 Montag, 30. Januar 2006 12:02 12

Die Refactorings im Überblick

12.2 Die Refactorings im Überblick


Eine bestehende Anwendung in einem einzigen Schritt auf eine Eclipse-Architektur umzu-
stellen, kann für ein Projekt ein großes Risiko darstellen. Refactoring-Techniken helfen, ein
solches Risiko zu reduzieren. Der Trick besteht darin, eine Anwendung in mehreren Schrit-
ten umzustellen, wobei die Anwendung selbst nach jedem einzelnen Schritt lauffähig ist
und ihr Verhalten nicht verändert hat.
Die folgenden einzelnen Refactorings helfen, solche sinnvollen Einzelschritte zu finden.
Darüber hinaus geben wir für jedes Refactoring so genannte Mechanics an. Die Mechanics
beschreiben, in welchen kleinen Schritten jedes einzelne Refactoring durchgeführt werden
kann. Der Aufbau der einzelnen Refactorings orientiert sich damit an den Refactoring-
Katalogen von Martin Fowler (s.o.) und Joshua Kerievsky1.
Die einzelnen Refactorings sind:
쐌 Konvertiere Standalone-Anwendung in eine Single-Plug-In-Eclipse-Anwendung
쐌 Extrahiere Bibliotheks-Plug-In
쐌 Extrahiere Erweiterungs-Plug-In
쐌 Verallgemeinere Extension-Point
쐌 Konvertiere Anwendung in RCP-Anwendung
쐌 Ersetze Swing-Fenster durch RCP-View
Diese einzelnen Refactorings gehen davon aus, dass sich die Anwendung prinzipiell gut-
artig gegenüber dem Plug-In-Konzept von Eclipse verhält. Im vorangegangenen Kapitel
haben wir bereits gesehen, dass dies nicht immer selbstverständlich sein muss. Verwendet
beispielsweise eine Applikation einen eigenen Classloading-Mechanismus, wird dieser
höchstwahrscheinlich nicht mit der Eclipse-Plattform harmonieren. Diese Problematik
kann auch bei verwendeten Libraries auftreten. Bevor also eine Applikation auf den Plug-
In-Mechanismus von Eclipse umgestellt wird, sollte überprüft werden, ob diese Randbe-
dingungen von der Anwendung eingehalten werden.

12.3 Refactoring: Konvertiere Standalone-Anwendung


in eine Single-Plug-In-Eclipse-Anwendung
Eine komplette interaktive Anwendung soll zukünftig von dem Plug-In-Mechanismus Ge-
brauch machen und dazu innerhalb der Eclipse-Runtime laufen.
Konvertiere die Anwendung in ein einzelnes Plug-In, welches eine Eclipse-Application be-
reitstellt.

1. Joshua Kerievsky: Refactoring to Patterns, Addison-Wesley, 2004.

662
eclipse_v01.book Seite 663 Montag, 30. Januar 2006 12:02 12

12 – Refactoring to Eclipse

Meine
Anwendung
Meine Anwendung

Eclipse Platform

12.3.1 Motivation
Das Ziel dieses Refactoring ist es, eine normale Java-Standalone-Anwendung auf den Plug-
In-Mechanismus von Eclipse umzustellen. Anstatt die Anwendung direkt zu starten, kann
nach diesem Refactoring die Eclipse-Runtime gestartet werden. Die Eclipse-Runtime über-
nimmt dann das Starten der eigentlichen Anwendung. Diese wird dazu in ein einzelnes
Plug-In »verpackt« und in die Eclipse-Runtime eingesteckt (vgl. HelloWorld-Beispiel, Ka-
pitel 11).
Das Refactoring dient uns als Basis, um darauf aufbauend weitere Umstrukturierungen vor-
nehmen zu können, die direkt von den Features der Eclipse-Runtime Gebrauch machen.

12.3.2 Mechanics
1. Java-Projekt in Plug-In-Project konvertieren
2. Runtime-Libraries im Plug-In-Projekt deklarieren
3. Main-Methode durch Eclipse Extension ersetzen
4. Anwendung testen und ausführen

12.3.3 Beispiel
Zunächst muss das Java-Projekt der Anwendung in ein Plug-In-Projekt überführt werden.
Dazu kann der Wizard »Convert Project to Plug-In Project« benutzt werden, welcher sich
im Kontextmenü des Projekts unter PDE-Tools befindet. Der Wizard erstellt unter anderem
eine einfache Manifest.mf-Datei für die Anwendung:

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Bankxxs
Bundle-SymbolicName: com.entwickler.eclipsebuch.bankxxs
Bundle-Version: 1.0.0
Bundle-Localization: plugin

663
eclipse_v01.book Seite 664 Montag, 30. Januar 2006 12:02 12

Refactoring: Konvertiere Standalone-Anwendung in eine Single-Plug-In-Eclipse-Anwen-

Leider fehlen in der Manifest-Datei nach der Konvertierung des Projekts noch einige wich-
tige Informationen. Wir müssen vor allem die Runtime-Libraries definieren und welche
Packages exportiert werden sollen. In unserem Beispiel müssen wir zunächst das eigene
.jar-File angeben, in welches unser eigener Code kompiliert werden soll. Anschließend
zählen wir alle Packages auf, die exportiert werden. Dazu ergänzen wir die entsprechende
Runtime-Library und die Packages entweder manuell im Source-Code der Manifest-Datei
oder mit Runtime-Wizard des Manifest-Editors. Die Manifest-Datei sollte um folgende
Zeile ergänzt werden:

Bundle-ClassPath: bankxxs.jar
Export-Package: com.entwickler.eclipsebuch.bankxxs

Allerdings ist das JAR-Archiv, in welches unsere eigenen Sourcen kompiliert werden
sollen, nicht die einzige Runtime-Library, die wir benötigen. In unserem ursprünglichen
Projekt verwendeten wir eine zusätzliche Library, die sich direkt als JAR-Datei in einem
Lib-Verzeichnis in unserem Projekt befand. Wir müssen jetzt auch diese Library in der
Manifest-Datei als Bestandteil des Bundle-Classpath definieren:

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Bankxxs
Bundle-SymbolicName: com.entwickler.eclipsebuch.bankxxs
Bundle-Version: 1.0.0
Bundle-Localization: plugin
Bundle-ClassPath: bankxxs.jar,
itext-1.02b.jar
Export-Package: com.entwickler.eclipsebuch.bankxxs

Damit auch das Plug-In-Projekt die neue Library automatisch in den Classpath des Compi-
lers unseres Projekts einfügt, klicken wir mit der rechten Maustaste auf unser neues Plug-
In-Projekt und wählen bei den PDE-Tools »Update Classpath« aus. Anschließend sollte un-
sere externe Library auch als solche im Projekt auftauchen.
Um das Plug-In möglichst einfach mit dem PDE deployen zu können, können wir die zu-
sätzlichen Libraries auch in der build.properties-Datei vermerken. Dazu bedienen wir uns
des entsprechenden Plug-In-Manifest-Editors, der sich nach einem Doppelklick auf die
build.properties-Datei öffnet. Hier können wir die zusätzliche Library dem Binary-Build
hinzufügen. Wenn wir irgendwann einmal das Plug-In aus dem PDE heraus deployen, wird
dann unsere Library mit in das Plug-In-Verzeichnis kopiert werden. Zusätzlich kann man in
der build.properties-Datei festlegen, welche Source-Verzeichnisse in welche .jar-Files
kompiliert werden sollen.
Damit die Eclipse-Runtime die Anwendung starten kann, muss eine Erweiterung zu dem
Extension-Point org.eclipse.core.runtime.applications bereitgestellt werden.
Hierzu wird das Plug-In org.eclipse.core.runtime in der Manifest.mf-Datei zu-
nächst als Required-Bundle definiert. Anschließend können wir die Erweiterung deklarie-

664
eclipse_v01.book Seite 665 Montag, 30. Januar 2006 12:02 12

12 – Refactoring to Eclipse

ren und eine Klasse angeben, die von der Eclipse-Runtime aufgerufen werden soll (vgl. Ka-
pitel 11). Die Bearbeitung der plugin.xml-Datei sollte über den Assistenten geschehen, da
Eclipse mit einer leeren plugin.xml-Datei nicht zurechtkommt. Wichtig dabei ist, dass die
entsprechende Klasse voll qualifiziert angegeben wird, die später gestartet wird. Unsere
plugin.xml-Datei sieht anschließend folgendermaßen aus:

<?xml version="1.0" encoding="UTF-8"?>


<?eclipse version="3.0"?>
<plugin>
<extension
id="startup"
point="org.eclipse.core.runtime.applications">
<application>
<run class="com.entwickler.eclipsebuch.bankxxs.Startup"/>
</application>
</extension>
</plugin>

In der angegebenen Klasse muss nun die bisherige main-Methode der Anwendung ersetzt
werden. Bisher sah unsere Startup-Klasse wie folgt aus:

package com.entwickler.eclipsebuch.bankxxs;

public class Startup {


public static void main(String[] args) {
// Startup Anwendung
}
}

Um den Vertrag mit dem Extension-Point applications zu erfüllen, muss diese Klasse
das Interface IPlatformRunnable und damit eine run-Methode implementieren:

package com.entwickler.eclipsebuch.bankxxs;
import org.eclipse.core.runtime.IPlatformRunnable;

public class Startup implements IPlatformRunnable {


public Object run(Object args) throws Exception {
// Startup Anwendung
return IPlatformRunnable.EXIT_OK;
}
}

Wichtig dabei ist, dass nach Beendigung der Methode ein System.exit() von der Eclipse-
Runtime aufgerufen wird. Das führt bei den meisten Anwendungen dazu, dass die Anwen-
dung nach dem Start direkt wieder geschlossen wird (vgl. Kapitel 11). Da es sich bei unserer
Anwendung aber um eine interaktive Anwendung mit einer Swing-Oberfläche handelt, muss
zusätzlich ein Lock-Objekt eingefügt werden (siehe Kap 11).

665
eclipse_v01.book Seite 666 Montag, 30. Januar 2006 12:02 12

Refactoring: Extrahiere Bibliotheks-Plug-In

Nun kann die Eclipse-Runtime mit der neuen Anwendung als application-Plug-In ge-
startet werden. Ebenso können nun auch JUnit-Tests innerhalb der Eclipse-Runtime ge-
startet werden (RUN AS| JUNIT PLUG-IN TEST). Gerade neue Tests, die Plug-In-Funktionalität
voraussetzen, müssen über diesen Weg ausgeführt werden. Es können aber auch alle ande-
ren JUnit-Tests so gestartet werden. Wenn man innerhalb eines Tests oder zur Laufzeit si-
cherstellen möchte, dass die Eclipse-Runtime läuft, kann man das über Platform.isRun-
ning() abfragen. So lässt sich beispielsweise der Aufruf der run-Methode mit einem
entsprechendem assert absichern.

12.4 Refactoring: Extrahiere Bibliotheks-Plug-In


Ein Teil der Applikation wird bibliotheksartig benutzt, steht aber anderen Plug-Ins nicht als
eigenständige Bibliothek zur Verfügung.
Verschiebe die entsprechenden Klassen in ein neues Plug-In und verwende das Plug-In, wo
es möglich ist.

Meine Anwendung Meine


Anwendung

utils

utils

12.4.1 Motivation
Ein Ziel der plug-in-basierten Anwendungsentwicklung ist es, mittels Plug-Ins die Anwen-
dung softwaretechnisch zu modularisieren. Das bedeutet auch, dass ein zu großes Plug-In
einen so genannten Smell darstellt. Eine mögliche Lösung kann sein, bibliotheksartige Be-
standteile des Plug-In in ein eigenes Plug-In zu extrahieren. Dabei verbleiben alle Exten-
sions und Extension-Points im ursprünglichen Plug-In. Lediglich bibliotheksartig verwen-
dete Teile des Source-Codes werden in ein neues Plug-In verschoben.

12.4.2 Mechanics
1. Neues leeres Plug-In erzeugen
2. Neues Plug-In vom Basis-Plug-In verwenden lassen
3. Klassen in das neue Plug-In verschieben
4. Testen

666
eclipse_v01.book Seite 667 Montag, 30. Januar 2006 12:02 12

12 – Refactoring to Eclipse

12.4.3 Beispiel
Zunächst erstellen wir ein neues Plug-In-Projekt mit dem entsprechenden Wizard (FILE |
NEW | PROJECT | PLUG-IN PROJECT). In dem Wizard sollte auf der zweiten Seite ein Java-Pro-
jekt und auf der dritten Seite eine Plug-In-Klasse ohne UI-Funktionalität gewählt werden.
Im nächsten Schritt müssen wir es dem ursprünglichen Plug-In gestatten, auf Klassen des
neuen Plug-Ins zuzugreifen. Hierzu deklarieren wir eine require-Beziehung in der Mani-
fest-Datei des ursprünglichen Plug-In:

Require-Bundle: org.eclipse.core.runtime,
com.entwickler.eclipsebuch.utils

In einem letzten Schritt müssen wir die entsprechenden Klassen und die zugehörigen Tests
in das neue Plug-In verschieben. Dabei müssen wir sehr genau hinschauen, welche Klassen
wir in das neue Plug-In verschieben. Zwar verwendet das ursprüngliche Plug-In unser neu-
es Plug-In und kann deshalb auch auf alle Klassen zugreifen, die wir in das neue Plug-In
verschieben. Allerdings darf das neue Plug-In das ursprüngliche Plug-In nicht referenzie-
ren, da ansonsten eine zyklische Abhängigkeit zwischen den beiden Plug-Ins entstehen
würde. Deshalb dürfen die Klassen, die wir in das neue Plug-In verschieben, keine Refe-
renzen auf Klassen besitzen, die im ursprünglichen Plug-In verbleiben sollen. Glücklicher-
weise hilft uns das Plug-In-Development-Environment von Eclipse insofern weiter, als dass
wir sofort entsprechende Compile-Fehler angezeigt bekommen, sollten in dem neuen Plug-
In Klassen referenziert werden, die sich (noch) nicht in dem neuen Plug-In befinden1.
Haben wir die ausgewählten Klassen in das neue Plug-In verschoben, müssen wir gegebe-
nenfalls einige Plug-Ins in die Manifest.mf-Beschreibung des neuen Plug-In als required
aufnehmen. Dies ist dann der Fall, wenn die verschobenen Klassen andere Typen verwen-
den, die nicht Bestandteil des ursprünglichen Plug-In waren, sondern von einem anderen
Plug-In geliefert werden.

12.5 Refactoring: Extrahiere Erweiterungs-Plug-In


Ein Teil der Applikation wird direkt verwendet, hat aber eher den Charakter einer Erweite-
rung.
Erzeuge einen neuen Extension-Point und verschiebe den erweiterungsartigen Bestandteil
als Extension in ein neues Plug-In.

1. Sollte in der Anwendung Reflection verwendet werden, kann der Compiler natürlich nicht mehr alle Probleme
identifizieren. Für einen solchen Fall dienen die Unit-Tests der entsprechenden Plug-Ins als Sicherheitsnetz.

667
eclipse_v01.book Seite 668 Montag, 30. Januar 2006 12:02 12

Refactoring: Extrahiere Erweiterungs-Plug-In

Bankanwendung Bankanwendung

CheckingAccount
Tool CheckingAccount
Tool

12.5.1 Motivation
Ein Teil der Funktionalität eines Plug-In soll in ein eigenes Plug-In extrahiert werden.
Allerdings handelt es sich bei der Komponente, die extrahiert werden soll, nicht um eine
bibliotheksartig verwendete Komponente. Vielmehr erweitert die zu extrahierende Kom-
ponente die Funktionalität eines Plug-In. Aus diesem Grund kann die zu extrahierende
Komponente nicht in ein einfaches bibliotheksartiges Plug-In verpackt werden.
Stattdessen bedienen wir uns des Extension-Mechanismus von Eclipse, der es uns ermög-
licht, die Funktionalität einer Anwendung an einer vordefinierten Stelle zu erweitern.

12.5.2 Mechanics
1. Extension-Point entwerfen
2. Extension-Point und Extension deklarieren
3. Extensions verarbeiten
– Passende Extension finden
– Extension validieren
– Extension aktivieren
4. Testen mit und ohne Extension
5. Ein neues Plug-In definieren
6. Extension in neues Plug-In verschieben
7. Testen

12.5.3 Beispiel
Bevor eine Extension implementiert werden kann, müssen wir einen Extension-Point ent-
werfen. Dazu definieren wir, was eine Extension liefern soll, um erfolgreich mit dem Ex-
tension-Point verbunden zu werden. In einer Anwendung wurde beispielsweise bisher ein
bestimmtes Konto, z.B. CheckingAccount, direkt erzeugt:

668
eclipse_v01.book Seite 669 Montag, 30. Januar 2006 12:02 12

12 – Refactoring to Eclipse

CheckingAccount account = new CheckingAccount();


account.setOwner("Hans Zimmermann");
account.setAccountNumber("4711");

Die Extension muss also eine Account-Klasse mit bestimmten Attributen liefern, die dann
gesetzt werden können. Wir definieren ein passendes Interface, welches eine Extension un-
seres neuen Extension-Point implementieren muss:

package com.entwickler.eclipsebuch.bankxxs;

public interface IAccount {


public void setOwner(String ownerName);
public void setAccountNumber(String accountNumber);
}

Durch das in Eclipse eingebaute Refactoring »Extrahiere Interface« lässt sich ein Interface-
Typ leicht erstellen und vom CheckingAccount implementieren.
Als Nächstes können wir zu dem neuen Interface eine passende Extension definieren. Für
die Deklaration des Extension-Point in dem Basis-Plug-In muss lediglich eine Id angege-
ben werden1:

<extension-point id="checkingaccount"/>

Wir können nun die Implementierung des CheckingAccount als Extension zu diesem
Extension-Point definieren. Im ersten Schritt definieren wir dazu die Extension im gleichen
Plug-In wie den Extension-Point. Das bedeutet, dass unser Basis-Plug-In sowohl den
Extension-Point als auch eine Extension für diesen Extension-Point definiert. Diese Art,
den Extension-Mechanismus zu verwenden, ist keineswegs unüblich und wird auch in den
Eclipse-eigenen Plug-Ins angewandt.
Wie aber sieht eine passende Extension aus? Wir wissen, dass eine Extension eine Klasse
angeben muss, die später erzeugt und benutzt wird. Der Plug-In-Mechanismus sieht hierzu
das class-Attribut als Konvention vor. Allerdings kann dieses Attribut nur innerhalb eines
neuen XML-Elements definiert werden. Eine passende Extension sieht demnach in der
plugin.xml wie folgt aus:

<extension
point="com.entwickler.eclipsebuch.bankxxs.checkingaccount"
id="checkingaccount">
<account class="com.entwickler.eclipsebuch.bankxxs.checking.
CheckingAccount"/>
</extension>

1. Das Schema haben wir der Einfachheit halber an dieser Stelle weggelassen.

669
eclipse_v01.book Seite 670 Montag, 30. Januar 2006 12:02 12

Refactoring: Extrahiere Erweiterungs-Plug-In

Nachdem wir bis jetzt einen neuen Extension-Point definiert und eine entsprechende Ex-
tension in die plugin.xml-Datei eingefügt haben, müssen wir das Plug-In, welches den Ex-
tension-Point anbietet, so weit erweitern, dass es den Extension-Point auswertet und Exem-
plare der verfügbaren Extensions erzeugt, statt den CheckingAccount direkt zu erzeugen.
Der dazu passende Code wird zunächst direkt in die entsprechende Klasse eingefügt und er-
setzt die bisherigen Zeilen, in denen der Account direkt erzeugt wurde. Später lässt sich die-
ser Code gegebenenfalls verallgemeinern.
Zunächst muss an der Extension-Registry angefragt werden, ob zu einem Extension-Point
eine passende Extension vorhanden ist. Dabei wird zusätzlich geprüft, ob genau eine Ex-
tension vorhanden ist. Dies ist notwendig, da zurzeit noch nicht entschieden werden kann,
welche Extension benutzt werden soll, falls mehrere vorhanden sind. 1

String xPId =
"com.entwickler.eclipsebuch.bankxxs.checkingaccount";
IExtensionRegistry registry = Platform.getExtensionRegistry();
IExtensionPoint extPoint = registry.getExtensionPoint(xPId);
IExtension [] extensions = extPoint.getExtensions();
if (! extensions.length == 1)
// === Error, da nur eine Extension möglich
IExtension ext = extensions [0];

Die gefundene Extension muss nun nach der Vorgabe des Extension-Point validiert werden.
In unserem Fall muss überprüft werden, ob die Extension ein Account-Element mit Class-
Attribut deklariert. Ob die entsprechende Klasse das richtige Interface implementiert, lässt
sich erst überprüfen, wenn das Plug-In aktiviert wurde.

IConfigurationElement elements = ext.getConfigurationElements();


assert elements.length == 1 : "Just one nested element";
IConfigurationElement accountElement = elements[0];
assert accountElement.getName().equals("account") : "The element’s
name has to be account";
assert accountElement.getAttribute("class") != null : "The account
has to define a class";

Zum Schluss kann das CheckingAccount erzeugt werden. Hierzu wird die Methode
createExecutableExtension aufgerufen. Diese Methode lädt die entsprechende Klas-
se aus dem neuen Plug-In und erzeugt ein Exemplar. Mit diesem Methodenaufruf wird das
neue Plug-In gleichzeitig aktiviert. Anschließend können die Attribute wie OwnerName
gesetzt werden.

1. Im nächsten Abschnitt wird ein Extension-Point vorgestellt, der mehrere Extensions erlaubt.

670
eclipse_v01.book Seite 671 Montag, 30. Januar 2006 12:02 12

12 – Refactoring to Eclipse

IAccountTool account = null;


Object exeExt =
accountElement.createExecutableExtension("class");
if (exeExt instanceof IAccount) {
account = (IAccount) exeExt;
} else {
// === Error ===
// Account-Element doesn't define an instance of IAccount
}

Die Anwendung ist an diesem Punkt wieder voll funktionsfähig und kann gestartet werden.
Bis jetzt befinden sich Extension-Point und Extension noch in einem gemeinsamen Plug-
In. Das ändern wir, indem wir zunächst ein neues Plug-In-Projekt für die Checking-
Account-Erweiterung erzeugen. Das Plug-In sollte in seiner Manifest.mf -Datei eine Ab-
hängigkeit zum Basis-Plug-In definieren.
Dann können wir sowohl die Definition der CheckingAccount-Extension in das neuen
Plug-In verschieben als auch den entsprechenden Code. Das Ergebnis dieser Verschiebe-
Operationen ist wieder eine komplett lauffähige Anwendung, die wir ausführen und testen
können.

12.6 Refactoring: Verallgemeinere Extension-Point


Es existieren mehrere Extension-Points in einem Plug-In, die in ihrer Funktionalität sehr
ähnlich sind.
Verallgemeinere verschiedene Extension-Points zu einem, so dass Extensions mit ähnlicher
Funktionalität zusammengelegt werden können.

Meine Meine
Anwendung Anwendung

Extension-Plug-In Extension-Plug-In Extension-Plug-In Extension-Plug-In


A B A B

12.6.1 Motivation
In diesem Refactoring werden verschiedene Extension-Points zu einem Extension-Point
verallgemeinert, so dass unterschiedliche Extensions möglich sind und die Anwendung da-
mit erweiterbar wird. Dabei werden verschiedene Extension-Points mit ähnlicher Funktio-
nalität zu einem allgemeineren Extension-Point zusammengeführt.

671
eclipse_v01.book Seite 672 Montag, 30. Januar 2006 12:02 12

Refactoring: Verallgemeinere Extension-Point

12.6.2 Mechanics
1. Neuen allgemeinen Extension-Point entwerfen
2. Unterschiedliche Extensions generisch verarbeiten
3. Extension-Points und Extensions umdeklarieren
4. Testen

12.6.3 Beispiel
In diesem Refactoring werden verschiedene Extension-Points zu einem zusammengeführt.
In unserem Beispiel sind das »checkingaccount« und »insuranceaccount«, die bisher je-
weils eigene Extension-Points waren und zu dem Extension-Point »account« zusammen-
geführt werden. Wenn nun ein Account erzeugt werden soll, muss das richtige Werkzeug
ausgesucht werden. Es muss also ein Entscheidungskriterium eingeführt werden, welches
die richtige Extension für die gewünschte Funktionalität findet. In unserem Fall muss eine
Extension zu dem passenden Account-Typ gefunden werden. Die Extension muss also mit-
teilen, mit welcher Klasse von Account (z.B. Checking oder Insurance) sie arbeitet. Eine
Extension könnte demnach wie folgt aussehen:

<extension point="com.entwickler.eclipsebuch.bankxxs.account">
<account
class="com.entwickler.eclipsebuch.bankxxs.checking.CheckingAccoun
t"
label="Checking Account"
type="checking">
</account>
</extension>

Die Verarbeitung der Extensions kann nun generisch geschehen. Wenn ein Account bear-
beitet werden soll, werden alle Extensions durchgegangen und befragt, ob sie mit dem ent-
sprechenden Account arbeiten können. Eine einfache Version könnte wie folgt aussehen:

String accountType = account.getType();


IExtension[] exts = extPoint.getExtensions();
for (int i = 0; i < exts.length; i++) {
IConfigurationElement elem =
exts[i].getConfigurationElements()[0];
if (elem.getAttribute("type").equals(accountType)) {
// aktiviere Extension wie bisher
}
}

Zuerst kann der neue Extension-Point zusätzlich zu dem alten deklariert werden. Anschlie-
ßend lassen sich die einzelnen Extensions von dem einen auf den anderen Extension-Point
umdeklarieren. Zum Schluss können dann die Deklarationen der alten Extension-Points ge-
löscht werden.

672
eclipse_v01.book Seite 673 Montag, 30. Januar 2006 12:02 12

12 – Refactoring to Eclipse

12.7 Refactoring: Konvertiere Anwendung in RCP-Anwendung


Eine existierende Anwendung basiert auf Swing, soll aber in Zukunft Gebrauch von der
Eclipse Rich Client Platform machen.
Starte die Anwendung als Plug-In der Rich Client Platform und konvertiere das erste Fens-
ter zu einer RCP-View.

Meine
Anwendung
Meine Anwendung

Eclipse RCP

12.7.1 Motivation
In diesem Refactoring wird eine Swing-basierte Anwendung so umgestellt, dass das erste
Fenster dieser Anwendung als eine RCP-View angezeigt wird. Dabei wird die in Eclipse in-
tegrierte SWT-Bridge benutzt, um Swing-Komponenten direkt in SWT anzuzeigen. Die
Anwendung wird also nur minimal angepasst und es wird ein RCP-Gerüst erstellt, welches
die Anwendung in eine View kapselt. Die View wird wiederum in eine Perspective gekap-
selt und aus der bekannten run-Methode des Interface IPlatformRunnable heraus ge-
startet. Andere Anwendungsteile können anschließend sukzessive in die Perspective über
einzelne Views hinzugefügt werden.
Details zur RCP-Architektur finden Sie in Kapitel 11. Der Zusammenhang zwischen View,
Workbench und Perspective wird in Kapitel 12 besprochen.

12.7.2 Mechanics
1. WorkbenchAdvisor, WorkbenchWindowAdvisor und Perspective aus den vorgestellten
Mustern erzeugen
2. Extension-Point Perspective deklarieren
3. Start-Fenster durch View ersetzen (führe Refactoring: »Ersetze Swing-Fenster durch
RCP-View« aus)
4. Workbench als PlatformRunnable starten
5. Testen

673
eclipse_v01.book Seite 674 Montag, 30. Januar 2006 12:02 12

Refactoring: Konvertiere Anwendung in RCP-Anwendung

12.7.3 Beispiel
Als Erstes wird ein WorkbenchAdvisor erstellt, der die Perspective angibt, die beim Starten
angezeigt werden soll. In unserem Fall werden wir auch keine weitere Perspective erstellen,
so dass dies gleichzeitig die einzige ist. Zusätzlich erstellt der WorkbenchAdvisor einen
WorkbenchWindowAdvisor.

public String getInitialWindowPerspectiveId() {


return "com.entwickler.eclipsebuch.bankxxs.bankxxsperspective";
}
public WorkbenchWindowAdvisor createWorkbenchWindowAdvisor(
IWorkbenchWindowConfigurer configurer) {
return new BankxxsWorkbenchWindowAdvisor(configurer);
}

Der WorkbenchWindowAdvisor konfiguriert das Fenster und kann beispielsweise die Grö-
ße der Anwendung angeben.

public void preWindowOpen() {


IWorkbenchWindowConfigurer configurer = getWindowConfigurer();
configurer.setInitialSize(new Point(400, 400));
configurer.setShowCoolBar(false);
configurer.setShowStatusLine(false);
}

Die Perspective ist in diesem Refactoring ebenfalls recht einfach und es wird lediglich eine
View angegeben, welche den ganzen sichtbaren Bereich der Workbench in Anspruch
nimmt.

public void createInitialLayout(IPageLayout layout) {


layout.setEditorAreaVisible(false);
layout.addView(BankxxsView.ID_VIEW, IPageLayout.TOP,
IPageLayout.RATIO_MAX, IPageLayout.ID_EDITOR_AREA);
}

Die erstellte Perspective muss als Extension deklariert werden, damit der WorkbenchAdvi-
sor diese finden und starten kann. Hierzu muss lediglich ein Name, eine ID und die passen-
de Klasse angegeben werden.

<extension point="org.eclipse.ui.perspectives">
<perspective name="bankperspective"
class="com.entwickler.eclipsebuch.bankxxs.BankxxsPerspective"
id="com.entwickler.eclipsebuch.bankxxs.bankxxsperspective"/>
</extension>

674
eclipse_v01.book Seite 675 Montag, 30. Januar 2006 12:02 12

12 – Refactoring to Eclipse

In einem nächsten Schritt muss das Swing-Fenster, welches unmittelbar nach dem Starten
angezeigt wird, durch eine View ersetzt und der Perspective hinzugefügt werden. Da dies
losgelöst von diesen Schritten auch für andere Refactorings betrachtet werden kann, wird es
in einem eigenen Refactoring: »Ersetze Swing-Fenster durch RCP-View« beschrieben.
Nachdem nun das UI auf Views umgestellt wurde, muss zum Schluss noch das Starten der
Anwendung angepasst werden. Bisher wurde die Anwendung in der run-Methode von
IPlatformRunnable direkt gestartet. Nun muss an dieser Stelle die Workbench gestartet
werden, welche dann die Perspective mit den entsprechenden Views anzeigt.

WorkbenchAdvisor workbenchAdvisor = new BankxxsWorkbenchAdvisor();


Display display = PlatformUI.createDisplay();
try {
PlatformUI.createAndRunWorkbench(display, workbenchAdvisor);
} finally {
display.dispose();
}

Anschließend kann die Anwendung als RCP-Anwendung gestartet werden. Dabei müssen
neben dem Plug-In org.eclipse.ui für WorkbenchAdvisor, Perspective und Views die
weiteren RCP-Plugins vorhanden sein. Diese lassen sich am einfachsten über »Add Re-
quired Plug-Ins« in der Run-Time-Konfiguration hinzufügen.

12.8 Refactoring: Ersetze Swing-Fenster durch RCP-View


Ein Anwendungsteil soll nicht mehr losgelöst in einem eigenen Swing-Fenster liegen, son-
dern als View in die RCP-Anwendung integriert werden.
Konvertiere ein Anwendungsteil in eine eigene View und integriere diese in eine bestehende
Perspective.

Bankanwendung Bankanwendung

Swing-Frame View

Eclipse RCP Eclipse RCP

675
eclipse_v01.book Seite 676 Montag, 30. Januar 2006 12:02 12

Refactoring: Ersetze Swing-Fenster durch RCP-View

12.8.1 Motivation
In diesem Refactoring wird ein Anwendungsteil in eine RCP-Anwendung so eingegliedert,
dass es in einer eigenen View angezeigt wird. Dabei gehen wir davon aus, dass der Anwen-
dungsteil in einem eigenen Fenster läuft. Für Anwendungsteile, die anders eingegliedert
sind, z.B. über Tabs, sollten die Schritte annähernd gleich sein.

12.8.2 Mechanics
1. JFrame in JPanel umstellen
2. Neue View erstellen und das JPanel einbinden
3. Extension-Point View deklarieren
4. Perspective anpassen
5. Gegebenenfalls Kommunikation zwischen Views anpassen
6. Testen

12.8.3 Beispiel
In einem ersten Schritt muss ein JFrame zu einem JPanel umgestellt werden, um es später
in die View einzugliedern. Dies sollte problemlos möglich sein und häufig fallen nur weni-
ge JFrame-spezifische Methoden heraus. So muss beispielsweise die Fenstergröße nicht
mehr angegeben werden oder die enthaltenen Komponenten wie Buttons können direkt
dem JPanel übergeben werden und nicht über einen enthaltenen ContentPane.
Das umgestellte JPanel muss in einem nächsten Schritt in eine neue View (MyView ex-
tends ViewPart) integriert werden. Hierzu bietet Eclipse die Klasse SWT_AWT an. In
dieser Klasse gibt es die statische Methode new_Frame, die ein AWT-Frame zurückgibt,
und zu diesem Frame fügen wir dann unser Panel hinzu. Dies implementieren wir in der
createPartControl-Methode der View:

public static String ID_VIEW = "eclipsebuch.bankxxs.bankxxsview";


public void createPartControl(Composite parent) {
Composite child = new Composite(parent, SWT.EMBEDDED);
Frame frame = SWT_AWT.new_Frame(child);
CheckingAccountTool checkingAccountTool
= new CheckingAccountTool();
frame.add(_checkingAccountTool.getGui());
}

Die erstellte View muss nun als Extension für den Extension-Point org.eclipse.ui.
views deklariert werden. Hierzu muss das Basis-UI-Plug-In von Eclipse org.eclipse.
ui importiert werden. Um später die richtigen View-Extensions zu der Perspektive hinzu-
zufügen, sollten die Views eine einheitliche Category angeben. Die Deklaration der Exten-
sion in der plugin.xml-Datei sieht dann folgendermaßen aus:

676
eclipse_v01.book Seite 677 Montag, 30. Januar 2006 12:02 12

12 – Refactoring to Eclipse

<extension
point="org.eclipse.ui.views">
<category name="BankXXS"
id="com.entwickler.eclipsebuch.bankxxs"/>
<view name="Bank XXS View"
icon="icons/bankxxs.gif"
category="com.entwickler.eclipsebuch.bankxxs"
class="com.entwickler.eclipsebuch.bankxxs.rcp.BankxxsView"
id="com.entwickler.eclipsebuch.bankxxs.BankxxsView"/>
</extension>

Je nachdem, ob schon eine View vorhanden ist oder nicht, muss ein Layout ausgewählt wer-
den, wie die Views angeordnet und der Perspective hinzugefügt werden. In unserem Bei-
spiel wird ein FolderLayout erzeugt und die einzelnen Views werden hinzugefügt, so dass
ein großes Fenster mit einzelnen Tabs, den jeweiligen Views, erzeugt wird. Dazu über-
schreiben wir die Operation createInitialLayout in der Factory unserer Perspektive
wie folgt:

public void createInitialLayout(IPageLayout layout) {


layout.setEditorAreaVisible(false);
IFolderLayout folder = layout.createFolder("",
IPageLayout.TOP, 1, IPageLayout.ID_EDITOR_AREA);
folder.addView(BankxxsView.VIEW_ID);
folder.addView(CheckingView.VIEW_ID);
}

Wenn man keinen direkten Zugriff auf die View-Ids hat, weil sie beispielsweise in einem
anderen Plug-In liegen, können auch die View-Extensions der einzelnen Plug-Ins ausge-
wertet werden. Dann müssen die IDs zu allen Views der passenden Category gefunden wer-
den. Diese Auswertung ist ein einfaches Parsen der Plug-In-Extensions und wurde exem-
plarisch in dem Beispielprogramm auf der CD durchgeführt.
In einem letzten Schritt muss gegebenenfalls die Kommunikation zwischen den Views an-
gepasst werden. In dem Beispielprogramm holt sich beispielsweise die View des Checking-
Account den aktuell selektierten Account aus der Main-View.

12.9 Zusammenfassung
Wir haben in diesem Kapitel eine Reihe von Refactorings vorgestellt, die aus einer norma-
len Java-Anwendung über mehrere Zwischenschritte eine Eclipse-Platform-Anwendung
oder eine RCP-Anwendung machen. Die daraus entstehenden Vorteile und Möglichkeiten
sind vielfältig. Einzelne Plug-Ins der Anwendung können getrennt voneinander weiterent-
wickelt werden. Die Extension-Points lassen sich so gestallten, dass neue Plug-Ins durch
Dritte geschrieben werden können, die die Anwendung erweitern. Und dabei fügen sich die
Erweiterungen nahtlos in die bestehende Anwendung ein.

677
eclipse_v01.book Seite 678 Montag, 30. Januar 2006 12:02 12

Zusammenfassung

Darüber hinaus sind noch weitere Schritte denkbar, die noch mehr Möglichkeiten der
Eclipse-Plattform im Allgemeinen und der Rich Client Platform im Speziellen nutzen. So
lässt sich beispielsweise ein Update-Mechanismus für die Anwendung implementieren, der
einzelne Anwendungsteile aktualisiert. Oder die Anwendung wird durch ein Hilfe-System
erweitert, das es erlaubt, dem Benutzer eine interaktive Hilfe zu dem Programm mitzu-
geben.
Einige der vorgestellten Refactorings lassen sich auch generell für die Plug-In-basierte An-
wendungsentwicklung nutzen. Das Refactoring »Extrahiere Erweiterungs-Plug-In« kann
auch genutzt werden, wenn eine Anwendung von Beginn an auf Basis der Eclipse-Plug-In-
Technologie entwickelt wird. Gerade für diesen Einsatzbereich haben wir in diesem Kapitel
sicherlich keine abschließende Sammlung an Refactorings vorgestellt, sondern nur erste
Ideen darstellen können.

678
eclipse_v01.book Seite 679 Montag, 30. Januar 2006 12:02 12

Die Autoren

Dr. Kai Brüssau


Dr. Kai Brüssau (bruessau@econ.uni-hamburg.de), geboren
1972 in Bremerhaven, promovierte nach dem Studium der
Wirtschaftsmathematik am Institut für Wirtschaftsinformatik an
der Universität Hamburg. Dort arbeitet er auch seit 1996 als Do-
zent und ist unter anderem für die Ausbildung der Studierenden
im Bereich der Software-Entwicklung und Programmierspra-
chen zuständig. Zu den Schwerpunkten zählen dabei die kom-
ponentenbasierte Software-Entwicklung, die Model Driven
Architecture sowie die J2EE-Technologie. Daneben hat er in
verschiedenen Software-Projekten im Bereich der Optimierung
von Produktionsplanungs- und Routing-Problemen mitgewirkt.

Oliver Widder
Oliver Widder (oliver.widder@t-online.de), geboren 1968 in
Mannheim, studierte an der dortigen Fachhochschule ab 1989
Informatik. Als von Java noch niemand redete, tat er das auch
nicht. Er brach stattdessen sein Studium ab und zog nach Ham-
burg, um dort Mathematik zu studieren. Kaum diplomiert zog es
ihn allerdings schon wieder zurück in die Informatik. So ist er
seit 1997 bei sd&m in Hamburg tätig. In vielen Projekten hat er
inzwischen bei vielen Kunden am Bau betrieblicher Informa-
tionssysteme mitgearbeitet. Zu Beginn seiner Karriere wurden
dazu durchaus auch andere Programmiersprachen als Java be-
nutzt. Inzwischen finden Java-Entwicklungsumgebungen und
Java Application Server sein besonderes Interesse.

Dr. Herbert Brückner


Dr. Herbert Brückner (hb@brueckner-keutmann.de), geboren 1967,
Mathematikstudium an der RWTH Aachen, 1998 Promotion im
Bereich algorithmische und computergestützte Algebra, 1998/
99 Research Assistant an der University of Sydney, 1999–2001
Leiter Forschung und Entwicklung der Aixonix GmbH mit dem
Aufgabengebiet internetbasierte Informationssysteme, von
2001 bis 2003 als Berater Anwendungsentwicklung bei sd&m
Köln/Bonn mit Schwerpunkt im Build Management und Testen
von Internet-Anwendungen und -Portalen. Seit Anfang 2004
selbstständig in der Beratung für (internetbasierte) IT-Architek-
turen mit mobilen Komponenten.

679
eclipse_v01.book Seite 680 Montag, 30. Januar 2006 12:02 12

Die Autoren

Martin Lippert
Dipl.-Inform. Martin Lippert (martin.lippert@it-agile.de oder
http://www.it-agile.de) ist langjähriger Consultant und Coach in
den Bereichen agile Methoden, Software-Architekturen und
Java. Er berät Entwicklungsorganisationen bei der Einführung
und Anwendung agiler Entwicklungsmethoden und hilft Teams,
neue Wege in der Softwareentwicklung zu beschreiten. Neben
großen und komplexen Refactorings gehört die Entwicklung
großer Plug-In-basierter Anwendungssysteme auf Basis der
Eclipse-Plattform zu seinen Spezialgebieten.

Matthias Lübken
Matthias Lübken (matthias@luebken.com) hat im Februar 2004
an der FH Bonn-Rhein-Sieg sein Studium der Angewandten
Informatik absolviert und ist nun Software-Entwickler bei der
it-agile GmbH. Im Bereich der Software-Entwicklung interes-
siert er sich für agile Entwicklungsprozesse wie XP und deren
Einführung und Umsetzung. Zu seinen Lieblings-Features von
Eclipse gehören die RCP und das Komponentenmodell der
Plug-Ins und er hofft weiter auf die konsequente Umsetzung in
Mac OS X.

Dr. Birgit Schwartz-Reinken


Dr. Birgit Schwartz-Reinken (schwartz@econ.uni-hamburg.de)
arbeitet als Dozentin am Institut für Wirtschaftsinformatik an
der Universität Hamburg. Dort ist sie zum einen für die Ausbil-
dung der Studierenden im Bereich der Software-Entwicklung
und Programmiersprachen und zum anderen im Bereich Stan-
dardanwendungssoftware (Word, Access, Excel) zuständig. Ihr
Aufgabengebiet umfasst neben dem Unterrichten und der Er-
stellung von Lehrmaterialen für Präsenzveranstaltungen auch
die Entwicklung von web-basierten Lernmodulen zum virtuel-
len Lernen.

680
eclipse_v01.book Seite 681 Montag, 30. Januar 2006 12:02 12

Die Autoren

Lars Wunderlich
Lars Wunderlich (lars.wunderlich@gmx.net) studierte nach sei-
ner Ausbildung zum Fachinformatiker Fachrichtung Anwen-
dungsentwicklung Wirtschaftsinformatik im Fernstudium und
arbeitet seitdem als Software Engineer und System Architekt
in Großprojekten im J2EE-Umfeld bei der TUI InfoTec
GmbH&Co.KG in Hannover. Er bringt jahrelange Erfahrungen
im Umgang mit Eclipse und dem Vorgänger-Produkt VisualAge
for Java mit. Neben seiner Arbeit als Buch-Autor und Verfasser
von Artikeln in Fachzeitschriften gehört seine Freizeit vor allem
seiner Familie und seinem kleinen Sohn.

681
eclipse_v01.book Seite 682 Montag, 30. Januar 2006 12:02 12
eclipse_v01SIX.fm Seite 683 Montag, 13. Februar 2006 12:29 12

Stichwortverzeichnis

A Attach Source 35
AbstractTextEditor 488, 525 Audits 123
access and modification 76 Aufrufhierarchien 88
Accessor 55, 86 Autoboxing 37, 94
Accessor-Methoden 43 AWT 206, 208, 209, 211, 222, 232
Action 487 AWT/Swing-Komponenten im
Action Class 566 Visual Editor 183
Action Delegate 504, 566, 569 Axis 397, 559, 561
Action Label 566 – AdminClient 402
ActionListener 193, 197, 198, 202 – deploy.wsdd 401
Actions 651 – JAVA2WSDL 407
Adapter 413 – server-config.wsdd 402
Adapter Factory 432 – WSDL2Java 397, 404
Adapter Manager 432
Adapterklasse 181, 182, 187, 192, 232, B
238, 250 Bean Managed Persistence Siehe BMP
Add Constructor from Superclass 42 Betriebssystem 100
Annahmen 61 Binary Project 412, 416
Annotations 82 binding-Element 395, 409
anonyme innere Klasse 54 BMP 304, 353, 370, 371
anonyme Klasse 182, 186, 238, 240, 248, Bootstrap Entries 66
250 BorderLayout 183, 184, 185
Ant 103, 124, 125, 159 BoxLayout 183, 184
– build.xml 162 Breakpoint View 75
– Project 161 Breakpoints 62, 63, 68, 71, 75, 76
– Target 161 – organisieren 78
– Task 161 Bugs 62
Ant Editor 273 Bugtracking 121
Ant-Skript 273, 589 Bugzilla 121
Apache Axis 559 Build 137
Apache-Coyote 559 – Ausgabetypen 146
API-Dokumentation 38 – automatisch 143
Appearance 102, 105 – Einstellungen 139
Application Server 66, 81, 656 – Externe Builds 169
Applikationsserver 299 – inkrementell 138
– Deployment 310 – komplett 138
ArmListener 233 – manuell 143
Artefakte 123 Build Order 103
Assertions 61 Build Path 139
AST 597 Build-Prozess 86, 123
AST Parser 597 Bundle 538, 637
AST View 598 Bundle Context 539
ASTVisitor 610 Bundle Manifest 538

683
eclipse_v01SIX.fm Seite 684 Montag, 13. Februar 2006 12:29 12

Stichwortverzeichnis

Business Registries 392 Compile-Fehler 29


Button 196, 198, 211, 220, 232, 247 Compiler 37, 106
– Beschriftung 221 – Einstellungen 142
Bytecode 105 – Fehlerbehandlung 143
complexType-Element 408
C Composite 209, 219, 222, 245
Call Hierarchy View 88 Connection 294, 348
Calls to method View 85 Console 76, 99, 108
Capabilities 102 Console View 68, 76
CardLayout 183 Consumer 380
Catalina 559 Container Managed Persistence Siehe CMP
Catch-Block 46 Content Assist 80
Change Management 121 Content Provider 418, 420, 421, 422, 481,
ChangeListener 205 513, 601, 653
Cheat Sheets 27 Content Types 102
Checkstyle 125, 127, 128 ContentOutline 414, 415, 416, 418, 422,
Checkstyle-Konfigurationen 125 431, 435, 439, 441, 456, 487, 495,
Class Load Breakpoint 77 497, 510
Classloader 66, 72, 642 ContentOutlinePage 416, 418, 422, 487,
Class-Loading 642 495, 497, 510
Classloading 656 ContentPane 185, 186, 190, 193, 195
CLASSPATH 105 Contribution Item 478
Classpath 64, 66, 145, 664 Contribution Manager 478
Client ControlListener 233
– Zugriff EJB 305, 332, 334, 338 Copyrighthinweis 106
– Zugriff Entity Bean 378 COS 300
– Zugriff Session Bean 332, 334 createControl 418, 422, 510
Client-Server-Umfeld 656 createExecutableExtension 618, 670
CMP 304, 353, 365 createMarker 525
Code 106 create-Methode 305, 306, 309, 334, 335,
Code completion 44, 45, 48, 49 365, 374
Code Snippet 74 createWidgets 576
Code Style 106 CVS 138
Code Style/Code Templates 106 – Branch 150, 155
Code Templates 57 – Check out 150
Codegenerierung – Commit 152
– Visual Editor 184, 237 – CVSROOT 149
– WindowBuilder 248 – Merge 155
Colors/Fonts 102 – Repository 149
Combobox 201, 202, 214, 256 – Serververbindung 148
– Einträge 201, 256 – ssh-Verschlüsselung 149
Commands 98 – Synchronize 152
Common 67 – Tag 154
Compare/Patch 102 – Tag as Version 154
CompilationUnit 565 – Update 152
Compile 14 – Verbindung trennen 157

684
eclipse_v01SIX.fm Seite 685 Montag, 13. Februar 2006 12:29 12

Stichwortverzeichnis

D Document Object Model 391


Dateiendungen 103 Document Type Declaration Siehe DTD
Datenbank 292 doGet-Methode 276, 278
Datenbanktreiber 299 Dokumentation 15, 123, 138
Datenbankverbindung 293 DOM 391
Datenbankverbindungsobjekt 348 Dom4J 568, 586, 590
Dead Lock 63 doPost-Methode 276
Debug 64, 106 doSave 456
Debug View 71, 72 Drop to frame 72
Debuggen 13 DTD 265
Debugger 68, 81 Duplikate 53
Debugging 14
Debug-Modus 100 E
Debug-Perspektive 67, 71, 72 earlyStartup 437
Debug-Preferences 108 Eclipse 13, 14, 15, 16
Declaration View 88 – RCP 635
Default-Konstruktor 42 – Rich Client Platform 635
definitions-Element 393, 407 Eclipse Application 424
Deklaration 86, 88 Eclipse Modeling Framework 118
Delegate 42 Eclipse Workbench 411
Deployment 13, 15, 104, 124, 137, 264, Eclipse-Runtime 662
658 Edit Step Filter 69
– EJB 310 Editor 23, 68, 102, 104, 107
– Entity Bean 358 Eigenschaften
– Web Service 401 – ändern 175
– Web-Anwendung 279, 298, 342 – ändern im Visual Editor 179, 185, 194
Deployment Descriptor 586 – ändern im WindowBuilder 245
Deployment-Deskriptor Einrückung des Sourcecodes 41
– EJB 305, 310, 331, 355, 358, 382 EJB 13, 263, 299, 302, 309
– Entity Bean 355, 358 EJB Container 304, 352, 356, 370, 373,
– Message Driven Bean 382 382
– Session Bean 311, 331 EJB Query Language Siehe EJB-QL
– Web Service 401 ejbCreate-Methode 306, 335
– Web-Anwendung 278, 280, 282 ejbFindByPrimaryKey 354
Detail Formatter 74, 106 EJBHome 308
Detailanzeige 72 ejb-jar.xml 310, 311, 358
Directory Service Siehe Verzeichnisdienst EJB-Klasse 327
DirectoryDialog 580 EJBLocalHome 308
Disconnect 69 EJBLocalObject 309
Display 79, 646 EJBObject 309
Display View 78, 79 EJB-Projekt 326
DisposeListener 233 EJB-QL 362, 363
DNS 300 EMF 118
docking 22 enablesFor 567
Doclet 328 endpoint 396, 404
doCreatePage 414, 415, 430, 435, 439 Enterprise Java Beans Siehe EJB

685
eclipse_v01SIX.fm Seite 686 Montag, 13. Februar 2006 12:29 12

Stichwortverzeichnis

Enterprise-Anwendungen 636 FillLayout 222, 223, 235, 245, 246, 247


Entity Bean 302, 304, 352 – Einstellungsmöglichkeiten 223
– BMP 370 Filter Package 69
– CMP 354 Filter Type 69
– Erzeugen 365 Filtering 63
– Grundlagen 352 Find/Replace 84
Entwicklungszyklus 15 findByPrimaryKey 353
Envelope 559 Finder-Methode 353, 354, 363
Environment 67 – CMP 362
Ereignis Flags 577
– Anzeige im Visual Editor 182 FlowLayout 183, 184
– entfernen im Visual Editor 182 FocusListener 233, 241
– entfernen im WindowBuilder 247 Folding 107
– hinzufügen im Visual Editor 181, 186, foreach 95
192, 238 Formatierung 41, 106
– hinzufügen im WindowBuilder 232, 247, FormLayout 222, 229, 235, 236, 245, 246,
250 247
Ereignisverarbeitung 175 – Einstellungsmöglichkeiten 229, 230, 231
– SWT 207, 232 Fortschrittsbalken 217, 218
– Visual Editor 181 Frame im Stack 71
– WindowBuilder 247, 249, 258
Errors 82 G
Evaluation 14 GEF 119
Exception 77 General-Properties 102
Exception Breakpoint 77 Generate Constructor using Fields 43
Exception-Klassen 47 Generate Delegate Methods 42
Execute 79 Generate Getters and Setters 42
Export 102 generische Typen 37, 55
Expression View 75, 78, 79 getAdapter 415, 423, 430, 432, 440, 441,
Extensible Markup Language Siehe XML 456, 495
Extension getAdapterList 432
– extrahieren 667 getChildren 419, 420, 481, 513, 602
Extension-Point getCursorPosition 468
– org.eclipse.core.runtime.applications getElementName 578
637, 664 getElements 419, 420, 481, 513, 601
– verallgemeinern 671 getFlags 578
Externalisierung 57 getImage 420
Extrahierung 53 getMethods 578
getParameterNames 578
getParameterTypes 578
F getPersistentProperty 492
Fabrikmethode 55 getReturntype 578
factory 55 getSessionProperty 492
Fast Views 23 Getter-Methode 185, 186, 195, 196
File Associations 103 Getters und Setters 43
File Search 83 getText 420, 481, 514

686
eclipse_v01SIX.fm Seite 687 Montag, 13. Februar 2006 12:29 12

Stichwortverzeichnis

Graphical Editing Framework 119 IEditorPart.getEditorSite() 475


Grid Layout 576 IEditorSite 475
GridBagLayout 183, 184, 229 ILabelProvider 419
GridLayout 183, 184, 195, 222, 226, 235, IMethod 578
236, 240, 245, 246, 247, 254 IMethod.getElementName 578
– Einstellungsmöglichkeiten 226, 227 IMethod.getFlags 578
Größenbeschränkungen 126 IMethod.getParameterNames 578
Group 223 IMethod.getParameterTypes 578
Group Marker 478 IMethod.getReturntype 578
GUI-Design 15 IMethod.isConstructor 578
GUI-Prozess 206 Implementierungen 86
Import 35, 102
H Importfunktionalität 35
Help 104 Importstatements 42, 63, 106
Hierarchie 50, 86, 87 Incremental Search 84
Hierarchy View 85, 86, 87 Indentation 41
Hilfe 27, 104 Informationhiding 86
Historie 82 InitialContext 332, 334, 346, 384, 386
Home Interface 305, 309, 353 inkrementeller Compile 29, 72
Home-Objekt 304, 305, 306, 338 Inlinecode 55
Hot Code Replacement 80 innere Klasse 182, 248, 251
Hotspot 70 Insert 50
Hotswap 80 Insertion point 43
Hotswap Feature 80 Inspect 75, 79
Hover 28 Install/Update-Properties 104
HTML-Dokument 275 Installieren
HTTP Header 559 – Visual Editor für Java 176
HTTP Post Request 558 – WindowBuilder 242
HTTP Request 275 Instance Breakpoints 74
HTTP Response 558 Interface 55
HTTP Server 559 Internal Frame 193, 194, 198
HttpServlet 276 IObjectActionDelegate 566, 569
HttpServletRequest 276 IPlatformRunnable 637
HttpServletResponse 277 ISelection 569
isPublic 577
I IStructuredContentProvider 419
IActionBars 476 ItemListener 205
IAdaptable 413, 415 ITreeContentProvider 419
ICompilationUnit 577 IType 577
ICompilationUnit.getAllTypes 577 IType.getFlags 577
IConfigurationElement 618 IType.getMethods 578
IContentOutlinePage 415, 416, 422, 431, IViewPart 475
456 IViewPart.getViewSite() 475
IDE 13, 14, 16 IViewSite 475
IDocument 452 IWorkbench 472
IEditorPart 475 IWorkbenchPage 474

687
eclipse_v01SIX.fm Seite 688 Montag, 13. Februar 2006 12:29 12

Stichwortverzeichnis

IWorkbenchPart 415 Javadoc 26, 38, 45, 81, 85, 107, 126
IWorkbenchPartSite 475 Java-Perspektive 19
IWorkbenchWindow 473 Java-Preferences 105
Java-Projekt 28, 127
J Java-Projekt _ Eigenschaften 36
J2EE 263 Java-Projekte 14
J2EE Connector Architecture Siehe JCA JAX 391
J2EE-Applikationsserver 299, 304, 307 JAXB 391
J2EE-Architektur 263 JAXP 391
J2EE-Features 15 JAXR 392
J2EE-Prüfungen 127 JAX-RPC 391
J2SE 5.0 options 37 JBoss Server 263
Jakarta ORO 27, 31, 127, 128 jboss.xml 310, 312, 359
Jakarta Tomcat 559 jbosscmp-jdbc 355
JAR-Archiv 664 JBoss-Server 313
Jar-File 36 JCA 345
Java 13, 14, 16, 89 JDBC 263, 299
Java 2 Platform, Enterprise Edition Siehe JDBC-API 299
J2EE JDBC-ODBC-Bridge 300
Java 5 37, 55 JDBC-Treiber 300, 345
Java 6 89 JDBC-Treibermanager 300
Java API for XML 391 JDK 36
Java API for XML Registries 392 JDT 578
Java API for XML-based Remote Procedure JFace 411, 418
Calls 391 JMS 304, 379
Java API_ for XML Processing 391 – Point-to-Point 380
Java Architecture for XML Binding 391 – Publish / Subscribe 380
Java Bean 296 JNDI 301, 332
Java Build Path 38 JNDI_NAME 339
Java Code Style 38 JNDI-API 302
Java Database Connectivity Siehe JDBC JNDI-Name 312, 328, 345, 355
Java Messaging Service Siehe JMS JNP 332
Java Naming and Directory Interface job infrastructure 88
Siehe JNDI JRE 66, 100
Java Package Name 566 JREs 107
Java Reflection 86 JSP 13, 263, 281
Java Runtime Environment 24, 66 JSP-Dokument 275, 281, 296, 336
Java Search 83 JSP-Editoren 274
Java Server Pages Siehe JSP JUnit 59, 62, 65, 67, 107, 121, 122, 124
Java Servlet API 263 Junit Plug-In Test 666
java.util.StringTokenizer 584 JVMTI 70
JAVA_HOME 105
JAVA2WSDL 407 K
Javabeans 86 KeyListener 233
Java-Client Keys 103
– EJB 344 Klammern 107

688
eclipse_v01SIX.fm Seite 689 Montag, 13. Februar 2006 12:29 12

Stichwortverzeichnis

Klasse – Widget 208


– Button 211 – WindowAdapter 192
– Canvas 210 Klassenpfad 66, 72
– Combo 214 Kommentar 41, 62
– Composite 209 Kommunikationsendpunkt 396, 404
– Control 208 Komponenten
– CoolBar 221 – Beschriftung im Visual Editor 179
– CoolItem 221 – entfernen im Visual Editor 179
– Display 206, 249 – entfernen im WindowBuilder 245
– erstellen im Visual Editor 177, 189, 239 – hinzufügen 175
– erstellen im WindowBuilder 243, 253 – hinzufügen im Visual Editor 178, 185,
– Font 241 186, 195, 201
– FontData 241 – hinzufügen im WindowBuilder 245, 249,
– FormAttachment 230, 231 250, 253
– FormData 229 – verschieben im Visual Editor 179
– GridData 228 – verschieben im WindowBuilder 245
– Group 210 Komponententechnologie 636
– JComboBox 202 Konfiguration 99, 138
– JDesktopPane 190 Konfigurations- und Buildmanagement 15
– JFrame 185, 189 Konfigurationsfile 99
– JInternalFrame 185 Konflikt 156
– JList 202 Konstante 55
– JPanel 185 Konstruktor 42, 55
– Label 210 Konventionen 126, 128
– List 213
– Menu 221 L
– MenuItem 221 Label 186, 195, 239
– ProgressBar 217 – Beschriftung 195, 254
– RowData 225 Label Decorators 21
– Sash 219 Label Provider 419, 420, 481, 514
– SashForm 219 Launch configs 105
– Scale 217 Launch Configuration 64, 67, 69
– ScrollBar 218 Launch Perspective 67
– Shell 206, 249 Layers 636
– Slider 217 Layoutmanager 175
– Spinner 215 – allgemeine Einstellungen im Visual
– TabFolder 220 Editor 180, 235, 236
– TabItem 220 – allgemeine Einstellungen im Window-
– Table 215 Builder 246
– TableColumn 215 – individuelle Einstellungen im Visual
– TableItem 215 Editor 180, 235, 236
– Text 212 – individuelle Einstellungen im Window-
– ToolBar 220 Builder 246
– ToolItem 220 – null im Visual Editor 180, 181
– Tree 218 – null im WindowBuilder 245
– TreeItem 218 – SWT 222

689
eclipse_v01SIX.fm Seite 690 Montag, 13. Februar 2006 12:29 12

Stichwortverzeichnis

– Visual Editor 183 MessageListener 381


– wählen im Visual Editor 180, 195 Method Breakpoint 77
– wählen im WindowBuilder 246, 253 Methode
– WindowBuilder 245 – actionPerformed 193, 198, 202
LDAP 301 – add 198, 208, 210, 213, 214
Libraries 36, 66, 84, 105 – addItem 201
Library 65 – append 213
Lightweight Directory Access Protocol – clearSelection 213
Siehe LDAP – controlMoved 234
Link to Editor 24 – controlResized 234
Link to Source 35 – copy 213
Link with Editor 22 – cut 213
Linked Resources 103 – dispose 198, 207
Liste 201, 256 – focusGained 234, 240
– Einträge 202, 256 – focusLost 234
Listener 181, 186, 232 – getCaretLineNumber 213
ListSelectionListener 202 – getCaretPosition 213
Local History 51, 103 – getCharCount 213
Local Home Interface 308, 338, 360 – getDigits 215
Local Interface 309, 360 – getItems 214
Locale 100 – getSelection 212, 214, 217, 218, 219,
localhost 279 220, 221
Log 77 – getSelectionCount 213
LOG4J 16, 108 – getSelectionIndex 214, 220
logical structure 106 – getSelectionIndices 214
Look&Feel 657 – getSelectionText 213
– getSource 234
M – getText 213, 214
Main-Thread 71 – helpRequested 234
Mark Occurrences 107 – insert 213
Mechanics 662 – itemStateChanged 205
mehrschichtige Anwendungen 275 – keyPressed 234
Mehrsprachigkeit 57 – keyReleased 234
Menü 189, 221 – keyTraversed 234
Menübefehl 189, 222 – main 189, 190, 249
Menüleiste 189, 221 – menuHidden 234
MenuListener 233 – menuShown 234
Message – modifyText 234
– Message Driven Bean 384, 386 – mouseDoubleClick 234
– Web Service 390, 394 – mouseDown 234, 241
Message Driven Bean 302, 304, 381 – mouseEnter 234
– Queue 383 – mouseExit 234
– Topic 386 – mouseHover 234
Message Listener 304, 381 – mouseMove 234
MessageDrivenBean 381 – mouseUp 234
message-Element 394, 408 – open 207, 249

690
eclipse_v01SIX.fm Seite 691 Montag, 13. Februar 2006 12:29 12

Stichwortverzeichnis

– paintControl 234 Metrics 129


– paste 213 Metriken 123
– select 214 Model Driven Architecture 15
– selectAll 213, 241 Modellierungsmöglichkeiten 124
– setBackground 216 Modify Listener 576
– setControl 220, 221, 257 ModifyListener 233, 576
– setDigits 215 MOF 118
– setForeground 216 MouseListener 233
– setHeadVisible 216 MouseMoveListener 233
– setHeight 242 MouseTrackListener 233
– setImage 211, 212, 219, 221 Multiple Selection 566
– setIncrement 217 Multithreading 63
– setItem 213, 214 MySQL-Datenbank 345, 400
– setLayout 222 MySQL-Datenbanktreiber 292
– setLayoutData 222, 225, 229
– setLinesVisible 216 N
– setListData 202 Name Filter 566
– setMaximum 217, 218 Namensdienst 300, 305, 332
– setMenu 222 Namenskonventionen 126
– setMenuBar 221 Namensraum 267, 270
– setMinimum 217, 218 Namensräume 642
– setPageIncrement 217 Namespace 267, 270
– setSelection 213, 214, 217, 218, 219, Naming Service Siehe Namensdienst
220 NamingContextFactory 333
– setSize 208 narrow-Methode 335
– setText 210, 211, 212, 213, 214, 216, native Methode 71
219, 220, 221, 222 Navigation 81, 82
– setThumb 217 Navigator View 88
– setToolTipText 221 Nullpointerexception 77
– setValues 217
– setVisible 208 O
– shellActivated 234, 240 Oberklasse 55
– shellClosed 234 ObjectInputStream 644
– shellDeactivated 234 ObjectOutputStream 644
– shellDeiconified 234 ODBC 300
– shellIconified 234 Online-Hilfe 14
– showSelection 213 onMessage-Methode 381
– stateChanged 205 Open Call Hierarchy 85
– treeCollapsed 234 Open Database Connectivity Siehe ODBC
– treeExpanded 234 Open Declaration 85
– valueChanged 202 Open Declaration Type 69
– verifyText 234 Open Declaration Type Hierarchy 69
– widgetArmed 234 Open Declared Type 74
– widgetDefaultSelected 232, 234 Open Declared Type Hierarchy 74
– widgetDisposed 234 Open External Javadoc 85
– widgetSelected 234, 258, 259, 260 Open Funktionen 84

691
eclipse_v01SIX.fm Seite 692 Montag, 13. Februar 2006 12:29 12

Stichwortverzeichnis

Open Source 13, 16 org.eclipse.ui.internal.ViewPluginAction


Open Super Implementation 85 497
Open Type 85 org.eclipse.ui.IStartup 437
Open Type Hierarchy 85, 87 org.eclipse.ui.IViewActionDelegate 497
operation-Element 394 org.eclipse.ui.part.Page 495
org.eclipse.ant.core.AntRunner 592 org.eclipse.ui.part.PageBookView 495
org.eclipse.core.resources.IResource 492, org.eclipse.ui.part.WorkbenchPart 431
525 org.eclipse.ui.popupMenus 565
org.eclipse.core.runtime.IConfiguration- org.eclipse.ui.startup 436, 438
Element 448 org.eclipse.ui.texteditor.AbstractTextEditor
org.eclipse.core.runtime.IExtension- 431, 468
Registry 448 org.eclipse.ui.texteditor.ExtendedText-
org.eclipse.jdt.core.dom.ASTParser 597 Editor 431
org.eclipse.jdt.core.dom.ASTVisitor 610 org.eclipse.ui.viewActions 497, 504
org.eclipse.jdt.core.dom.DefaultAST- org.eclipse.ui.views 414, 416, 418, 430,
Visitor 610 480, 501, 512
org.eclipse.jdt.core.dom.NaiveAST- org.eclipse.ui.views.contentoutline.Content-
Flattener 610 Outline 414, 430
org.eclipse.jdt.core.Flags 577 org.eclipse.ui.views.contentoutline.IContent-
org.eclipse.jdt.core.IMethod 578 OutlinePage 414
org.eclipse.jdt.core.IType 577 org.eclipse.ui.workbench 505
org.eclipse.jdt.core.Signature 578 org.osgi.framework.BundleActivator 539
org.eclipse.jdt.internal.core.Compilation- org.osgi.framework.BundleContext 539
Unit 565 Organize Imports 41, 106
org.eclipse.jface 411 OSGi 537, 637
org.eclipse.jface.action.ActionContribution- OSGi Service 545
Item 478 Outline 20
org.eclipse.jface.action.GroupMarker 478 Outline View 23, 29, 412, 414, 415, 416,
org.eclipse.jface.action.IAction 478 418, 424, 427, 432
org.eclipse.jface.action.IAction.IObject- OutOfMemory 99
ActionDelegate 566, 569 Output 68
org.eclipse.jface.action.IContribution- Output-Verzeichnis 33
Item 478 Override/Implement Methods 42
org.eclipse.jface.action.Separator 478 Overwrite 50
org.eclipse.jface.viewers.ISelection-
ChangedListener 459 P
org.eclipse.jface.viewers.ITreeContent- Package
Provider 418 – org.eclipse.swt 206
org.eclipse.swt.DirectoryDialog 580 – org.eclipse.swt.custom 219, 231
org.eclipse.swt.events.ModifyListener 576 – org.eclipse.swt.graphics 241
org.eclipse.ui 436, 505 – org.eclipse.swt.layout 231
org.eclipse.ui.editors 423, 424, 457 – org.eclipse.swt.widgets 206, 219
org.eclipse.ui.editors.text.TextEditor 423, Package Explorer 20, 21, 23, 29, 57, 67,
430, 452 86, 127
org.eclipse.ui.internal.ActionDescriptor Page Book View 495
507 PaintListener 233

692
eclipse_v01SIX.fm Seite 693 Montag, 13. Februar 2006 12:29 12

Stichwortverzeichnis

Panel 204 Projekt


– Beschriftung 204 – erstellen im Visual Editor 177, 188, 239
Part Listener 498 – erstellen im WindowBuilder 243, 252
PartListener 498 Projekte 21, 66
PDE 411, 417 Projekte referenzieren 39
Persistenz 304, 352 Projektmanagement 121, 122
Perspective 103 Projekt-Properties 34
– CVS Repository Exploring 148, 154 Properties File Editor 107
– Ressource 151 Property-Dateiname 58
– Synchronize with Repository 152 Property-File 58
– Team Synchronizing 156 Prozessorarchitektur 99
Perspektive 19, 98, 100, 103
Plattformoptionen 99 Q
Plug-In QualifiedName 492
– Entwicklung Java-Client EJB 336 Qualität 121, 123, 124
– Entwicklung Web Anwendung 299 Qualitätssicherung 15, 121
– extrahieren 666, 667 Queue 304, 380, 381
– XML-Dokumente 270 QueueConnection 384
Plug-In Action 566, 570 QueueConnectionFactory 384
Plug-In Development Environment 411, QueueSender 384
416, 417 Quick Diff-Referenz 51
Plug-In Manifest 415, 417, 424 Quick Fixes 43
Plug-In Manifest Editor 417
plugin.xml 415, 417, 637 R
Plug-In-Development-Perpektive 416 Radiobutton 205, 210
Plug-In-Manifest-Editor 664 – Beschriftung 205
Plug-In-Projekt 415 Raw types 55
Plug-Ins 13, 15, 16, 102, 103, 637 RCP 635
– dynamisch 637 – Anwendung konvertieren 673
Plug-In-Technologie 636 Redo 51
PortableRemoteObject 335 Refactoring 51, 56, 124, 661
port-Element 396 – Mechanics 662
portType-Element 394, 409 – to Eclipse 661
Post Selection Change Listener 520 Refactoring-Mechanismen 52
Preferences 101, 104, 125, 127, 129 Referenzen 86
PreparedStatement 294 Regeln 126
Preview 52 Register 204, 220, 256, 257
Primärschlüssel 357 Registerkartenbereich 204, 220, 256
primitive Java-Typen 74 Regular Expressions 27, 50, 83, 84
Problems View 24, 29, 34, 81 Relaunch 69
Producer 380 remote debugging 70
Produktbereichen 636 Remote Home Interface Siehe Home Inter-
Produktivität 128 face
Programmierung 15 Remote Interface 305, 309
Progress View 88 Remote Method Invocation Siehe RMI
Project Wizard 563 Remote-Debugging 72

693
eclipse_v01SIX.fm Seite 694 Montag, 13. Februar 2006 12:29 12

Stichwortverzeichnis

Remotedebugging 69 selectionChanged 459, 481, 520, 569


Remote-Zugriff SelectionListener 233, 258, 259, 260
– EJB 332 Separator 478
Request 275, 282 Serialisierung 644
– Get 276, 278 Server Debugging 298
– Post 276 service-Element 396, 410
Request-Objekt 275 Servlet 275, 276
Response 275 – Exception Handling 280
Response-Objekt 275 Servlet Container 66
ResultSet 294, 348 Session Bean 306
Resume 69 setActivePart 569
revert addeded line/revert removed line 51 setPersistentProperty 492
revert block 51 setSessionProperty 492
Reviews 123, 124 setUp-Methode 62
Rich Client Platform 13, 635 Share Project 151
– Menüs 650 Shell 207, 209
– Überblick 645 ShellListener 233
– Views und Editors 653 Shortcut-Bar 20
Rich-Client-Platform 661 Shortcuts 98
RMI 263, 308, 344 Shutdown 103
RMIClassLoaderSpi 644 Sichten 176
RowLayout 222, 224, 235, 236, 245, 246, – Visual Editor 177, 178, 189
247 – WindowBuilder 244
– Einstellungsmöglichkeiten 224 Signature 578
Run 69, 108, 569 Simple API for XML 391
Runtime-Core 411 Simple Object Access Protocol 389, 390
RuntimeExceptions 46 Smart Insert 50
Runtime-Libraries 664 Smell 666
Runtime-Workbench 639 snippet 78
SOAP 389, 390
S – Client 390
SAAJ 391 – Message 390
SashForm 253 – Standard 390
SAX 391 – Style 390
Schieberegler 204, 217, 257 Soap 557
– Beschriftung 204, 257 Soap Envelope 558
Schleifen 48 SOAP with Attachements API for Java 391
SCM-System 102, 103 Soap-Antwort-Nachricht 558
Scope 83 Soap-Nachricht 558
Scrollleisten 213, 216, 218 Source 66
ScrollPane 201 Sourcecode 105
Search 103 Sourcecode Folding 40
Search View 84, 86 Sourcecode-Abdeckung 124
Search-Menü 86 Sourcecode-Management 51, 103
Selection Changed Listener 459, 481 Sourcecode-Stilrichtlinien 124
SelectionAdapter 258 Sourcecode-Templates 48

694
eclipse_v01SIX.fm Seite 695 Montag, 13. Februar 2006 12:29 12

Stichwortverzeichnis

Source-Verzeichnis 33 Strukturierung
Stack 22, 71, 80 – von Anwendungen 641
StackLayout 222, 231, 245, 247 Stub-Klasse 404
– Einstellungsmöglichkeiten 231 Submenu Name 566
Stacktrace 76 Suche 82
start 435 Sun Code Conventions 126
Startkonfiguration 72 Superklasse 55
Startparameter 99 – festlegen im Visual Editor 177, 193
Startup 103 Supertypen 55
Startup-Parameter 72 Suspend 69
Stateful Session Bean 302, 303, 308, 336 Suspend Policy 63
Stateless Session Bean 302, 303 Suspend Thread 76
statischer Import 94 Suspend VM 64, 76
Step Into 69 Swing 206, 208, 209, 211, 222, 655, 675
Step Over 69 Swing Custom Look&Feels 643
Step Return 69 Swing und/oder SWT 655
Stilkonstante 207, 208 SWT 206, 411, 655
Stilkonstanten – Vererbungshierarchie 208
– Button 211 SWT_AWT 655
– Canvas 210 SWT-Komponenten 208
– Combo 214 – im Visual Editor 235
– Composite 210 – im WindowBuilder 244
– Control 208 Synchronisation 176
– FormAttachment 231 Syntaxbaum 598
– Group 210 Syntaxhighlighting 107
– Label 210 System Thread 71
– List 213 System.err 68
– Menu 221 System.out 68, 76
– MenuItem 222
– ProgressBar 217 T
– SashForm 219 TabbedPane 204
– Scale 217 Tabelle 199, 255
– Shell 207 TabFolder 256
– Slider 217 TabItem 256
– Spinner 215 Target Object's Class 565
– TabFolder 220 Targetnamespace 267, 393, 407
– Table 216 Tasks 20, 24, 25, 26, 60, 82
– TableColumn 216 Tasks View 25
– TableItem 216 TCP/IP-Port 70
– Text 212 TEAM 151
– ToolBar 220 Team
– ToolItem 221 – Branch 155
– Tree 218 – Disconnect 157
– Widget 208 – Merge 156
Stilparameter 207, 208 – Version Control 154
Stilrichtlinien 128 Templates 48, 107

695
eclipse_v01SIX.fm Seite 696 Montag, 13. Februar 2006 12:29 12

Stichwortverzeichnis

Terminate 69 Use Step Filter 69


Test 13, 59 User Entries 66
Test Case 59 User Libraries 105
Test Cases 122
Test Suite 59 V
TextEditor 424, 488, 525 varargs 94
Texteditor 412, 423, 427, 431, 452 Variables View 72, 74, 75, 78, 79
Textfeld 195, 239, 255 Vererbungshierarchie 69, 74, 77, 81
Threads 63, 69, 71, 88 Vererbungshierarchien 50
Toggle Watchpoint 74, 75 VerifyListener 233
Tomcat 16, 559, 560 Version 51
Tomcat Web Server 263, 276, 397 Versionierung 147
– Deployment eines Web Services 401 Versionsverwaltung 15, 138
– Deployment Web Anwendung 279 Verzeichnisdienst 301
– Start 279 Views 103
– webapps-Verzeichnis 279 Virtuelle Maschine 298
Toolbar 21, 82 Visual Age für Java 69
Topic 304, 380, 381 Vorlagen 48
TopicConnection 386
TopicPublisher 386 W
TopicSession 386 Wartung 123
toString 494 Watch 75, 78
Translation Packages 15 Watch-Expression 78
TraverseListener 233 Watchexpressions 79
Tree Viewer 418, 419, 420, 422, 452, 481, Watchpoint 74, 75, 76, 79
513, 514, 520, 526 Web Client
TreeContentProvider 420, 422, 425 – Zugriff Entity Bean 359
TreeListener 233 – Zugriff Session Bean 336
TreeViewer 418, 452 Web Server 276
Trennleisten 219 Web Service 389, 397
Try-Catch-Block-Generierung 46 – Deployment, Tomcat Web Server 401
types-Element 393, 408 – Deployment-Deskriptor 401
– Entwicklung 392
– Entwicklung eines Clients 404
U – WTP 398, 404
Überschreiben von Methoden 42 Web Service Deployment Plug-In 562
UDDI 389 Web Service Description Language 389
UDDI-Registrierungsdatenbank 389, 392 Web Tools Platform Siehe WTP
Umgebungsvariablen 67, 105 web.xml 278, 282
UML 15 Web-Anwendung 275, 292, 337
Undo 51 – Beispiel 276, 281
Universal Description, Discovery, and – Deployment-Deskriptor 278, 282
Integration 389 – Eclipse-Projekt 276
Unterklasse 54 – Exception Handling 280
Update Classpath 664 – JSP 281
Use Case 122 – Servlet 276

696
eclipse_v01SIX.fm Seite 697 Montag, 13. Februar 2006 12:29 12

Stichwortverzeichnis

– Start 279 – Erzeugen eines Clients 404


– Verzeichnisstruktur 279 – Fault Message 394
webapps 561 – Input Message 394
Webbrowser 103 – Kommunikationsendpunkt 396
WEB-INF 276 – Output Message 394
Werkzeugleiste 220, 221, 223 WTP 283, 312, 326
Wiederherstellung 51 – Dynamic Web Project 287
WindowListener 192 – EJB-Projekt 326
Workbench 411, 415, 476, 637 – Features 283
Workbench Page 469 – Installation 285
Workbench Part 415 – Konfiguration 312
Workbench Window 469 – Server Debugging 298
WorkbenchAdvisor 646 – Web Service 398, 404
WorkbenchPart 432 – XDoclet 286
Working Sets 25, 67, 78, 83, 86
Workspace 83, 86, 100, 103, 411, 412,
416, 476 X
Workspace-Core 411 XMI 118
WSDL 389, 391 XML 13, 264, 389
WSDL2Java 398 XML Editor 270
WSDL-Dokument 391, 392, 397, 404 – Ant Editor 273
– aus Java-Klasse 405 – Sample XML Editor 270
– Beispiel 392, 407 – XMLBuddy 273
– Element binding 395, 409 XML Schema 267
– Element definitions 393, 407 XMLBuddy 273
– Element message 394, 408 XML-Dokument 264
– Element operation 394 – DTD 265
– Element port 396 – Gültigkeitsprüfung 273
– Element portType 394, 395, 409 – Plug-In 270
– Element service 396, 410 – Prolog 265
– Element types 393, 408 – Root-Element 266
– endpoint 396 – Tag 265

697

You might also like