Verteilte Systeme Ein Tutorial über die durchgängige Anwendung

Document Sample
Verteilte Systeme Ein Tutorial über die durchgängige Anwendung Powered By Docstoc
					              Verteilte Systeme


Ein Tutorial über die durchgängige Anwendung
  verschiedener Java-Technologien bei einer
       gemeinsamen Ausgangsthematik




             Dipl.-Inf. (FH) Florian Lutz

                Sommersemester 2007



             Fachhochschule Regensburg

          Fachbereich Informatik/Mathematik
                                                           Inhaltsverzeichnis


Inhaltsverzeichnis

1. Einführung ............................................................................................................ 1
       1.1. Motivation ............................................................................................................................... 1
       1.2. Aufbau des Tutorials .............................................................................................................. 1
       1.3. Verwendete Software-Versionen ........................................................................................... 2
             1.3.1. Plattform / System..................................................................................................... 2
             1.3.2. Java........................................................................................................................... 2
             1.3.3. Eclipse....................................................................................................................... 2
             1.3.4. Frameworks / APIs.................................................................................................... 2

2. Ausgangsthematik Buchhaltung......................................................................... 3
       2.1. Übersicht ................................................................................................................................ 3
             2.1.1. Beschreibung ............................................................................................................ 3
             2.1.2. Objektmodell ............................................................................................................. 4
       2.2. Schnittstellen-Modell .............................................................................................................. 5
             2.2.1. Konto-Schnittstelle .................................................................................................... 6
             2.2.2. Kontotyp-Enumeration .............................................................................................. 7
             2.2.3. Buchungs-Schnittstelle ............................................................................................. 7
             2.2.4. Service-Schnittstelle ................................................................................................. 8
             2.2.5. Proxy-Schnittstelle .................................................................................................... 9
             2.2.6. Erweiterte Service-Schnittstelle ................................................................................ 9
       2.3. Ausnahmen .......................................................................................................................... 10
             2.3.1. Buchhaltungsfehler ................................................................................................. 10
             2.3.2. Ein/Ausgabe-Fehler ................................................................................................ 10
             2.3.3. Nicht implementierte Methoden .............................................................................. 11
             2.3.4. Andere Ausnahmen ................................................................................................ 11
       2.4. Modulares Konzept der Programmteile ............................................................................... 12
             2.4.1. Client ....................................................................................................................... 12
                     2.4.1.1. Proxy-Instanzierung über Reflection ......................................................... 12
                     2.4.1.2. Laden einer Service-Klasse....................................................................... 13
             2.4.2. Datenhaltung / Geschäftslogik................................................................................ 13
                     2.4.2.1. Daten und Persistenz ................................................................................ 13
                     2.4.2.2. Geschäftslogik ........................................................................................... 14
             2.4.3. Transport / Middleware ........................................................................................... 14
                     2.4.3.1. Client.......................................................................................................... 14
                     2.4.3.2. Server ........................................................................................................ 14
             2.4.4. Verkettung............................................................................................................... 15




                                                                       -I-
                                                           Inhaltsverzeichnis


3. Benutzeroberfläche ............................................................................................ 17
       3.1. Übersicht .............................................................................................................................. 17
       3.2. Zugriff über eine Proxy-Klasse............................................................................................. 18
              3.2.1. Laden und Verbinden eines Buchhaltungssystems................................................ 18
              3.2.2. Schließen der Verbindung ...................................................................................... 20
       3.3. Aufbau der Oberfläche ......................................................................................................... 21
              3.3.1. Kontenliste .............................................................................................................. 21
                     3.3.1.1. Laden der Kontenobjekte .......................................................................... 21
                     3.3.1.2. Anlegen eines neuen Kontos..................................................................... 21
                     3.3.1.3. Ändern der Daten eines Kontos ................................................................ 22
                     3.3.1.4. Löschen eines Kontos ............................................................................... 22
              3.3.2. Buchungsliste.......................................................................................................... 23
                     3.3.2.1. Laden der Buchungsobjekte...................................................................... 23
                     3.3.2.2. Erfassen einer neuen Buchung ................................................................. 23
                     3.3.2.3. Ändern der Daten einer Buchung .............................................................. 24
                     3.3.2.4. Löschen einer Buchung............................................................................. 24
              3.3.3. Soll- und Haben-Buchungen je Konto .................................................................... 25
              3.3.4. Saldo eines Kontos ................................................................................................. 25
       3.4. Behandlung von Ausnahmen ............................................................................................... 26
              3.4.1. Nachrichtenfenster.................................................................................................. 26
              3.4.2. Statusleiste ............................................................................................................. 26
              3.4.3. Direkte Einblendung................................................................................................ 27
       3.5. Kapselung aller Aufrufe........................................................................................................ 28
              3.5.1. Adapterschicht ........................................................................................................ 28
                     3.5.1.1. Swing-MVC-Pattern ................................................................................... 28
                     3.5.1.2. Zwischenspeicherung der Daten ............................................................... 29
              3.5.2. Aufteilung in separate Vorgänge ............................................................................ 29
                     3.5.2.1. Vorgänge zum Zugriff auf die Buchhaltung ............................................... 30
                     3.5.2.2. Vorgänge zur Aktualisierung der Benutzeroberfläche............................... 31
              3.5.3. Kontrolle der Vorgänge ........................................................................................... 32
                     3.5.3.1. Status-Benachrichtigung über Listener-Interface ...................................... 32
                     3.5.3.2. Auflistung der laufenden Vorgänge ........................................................... 32
                     3.5.3.3. Abbruch eines Vorgangs ........................................................................... 33
       3.6. Threading ............................................................................................................................. 34
              3.6.1. Zentraler Threading-Service ................................................................................... 34
                     3.6.1.1. AWT-Event-Dispatching-Thread................................................................ 34
                     3.6.1.2. Worker-Threads......................................................................................... 35
              3.6.2. Threading-Modelle .................................................................................................. 35
                     3.6.2.1. Singlethreaded........................................................................................... 36
                     3.6.2.2. Synchron einreihen.................................................................................... 37
                     3.6.2.3. Asynchron mit zusätzlichem Thread.......................................................... 38
                     3.6.2.4. Multithreaded ............................................................................................. 38
              3.6.3. Abbruch eines laufenden Vorgangs........................................................................ 39
       3.7. Weitere Funktionen .............................................................................................................. 41
              3.7.1. Testfunktionen ........................................................................................................ 41
                     3.7.1.1. Daten erzeugen und aufräumen................................................................ 41
                     3.7.1.2. Test der Funktionalität ............................................................................... 41
                     3.7.1.3. Test der Geschwindigkeit .......................................................................... 42
                     3.7.1.4. Test auf Threadsicherheit.......................................................................... 42
              3.7.2. Logging ................................................................................................................... 43
                     3.7.2.1. Logger-Klasse............................................................................................ 43
                     3.7.2.2. Auswertung des Stacktrace....................................................................... 44
                     3.7.2.3. Hierarchische Methodenaufrufe ................................................................ 44
                     3.7.2.4. Unterscheidung nach Thread .................................................................... 45
              3.7.3. Look & Feel ............................................................................................................. 45
              3.7.4. Hilfe ......................................................................................................................... 47
       3.8. Suchen und Laden der Proxy-Klasse .................................................................................. 48
              3.8.1. Suchverzeichnis...................................................................................................... 48
              3.8.2. Klassenlader für den Suchvorgang......................................................................... 48
              3.8.3. Listener-Schnittstelle für den Klassenlader ............................................................ 49

                                                                      - II -
                                                 Inhaltsverzeichnis

      3.8.4. Eigenschaften gefundener Klassen ........................................................................ 50
      3.8.5. Laden gefundener Klassen ..................................................................................... 51
      3.8.6. Anmerkung.............................................................................................................. 51
3.9. Verwendung der Buchhaltung in Eclipse ............................................................................. 52
      3.9.1. Neues Projekt anlegen............................................................................................ 52
      3.9.2. Importieren des Package mit der Buchhaltungsoberfläche .................................... 54
      3.9.3. Start der Anwendung .............................................................................................. 58
      3.9.4. Erstellung eines eigenen Moduls für die Buchhaltung............................................ 59




                                                          - III -
                                                           Inhaltsverzeichnis


4. Datenhaltung....................................................................................................... 63
       4.1. Übersicht .............................................................................................................................. 63
              4.1.1. Datenspeicherung................................................................................................... 63
              4.1.2. Geschäftslogik ........................................................................................................ 63
       4.2. Einfache lokale Datenhaltung .............................................................................................. 64
              4.2.1. Allgemeines ............................................................................................................ 64
              4.2.2. Daten in HashMaps ................................................................................................ 64
                     4.2.2.1. Konten und Buchungen ............................................................................. 64
                     4.2.2.2. Soll- und Haben-Buchungen ..................................................................... 64
                     4.2.2.3. Bidirektionaler Bezug................................................................................. 64
              4.2.3. Implementierung der Schnittstellen ........................................................................ 65
                     4.2.3.1. Service-Klasse........................................................................................... 65
                     4.2.3.2. Konto-Klasse ............................................................................................. 67
                     4.2.3.3. Buchungs-Klasse....................................................................................... 69
                     4.2.3.4. Proxy-Klasse.............................................................................................. 70
              4.2.4. Fazit ........................................................................................................................ 70
       4.3. Java-Serialisierung............................................................................................................... 71
              4.3.1. Allgemeines ............................................................................................................ 71
              4.3.2. Serialisierbare Klassen ........................................................................................... 71
                     4.3.2.1. Konto-Klasse ............................................................................................. 71
                     4.3.2.2. Buchungs-Klasse....................................................................................... 71
              4.3.3. De-/Serialisierungs-Vorgang................................................................................... 72
                     4.3.3.1. Service-Klasse........................................................................................... 72
                     4.3.3.2. Proxy-Klasse.............................................................................................. 73
              4.3.4. Fazit ........................................................................................................................ 74
       4.4. XML-Serialisierung über JAXB............................................................................................. 75
              4.4.1. Allgemeines ............................................................................................................ 75
              4.4.2. Annotations ............................................................................................................. 75
                     4.4.2.1. Package-Annotations ................................................................................ 75
                     4.4.2.2. Klassen-Annotations.................................................................................. 76
                     4.4.2.3. Enum-Annotations ..................................................................................... 77
                     4.4.2.4. Field-Annotations....................................................................................... 77
                     4.4.2.5. Objekt-Fabrik-Annotations......................................................................... 79
                     4.4.2.6. Adapter-Annotations .................................................................................. 79
              4.4.3. Anwendung der Annotations bei den Buchhaltungs-Klassen................................. 79
                     4.4.3.1. Service-Klasse........................................................................................... 79
                     4.4.3.2. Buchungs-Klasse....................................................................................... 80
                     4.4.3.3. Callback-Methoden.................................................................................... 81
                     4.4.3.4. Konto-Klasse ............................................................................................. 82
              4.4.4. Erzeugung des XSD-Schemas aus den annotierten Klassen ................................ 82
                     4.4.4.1. Generierung durch einen Ant-Vorgang ..................................................... 82
                     4.4.4.2. Das Schema zu den HashMaps ................................................................ 83
                     4.4.4.3. Resultierende XML-Datei .......................................................................... 84
                     4.4.4.4. Probleme ................................................................................................... 84
              4.4.5. Implementierung der Klassen nach einem vorgegebenen Schema ....................... 85
                     4.4.5.1. Service-Klasse........................................................................................... 85
                     4.4.5.2. Konto-Klasse ............................................................................................. 86
                     4.4.5.3. Buchungs-Klasse....................................................................................... 86
                     4.4.5.4. Schema und Daten .................................................................................... 87
              4.4.6. Proxy- und Utility-Klasse......................................................................................... 89
                     4.4.6.1. Proxy-Klasse.............................................................................................. 89
                     4.4.6.2. Utility-Klasse .............................................................................................. 90
                     4.4.6.3. Speichern der Objekte............................................................................... 90
                     4.4.6.4. Laden der Objekte ..................................................................................... 91
              4.4.7. Fazit ........................................................................................................................ 91
       4.5. Direkter SQL-Datenbankzugriff mit JDBC............................................................................ 92
              4.5.1. Allgemeines ............................................................................................................ 92
              4.5.2. JDBC-Datenbankanbindung ................................................................................... 92
                     4.5.2.1. Verbindung aufbauen ................................................................................ 92
                     4.5.2.2. Einzelne Werte .......................................................................................... 93

                                                                     - IV -
                                                    Inhaltsverzeichnis

              4.5.2.3. Auflistungen ............................................................................................... 94
              4.5.2.4. Anweisungen ............................................................................................. 94
              4.5.2.5. PreparedStatements.................................................................................. 95
       4.5.3. SQL-Anweisungen .................................................................................................. 95
              4.5.3.1. Datenbank-Schema ................................................................................... 95
              4.5.3.2. Service-Klasse........................................................................................... 96
              4.5.3.3. Konto-Klasse ............................................................................................. 97
              4.5.3.4. Buchungs-Klasse....................................................................................... 97
       4.5.4. Fazit ........................................................................................................................ 98
4.6. Hibernate.............................................................................................................................. 99
       4.6.1. Allgemeines ............................................................................................................ 99
              4.6.1.1. Persistenz von Objekten.......................................................................... 100
              4.6.1.2. Arbeitsweise mit POJOs.......................................................................... 100
              4.6.1.3. OR-Mapping ............................................................................................ 101
       4.6.2. Annotations ........................................................................................................... 101
              4.6.2.1. Entitäten................................................................................................... 101
              4.6.2.2. Identifikatoren - Schlüssel ....................................................................... 101
              4.6.2.3. Einfache Daten ........................................................................................ 102
              4.6.2.4. Beziehungen............................................................................................ 103
              4.6.2.5. Kollektionen ............................................................................................. 104
              4.6.2.6. Kaskadierung........................................................................................... 106
       4.6.3. Installation von Hibernate ..................................................................................... 106
              4.6.3.1. Hibernate-Einstellungen .......................................................................... 106
              4.6.3.2. Datenbankanbindung .............................................................................. 107
              4.6.3.3. Weitere Einstellungen.............................................................................. 107
       4.6.4. Zugriff auf Hibernate ............................................................................................. 108
              4.6.4.1. Erzeugen der Konfiguration..................................................................... 108
              4.6.4.2. Sitzungen................................................................................................. 108
              4.6.4.3. Transaktionen .......................................................................................... 109
              4.6.4.4. Fehlerbehandlung.................................................................................... 110
              4.6.4.5. Transaktions-Zeitlimit .............................................................................. 110
              4.6.4.6. Versionskontrolle ..................................................................................... 111
              4.6.4.7. Sperren .................................................................................................... 112
       4.6.5. Verwendung der Objekte ...................................................................................... 112
              4.6.5.1. Zustände.................................................................................................. 112
              4.6.5.2. Automatische Erkennung des Objektzustandes...................................... 113
              4.6.5.3. Laden der Objekte ................................................................................... 114
              4.6.5.4. Abfragen .................................................................................................. 115
              4.6.5.5. Modifikationen an Objekten ..................................................................... 115
              4.6.5.6. Löschen persistenter Objekte.................................................................. 115
       4.6.6. Buchhaltung mit einer Singleton-Service-Klasse.................................................. 116
              4.6.6.1. Service-Klasse......................................................................................... 116
              4.6.6.2. Konto-Klasse ........................................................................................... 118
              4.6.6.3. Buchungs-Klasse..................................................................................... 119
              4.6.6.4. Proxy-Klasse............................................................................................ 119
       4.6.7. Buchhaltung mit einer Entity-Service-Klasse........................................................ 120
              4.6.7.1. Service-Klasse......................................................................................... 120
              4.6.7.2. Konto-Klasse ........................................................................................... 121
              4.6.7.3. Buchungs-Klasse..................................................................................... 121
              4.6.7.4. Proxy-Klasse............................................................................................ 122
              4.6.7.5. Fazit ......................................................................................................... 123




                                                              -V-
                                                           Inhaltsverzeichnis


5. Transport............................................................................................................124
       5.1. Übersicht ............................................................................................................................ 124
              5.1.1. Prinzipieller Ablauf ................................................................................................ 124
              5.1.2. Erstellung mit Eclipse............................................................................................ 125
       5.2. Eigener Transport über Sockets ........................................................................................ 126
              5.2.1. Allgemeines .......................................................................................................... 126
              5.2.2. Anbindung an die Datenhaltung ........................................................................... 126
                     5.2.2.1. SocketServer ........................................................................................... 126
                     5.2.2.2. Parallele Server-Instanzen ...................................................................... 127
              5.2.3. Aufbau und Nutzung der Verbindung ................................................................... 127
                     5.2.3.1. Socket-Proxy-Klasse ............................................................................... 127
                     5.2.3.2. Verarbeitung auf dem Server .................................................................. 128
              5.2.4. Definition eines eigenes Protokolls....................................................................... 129
                     5.2.4.1. Anfrage .................................................................................................... 129
                     5.2.4.2. Request-Klassen ..................................................................................... 130
                     5.2.4.3. Antwort..................................................................................................... 132
                     5.2.4.4. Response-Klassen................................................................................... 132
              5.2.5. Un-/Marshalling der Objekte ................................................................................. 134
                     5.2.5.1. Die Klasse StringUnMarshaller................................................................ 134
                     5.2.5.2. Verarbeitung definierter Superklassen .................................................... 134
                     5.2.5.3. Aufruf der un-/marshal-Methoden............................................................ 135
                     5.2.5.4. Einfache Objekte ..................................................................................... 135
                     5.2.5.5. Kollektionen ............................................................................................. 136
                     5.2.5.6. Ausnahmen.............................................................................................. 136
              5.2.6. Entfernte Objekte .................................................................................................. 137
                     5.2.6.1. Aufruf der Methoden über Reflection ...................................................... 137
                     5.2.6.2. Übertragung der entfernten Objekte........................................................ 137
                     5.2.6.3. Lokale Stellvertreter................................................................................. 139
                     5.2.6.4. Anfrage-Methoden ................................................................................... 139
              5.2.7. Fazit ...................................................................................................................... 141
       5.3. Remote Method Invocation (RMI) ...................................................................................... 142
              5.3.1. Allgemeines .......................................................................................................... 142
              5.3.2. Entfernte Objekte .................................................................................................. 143
                     5.3.2.1. Remote-Interface ..................................................................................... 143
                     5.3.2.2. Remote-Object......................................................................................... 143
              5.3.3. Anbindung an die Datenhaltung ........................................................................... 143
                     5.3.3.1. Proxy-Instanzierung................................................................................. 143
                     5.3.3.2. Weitergabe der Aufrufe ........................................................................... 144
              5.3.4. Server - RMI-Registry ........................................................................................... 144
                     5.3.4.1. Registry starten........................................................................................ 144
                     5.3.4.2. Registry einbinden ................................................................................... 144
                     5.3.4.3. Objekt registrieren ................................................................................... 145
                     5.3.4.4. Der vollständige Server ........................................................................... 145
              5.3.5. Entfernte Buchhaltungsobjekte............................................................................. 146
                     5.3.5.1. Service-Klasse......................................................................................... 146
                     5.3.5.2. Konto-Klasse ........................................................................................... 147
                     5.3.5.3. Buchungs-Klasse..................................................................................... 148
              5.3.6. Aufruf der entfernten Buchhaltung........................................................................ 148
                     5.3.6.1. Anfrage an den Namensdienst ................................................................ 149
                     5.3.6.2. RMI-Proxy-Klasse.................................................................................... 149
              5.3.7. Codebase.............................................................................................................. 150
              5.3.8. Fazit ...................................................................................................................... 150
       5.4. CORBA............................................................................................................................... 151
              5.4.1. Allgemeines .......................................................................................................... 151
                     5.4.1.1. Object Request Broker (ORB) ................................................................. 151
                     5.4.1.2. Interoperable Object Reference (IOR)..................................................... 151
                     5.4.1.3. Portable Object Adapter .......................................................................... 151
                     5.4.1.4. Tie Mechanismus..................................................................................... 151
                     5.4.1.5. CORBA-Dienste....................................................................................... 151
                     5.4.1.6. Implementierungen .................................................................................. 152

                                                                     - VI -
                                           Inhaltsverzeichnis

5.4.2. Interface Definition Language............................................................................... 153
       5.4.2.1. Buchhaltungs-IDL .................................................................................... 153
       5.4.2.2. Zuordnung der IDL-Elemente zu Java-Typen ......................................... 155
       5.4.2.3. Java-Artefakte generieren ....................................................................... 155
       5.4.2.4. Erzeugte Klassen und Schnittstellen ....................................................... 156
5.4.3. Verwendung der erzeugten Klassen auf der Serverseite ..................................... 157
       5.4.3.1. CORBA-Server ........................................................................................ 157
       5.4.3.2. Konto-Klasse ........................................................................................... 160
       5.4.3.3. Buchungs-Klasse..................................................................................... 161
       5.4.3.4. Der ORB-Daemon ................................................................................... 161
5.4.4. Verwendung der erzeugten Klassen auf der Clientseite ...................................... 162
       5.4.4.1. Service-Klasse......................................................................................... 162
       5.4.4.2. Konto-Klasse ........................................................................................... 163
       5.4.4.3. Buchungs-Klasse..................................................................................... 164
       5.4.4.4. Proxy-Klasse............................................................................................ 165
5.4.5. JACORB................................................................................................................ 166
       5.4.5.1. Features................................................................................................... 166
       5.4.5.2. Installation................................................................................................ 166
       5.4.5.3. Verwendung............................................................................................. 167
       5.4.5.4. Einstellungen ........................................................................................... 168
5.4.6. Fazit ...................................................................................................................... 168




                                                    - VII -
                                                           Inhaltsverzeichnis


6. Java-Enterprise..................................................................................................169
       6.1. Übersicht ............................................................................................................................ 169
             6.1.1. Komponenten........................................................................................................ 170
                    6.1.1.1. EJB-Container ......................................................................................... 170
                    6.1.1.2. Application-Client-Container.................................................................... 170
                    6.1.1.3. Web-Container......................................................................................... 170
                    6.1.1.4. Applet-Container...................................................................................... 170
                    6.1.1.5. Datenbank ............................................................................................... 170
             6.1.2. Dienste .................................................................................................................. 171
                    6.1.2.1. JMS.......................................................................................................... 171
                    6.1.2.2. JMX.......................................................................................................... 171
                    6.1.2.3. JAAS........................................................................................................ 171
                    6.1.2.4. JTA .......................................................................................................... 171
                    6.1.2.5. JavaMail................................................................................................... 171
                    6.1.2.6. JAF .......................................................................................................... 171
                    6.1.2.7. JCA .......................................................................................................... 172
                    6.1.2.8. JAXR........................................................................................................ 172
                    6.1.2.9. JAX-RPC ................................................................................................. 172
             6.1.3. Verfügbare Implementierungen ............................................................................ 172
                    6.1.3.1. Open Source Server ................................................................................ 172
                    6.1.3.2. Proprietäre Server ................................................................................... 172
                    6.1.3.3. Web-Container......................................................................................... 172
       6.2. Der JBoss-Application-Server ............................................................................................ 173
             6.2.1. Allgemeines .......................................................................................................... 173
             6.2.2. Installation ............................................................................................................. 173
                    6.2.2.1. Aus dem beiliegenden Paket................................................................... 174
                    6.2.2.2. Originalinstallation von jboss.org............................................................. 174
                    6.2.2.3. Verzeichnisstruktur .................................................................................. 175
             6.2.3. Datenbankanbindung............................................................................................ 175
                    6.2.3.1. Hypersonic............................................................................................... 176
                    6.2.3.2. MySQL..................................................................................................... 177
                    6.2.3.3. PostgreSQL ............................................................................................. 178
                    6.2.3.4. Oracle ...................................................................................................... 178
             6.2.4. Verwendung.......................................................................................................... 179
                    6.2.4.1. Einfacher Start ......................................................................................... 180
                    6.2.4.2. JBoss als Windows-Systemdienst........................................................... 181
                    6.2.4.3. Deployment von Anwendungen............................................................... 182
             6.2.5. Verwaltung über die JMX-Konsole ....................................................................... 182
                    6.2.5.1. System-Eigenschaften............................................................................. 183
                    6.2.5.2. JNDI-Übersicht ........................................................................................ 183
                    6.2.5.3. Hypersonic-Manager ............................................................................... 184
             6.2.6. Produktiver Betrieb ............................................................................................... 185
             6.2.7. Integration in die IDE ............................................................................................ 185
                    6.2.7.1. Eclipse-Projekt anlegen........................................................................... 185
                    6.2.7.2. JBoss-Konfiguration anlegen................................................................... 187
                    6.2.7.3. Den Server steuern.................................................................................. 188
                    6.2.7.4. Deployment eigener Anwendungen ........................................................ 189
       6.3. Enterprise JavaBeans ........................................................................................................ 190
             6.3.1. Allgemeines .......................................................................................................... 190
                    6.3.1.1. Annotations.............................................................................................. 190
                    6.3.1.2. Home-Interface ........................................................................................ 190
                    6.3.1.3. Business-Interface ................................................................................... 190
                    6.3.1.4. Life-Cycle und Callback-Methoden.......................................................... 191
             6.3.2. Session-Beans...................................................................................................... 192
                    6.3.2.1. Zustandslose Session-Beans .................................................................. 192
                    6.3.2.2. Zustandsbehaftete Session-Beans.......................................................... 192
                    6.3.2.3. Dependency-Injection.............................................................................. 193
                    6.3.2.4. Interceptoren (AOP)................................................................................. 193
                    6.3.2.5. EntityManager.......................................................................................... 194
                    6.3.2.6. Service-Klasse......................................................................................... 194

                                                                   - VIII -
                                                           Inhaltsverzeichnis

             6.3.3. Entity-Beans.......................................................................................................... 197
                    6.3.3.1. Bean-Managed-Persistence .................................................................... 197
                    6.3.3.2. Container-Managed-Persistence............................................................. 197
                    6.3.3.3. FetchType................................................................................................ 198
                    6.3.3.4. Entity-Bean-Prinzip .................................................................................. 198
                    6.3.3.5. Konto-Klasse ........................................................................................... 199
                    6.3.3.6. Buchungs-Klasse..................................................................................... 201
             6.3.4. Weitere Bean-Arten .............................................................................................. 202
                    6.3.4.1. JMS - Message-Driven-Beans................................................................. 202
                    6.3.4.2. JMX - Management-Beans...................................................................... 205
                    6.3.4.3. Service-Beans ......................................................................................... 206
                    6.3.4.4. TimerService............................................................................................ 206
                    6.3.4.5. Management-Service-Bean mit Timer-Service ....................................... 207
             6.3.5. Deployment........................................................................................................... 208
                    6.3.5.1. Enterprise-Archiv ..................................................................................... 208
                    6.3.5.2. Archiv für Session-Beans ........................................................................ 209
                    6.3.5.3. Archiv für Entitiy-Beans ........................................................................... 209
                    6.3.5.4. Archiv für weitere Klassen ....................................................................... 210
                    6.3.5.5. Gemeinsame Ant-Build-Datei .................................................................. 210
                    6.3.5.6. Einstellungen ........................................................................................... 212
             6.3.6. Client-Anwendung................................................................................................. 213
                    6.3.6.1. Einbinden der nötigen Bibliotheken ......................................................... 213
                    6.3.6.2. Aufruf der Anwendung auf dem Server ................................................... 213
                    6.3.6.3. Senden einer Nachricht an die MessageBean ........................................ 214
                    6.3.6.4. Allgemeine Client-Einstellungen.............................................................. 215
       6.4. Web-Services ..................................................................................................................... 216
             6.4.1. Allgemeines .......................................................................................................... 216
                    6.4.1.1. RPC-Style vs. Document-Style................................................................ 217
                    6.4.1.2. literal vs. encoded.................................................................................... 217
             6.4.2. Die Buchhaltung als Web-Service-RPC-Endpunkt............................................... 218
                    6.4.2.1. Service-Schnittstelle ................................................................................ 218
                    6.4.2.2. Konto-Schnittstelle................................................................................... 219
                    6.4.2.3. Buchungs-Schnittstelle ............................................................................ 219
                    6.4.2.4. Implementierung ...................................................................................... 220
                    6.4.2.5. Deployment.............................................................................................. 221
             6.4.3. JBoss-Web-Service-Tools .................................................................................... 223
                    6.4.3.1. Verwendung mit Ant ................................................................................ 223
                    6.4.3.2. WSDL-Datei............................................................................................. 224
                    6.4.3.3. Client-Artefakte ........................................................................................ 225
                    6.4.3.4. Erfahrungen mit den JBoss-Web-Service-Tools ..................................... 225
             6.4.4. Web-Services mit AXIS......................................................................................... 226
                    6.4.4.1. Installation................................................................................................ 226
                    6.4.4.2. Ant-Vorgang............................................................................................. 226
                    6.4.4.3. Client-Artefakte ........................................................................................ 227
             6.4.5. Client-Implementierung......................................................................................... 227
                    6.4.5.1. Proxy-Klasse............................................................................................ 227
                    6.4.5.2. Service-Klasse......................................................................................... 228
                    6.4.5.3. Konto-Klasse ........................................................................................... 229
                    6.4.5.4. Buchungs-Klasse..................................................................................... 231
             6.4.6. Fazit ...................................................................................................................... 231

7. JSF-Web-Oberfläche .........................................................................................232
       7.1. Übersicht ............................................................................................................................ 232




                                                                     - IX -
                                                           Inhaltsverzeichnis


8. Buchhaltung Mobil ............................................................................................233
       8.1. Übersicht ............................................................................................................................ 233
              8.1.1. Pocket-PC ............................................................................................................. 233
              8.1.2. Windows-Mobile.................................................................................................... 233
              8.1.3. Java Micro Edition................................................................................................. 234
              8.1.4. Mysaifu JVM ......................................................................................................... 234
       8.2. Installation .......................................................................................................................... 235
              8.2.1. Laufzeitumgebung ................................................................................................ 235
              8.2.2. Aufruf der JVM ...................................................................................................... 235
              8.2.3. Entwicklungsumgebung ........................................................................................ 235
       8.3. Buchhaltungsoberfläche..................................................................................................... 236
              8.3.1. Verfügbare Proxy-Module ..................................................................................... 236
              8.3.2. Aufbau der Oberfläche.......................................................................................... 237
                      8.3.2.1. Konto und Buchungsliste......................................................................... 237
                      8.3.2.2. Konto anlegen oder ändern ..................................................................... 238
                      8.3.2.3. Buchung anlegen oder ändern ................................................................ 238
                      8.3.2.4. Konto oder Buchung löschen .................................................................. 239
                      8.3.2.5. Konto-Details ........................................................................................... 239
              8.3.3. Ausnahmen........................................................................................................... 240
              8.3.4. Menü ..................................................................................................................... 240
                      8.3.4.1. Schnittstellen-Menü ................................................................................. 240
                      8.3.4.2. Hilfe-Menü ............................................................................................... 241
       8.4. Anbindung an die vorhandenen Systeme .......................................................................... 242
              8.4.1. Lokale und Dummy-Klassen ................................................................................. 242
              8.4.2. An das Socket-Modul............................................................................................ 242
              8.4.3. Über RMI............................................................................................................... 242
              8.4.4. Andere Technologien............................................................................................ 242
       8.5. Erfahrungen und Probleme ................................................................................................ 242




                                                                      -X-
                                                          Inhaltsverzeichnis


Schlußwort .............................................................................................................243

Verzeichnisse.........................................................................................................244
       Abkürzungsverzeichnis ............................................................................................................. 244
       Abbildungsverzeichnis .............................................................................................................. 246
       Codeverzeichnis........................................................................................................................ 249
       Resourcenverzeichnis ............................................................................................................... 252
       Tabellenverzeichnis................................................................................................................... 253
       Quellenverzeichnis .................................................................................................................... 254

Anhänge .................................................................................................................259
       Anhang A: System-Eigenschaften des JBoss........................................................................... 259
       Anhang B: Erweiterte Darstellung des asynchronen Threading ............................................... 261
       Anhang C: Notwendige Bibliotheken......................................................................................... 262
            Java Architecture for XML Binding (JAXB) ..................................................................... 262
            Hibernate-Core................................................................................................................ 262
            Hibernate-Annotations .................................................................................................... 262
            Web-Services.................................................................................................................. 262
            JBoss-Web-Services-Tools............................................................................................. 262
            AXIS ................................................................................................................................ 263
            JBoss-Client .................................................................................................................... 263
            JacORB........................................................................................................................... 263
       Anhang D: Performance-Messung............................................................................................ 264
            Datenhaltung................................................................................................................... 264
            Transport......................................................................................................................... 264
            Enterprise JavaBeans..................................................................................................... 264
       Anhang E: WSDL-Datei für den Buchhaltungsservice.............................................................. 265
       Anhang F: Changelog der Anwendung ..................................................................................... 273




                                                                    - XI -
                                  Einführung - Motivation


1. Einführung
1.1. Motivation
Der Ausgangspunkt für dieses Tutorial ist die Vorlesung Verteilte Systeme des Sommerseme-
sters 2006. Dabei war ein Thema die Implementierung eines verteilten Systems anhand von
vorgegebenen Schnittstellen. Diese Schnittstellen wurden von der entsprechenden Vorlesung
Verteilte Systeme an der Fachhochschule Aargau

   http://www.gruntz.ch/courses/ds/index.html

übernommen und spezifizieren eine einfache Bankanwendung mit einem Bankobjekt und
mehreren Kontenobjekten. Im Verlauf des Semesters führten diese Vorgaben eigentlich eher
unbeabsichtigt zu mehreren modularen Implementierungen der Bankanwendung, die jeweils
auf unterschiedliche Weise (Sockets, RMI, CORBA) ein verteiltes System realisieren und
aufeinander aufbauen und miteinander verkettet sind. Daraus entstand die Idee, gezielt ein
eigenständiges und etwas umfangreiches Beispiel für die unterschiedlichen Möglichkeiten
verteilter Systeme mit Java zu erstellen.


1.2. Aufbau des Tutorials
Dieses Tutorial soll anhand einer durchgehenden thematischen Grundlage mehrere Vorge-
hensweisen und Technologien darstellen, die im Zusammenhang mit verteilten Systemen un-
ter Java (zum Teil aber auch systemübergreifend) anwendbar sind. Zuerst wird die Ausgangs-
thematik, eine Finanzbuchhaltungsanwendung, näher erläutert und analog zur Bankanwen-
dung die nötigen Schnittstellen definiert.
Danach wird die Clientanwendung vorgestellt, eine Swing-Benutzeroberfläche, die den
Zugriff auf die spezifizierten Buchhaltungsschnittstellen durchführt, und die Resultate aus-
wertet und darstellt.
Als nächstes folgen verschiedene Möglichkeiten der Datenhaltung, um lokal oder auf der Ser-
verseite die Daten zu speichern.
Dann werden übliche netzwerkbasierte Übertragungsarten betrachtet, bei denen die Anwen-
dung nun in Client- und Serverseite aufgeteilt wird
Schließlich folgt die Betrachtung und Implementierung der Buchhaltung mit Enterprise Java
Beans und als Web-Service, sowie einer Web-Oberfläche mit Java-Server-Faces als neue
Client-Anwendung zu den Beans.

Vom Umfang der einzelnen Teilthemen her liegt das Hauptaugenmerk des Tutorials beim
Hibernate-Persistenz-Framework (siehe 4.6) und dem JBoss-Application-Server (siehe 6.2).




                                           -1-
                       Einführung - Verwendete Software-Versionen


1.3. Verwendete Software-Versionen
1.3.1. Plattform / System

Entwickelt wird die Buchhaltungsanwendung unter dem Betriebssystem Windows XP, und
um zusätzlichen Ballast zu vermeiden, wird jeweils nicht explizit auf andere Plattformen ein-
gegangen. Allerdings sollte es durch die prinzipielle plattformunabhängigkeit von Java leicht
möglich sein, alle angesprochenen Vorgehensweisen auch auf andere Systeme zu übertragen.


1.3.2. Java

Bei den zugehörigen Java-Programmen kommt der JDK (Java Development Kit) in der Ver-
sion 5.0 (1.5.0) Update 12 [01] zum Einsatz. Alle implementierten Beispiele sind explizit auf
den Features dieser Java-Version aufgebaut und nicht mit älteren Versionen von Java ver-
wendbar.
Mit der neueren Java-Version 6 ist die Anwendung größtenteils lauffähig, lediglich die Web-
Services (6.4) funktionieren nicht ohne besondere Modifikationen, weil die Web-Services-
Klassen im JDK 6 nicht mit den Web-Services-Klassen, die der JBoss-Application-Server
(6.2) mitbringt und benötigt, kompatibel sind.


1.3.3. Eclipse

Als Entwicklungsumgebung wird die "JBoss Eclipse IDE" [38] verwendet. Ein modifiziertes
und mit etlichen Plugins erweitertes Eclipse 3.2.1 [30], das von den JBoss-Entwicklern unter

   http://labs.jboss.com/portal/jbosside

zur Verfügung gestellt wird und um spezifische Features, die speziell die Verwendung des
JBoss-Application-Servers unterstützen, erweitert wurde.
Während der Entwicklungszeit erschien die neuere Eclipse-Version 3.3.0 (Europa) [82], mit
der ebenfalls gearbeitet werden kann. Diese muß jedoch manuell um die JBoss-Plugins erwei-
tert werden.
Alle vorgestellten Beispiel-Programme liegen im Quellcode als Eclipse-Projekt bei.


1.3.4. Frameworks / APIs

Dieses Tutorial hält sich an die zum Zeitpunkt der Erstellung (Januar und Februar 2007) ver-
fügbaren Versionen der verwendeten APIs, wobei sich jedoch einige noch in einem Entwick-
lungsstadium befinden, bei dem sich zu einem späteren Zeitpunkt noch Änderungen ergeben
können, die dann u.U. von den vorgestellten Inhalten und Vorgehensweisen abweichen.

   •   Hibernate-Core 3.2 [27]
   •   Hibernate-Annotations 3.2.0.GA [27]
   •   JWSDP 2.0 - Java Web-Services Development Pack [28]
   •   JBoss-Application-Server 4.0.5.GA [02]
   •   Apache AXIS 1.4 [32]



                                            -2-
                        Ausgangsthematik Buchhaltung - Übersicht


2. Ausgangsthematik Buchhaltung
2.1. Übersicht
2.1.1. Beschreibung

Als Rahmen und Ausgangsbeispiel für die verschiedenen Konzepte und Technologien, die im
Folgenden vorgestellt werden, soll eine stark vereinfachte Finanzbuchhaltung [12] verwendet
werden. Dies dient dabei aber nur als durchgängige Thematik, das Hauptaugenmerk liegt auf
den Java-Technologien.

Die Buchhaltung wird durch folgende Annahmen definiert:

   •   jeder Buchungssatz besitzt genau eine Soll- und eine Haben-Buchung.

   •   da nur eine Soll- und eine Haben-Buchung existieren besitzt ein Buchungssatz nur ge-
       nau einen Betrag.

   •   das Buchungsdatum wird nicht erhoben

   •   Umsatzsteuer wird grundsätzlich nicht berücksichtigt

   •   Konten werden lediglich in fünf definierte Kontenarten unterschieden:

          o   Aktiv
          o   Passiv
          o   Aufwand
          o   Ertrag
          o   Ergebnisrechnung

   •   Es gibt folglich keine automatische Aggregation von Hilfskonten in übergeordnete
       Konten

   •   Es gibt keine automatisierte Generierung von Ergebnisrechnungen

Ansonsten verhält sich das Buchhaltungssystem passend zu den im Fach Rechnungswesen
vermittelten Buchhaltungsmethoden [13] nach HGB unter Verwendung z.B. des Industrie-
Kontenrahmen




                                           -3-
                           Ausgangsthematik Buchhaltung - Übersicht



2.1.2. Objektmodell

Diese Annahmen führen dann zu einem vereinfachten Objektmodell, das die relevanten Daten
einer Finanzbuchhaltung abdeckt.




Abbildung 1: Objektmodell der Buchhaltung

Das Modell erlaubt es Buchungssätze in der folgenden gewohnten Form anzugeben:


Soll-Konto         an Haben-Konto           Betrag         Text
6000               4400                     1000,- €       Kauf von Rohstoffen
Tabelle 1: Beispiel-Buchungsatz



Um später Rechnungen auf einem entfernten System durchzuführen, soll der Buchhaltungs-
dienst eine Saldierfunktion bieten, die in verteilten Szenarien jeweils auf dem Server den Sal-
do zu einer gegebenen Kontonummer ermittelt, auch wenn diese Funktionalität eigentlich
eher einem Konto direkt zuzuordnen wäre.




                                              -4-
                    Ausgangsthematik Buchhaltung - Schnittstellen-Modell


2.2. Schnittstellen-Modell
Im Hinblick auf eine serviceorientierte Programmierung und die spätere Implementierung der
verschiedenen Technologien wird das Model des Buchhaltungssystems auf Schnittstellen mit
durchgängigen englischen Bezeichnern abgebildet.

AccountType.ACTIVE                                  Aktiv-Konto
AccountType.PASSIVE                                 Passiv-Konto
AccountType.EXPENSE                                 Aufwand-Konto
AccountType.PROCEEDS                                Ertrag-Konto
AccountType.AMOUNT                                  Ergebnisrechnung (IKR Klasse 8)
Account                                             Konto
AccountingEntry                                     Buchungssatz
AccountingEntry.DebitAccount                        Soll-Konto
AccountingEntry.CreditAccount                       Haben-Konto
AccountingEntry.Amount                              Betrag der Buchung
Account.Balance                                     Saldo des Kontos

Tabelle 2: Verwendete Begriffe




Abbildung 2: Schnittstellenmodell der Buchhaltung


                                                -5-
                    Ausgangsthematik Buchhaltung - Schnittstellen-Modell

Dieses Modell erlaubt später eine flexible modulare Implementierung verschiedener Ausprä-
gungen des Systems. Einige Methoden stehen unter Umständen nicht im Einklang mit be-
kannten realen Buchhaltungsverfahren (z.B. Buchung löschen vs. Storno-Buchung), sind aber
im Rahmen des Gesamtkonzepts der später angesprochenen Technologien sinnvoll, die hier
im Vordergrund vor einer korrekten Finanzbuchhaltung stehen.
Dies ist auch der Grund weshalb alle im Folgenden angesprochenen Schnittstellen bereits von
Remote abgeleitet wurden. So können die Schnittstellen direkt für RMI (siehe 5.3) und EJB
(siehe 6.3) verwendet werden.


2.2.1. Konto-Schnittstelle

Konto-Klassen implementieren die Konto-Schnittstelle zur Verwaltung der minimal notwen-
digen Daten für ein Buchungs-Konto. Die Schnittstelle liefert und ändert die Daten eines
Kontos (Nummer, Typ, Beschreibung) über Getter- und Setter-Methoden. Lediglich die
Nummer eines Kontos soll einmal festgelegt und nicht nachträglich veränderbar sein.

public interface IAccount extends Remote {
  Set<Integer> getCreditEntries()
      throws IOException;
  Set<Integer> getDebitEntries()
      throws IOException;
  String getDescription()
      throws IOException;
  Integer getNumber()
      throws IOException;
  AccountType getType()
      throws IOException;
  void setDescription(String description)
      throws IOException;
  void setType(AccountType type)
      throws IOException;
}

Code 1: Interface IAccount

Dazu   existieren noch zwei weitere Methoden getCreditAccounts() und
getDebitAccounts(), die die Haben- und Soll-Buchungen, die dem Konto zugeordnet sind,
zurückgeben sollen. Diese beiden Methoden werden später aufgerufen, sobald ein Konto in
der Kontentabelle der Benutzeroberfläche ausgewählt wird. Die Resultate der Methoden wer-
den in Tabellen entsprechend angezeigt.




                                            -6-
                    Ausgangsthematik Buchhaltung - Schnittstellen-Modell



2.2.2. Kontotyp-Enumeration

Für die Buchhaltung werden fünf mögliche Kontotypen in der Enumeration AccountType
festgelegt, die vom Rechnungswesen her bekannt sind.

package de.fhr.vs_iw.model;

public enum AccountType implements Serializable {
  ACTIVE (0, "Aktiv"),
  AMOUNT (1, "Ergebnis"),
  EXPENSE (2, "Aufwand"),
  PASSIVE (3, "Passiv"),
  PROCEEDS(4, "Ertrag");
  public int getId();
  public String toString();
  public static AccountType getAccountTypeById(int id);
  public static List<AccountType> getAccountTypes();
}

Code 2: Enumeration AccountType



2.2.3. Buchungs-Schnittstelle

Die Buchungs-Schnittstelle bietet über Getter- und Setter-Methoden entsprechenden Zugriff
auf die Daten einer Buchung (ID, Haben-Konto, Soll-Konto, Betrag, Text).

package de.fhr.vs_iw.model;

public interface IAccountingEntry extends Remote {
  Double getAmount()
      throws IOException;
  Integer getCreditAccount()
      throws IOException;
  Integer getDebitAccount()
      throws IOException;
  Integer getId()
      throws IOException;
  String getText()
      throws IOException;
  void setAmount(Double amount)
      throws IOException;
  void setCreditAccount(Integer number)
      throws IOException, AccountingException;
  void setDebitAccount(Integer number)
      throws IOException, AccountingException;
  void setText(String text)
      throws IOException;
}

Code 3: Interface IAccountingEntry

Die ID muß dabei von der Implementierung des Buchhaltungssystems für jede Buchung ein-
deutig vergeben werden und darf nicht nachträglich geändert werden, weshalb dafür in der
Schnittstelle auch keine Setter-Methode vorgesehen ist.



                                            -7-
                    Ausgangsthematik Buchhaltung - Schnittstellen-Modell


2.2.4. Service-Schnittstelle

Über die Service-Schnittstelle wird dann auf die Geschäftslogik der Buchhaltung, sowie die
Buchungen und Konten, zugegriffen.

package de.fhr.vs_iw.model;

public interface IAccountingService extends Remote {
  Double calculateBalance(Integer number)
      throws IOException, AccountingException;
  void createAccount(Integer number, AccountType type, String description)
      throws IOException, AccountingException;
  Integer createAccountingEntry(Integer debit, Integer credit,
      Double amount, String text)
      throws IOException, AccountingException;
  IAccount getAccountByNumber(Integer number)
      throws IOException, AccountingException;
  Set<Integer> getAccountingEntries()
      throws IOException;
  IAccountingEntry getAccountingEntryById(Integer id)
      throws IOException, AccountingException;
  Set<Integer> getAccounts()
      throws IOException;
  void removeAccount(Integer number)
      throws IOException, AccountingException;
  void removeAccountingEntry(Integer id)
      throws IOException, AccountingException;
}

Code 4: Interface IAccountingService

Die   wichtigsten Methoden, sind im ersten Ansatz getAccounts() und
getAccountignEntries() um alle vorhandenen Buchungen und Konten zu erhalten. Die
zurückgegebenen Werte (Kontonummer bzw. Buchungs-ID) stellen für die Geschäftslogik der
Buchhaltung Primärschlüssel dar und müssen im jeweiligen System eindeutig sein.
Danach      kann      für    jeden   erhaltenen   Wert    eine   der   Factory-Methoden
getAccountByNumber(Integer number)                       für    die     Konten        bzw.
getAccountingEntryById(Integer id) für die Buchungen aufgerufen werden, um die ent-
sprechenden Objekte zu erhalten.
Die weiteren Methoden zum Anlegen und Löschen von Buchungen vervollständigen das
Buchhaltungssystem, damit es in seiner einfachen Form verwendbar wird. Die Methode
createAccount() erzeugt ein neues Konto, muß aber eine Buchhaltungsausnahme werfen,
wenn bereits ein Konto mit der angegebenen Nummer existiert oder die angegebene Konto-
nummer ungültig (z.B. negativ) ist. Beim Erfassen einer neuen Buchung durch
createAccountingEntry() muß eine Buchhaltungsausnahme geworfen werden, wenn das
angegebene Haben- und Soll-Konto identisch sind oder der angegebene Betrag ungültig ist.
Für alle entsprechenden Methoden, die einen Schlüsselwert (Nummer bzw. ID) als Parameter
erfordern, gilt, daß diese eine Buchhaltungsausnahme werfen müssen, wenn kein Objekt zum
angegebenen Schlüsselwert existiert.




                                            -8-
                    Ausgangsthematik Buchhaltung - Schnittstellen-Modell



2.2.5. Proxy-Schnittstelle

Die Proxy-Schnittstelle dient als Einstiegspunkt, um überhaupt Zugriff auf eine Implementie-
rung eines Buchhaltungssystems zu erlangen. Sie hat nichts mit der Geschäftslogik der Buch-
haltung zu tun, wird aber benötigt, damit die von der Benutzeroberfläche verwendeten Im-
plementierungen des Systems austauschbar sind.

package de.fhr.vs_iw.model;

public interface IAccountingProxy {
  void close()
      throws IOException;
  IAccountingService connect(String[] args)
      throws IOException;
}

Code 5: Interface IAccountingProxy

Durch den Aufruf von connect(String[] args) wird die Instanz einer Service-Klasse gela-
den. Es ist dabei der Implementierung der Proxy-Klasse überlassen, auf welche Weise diese
Instanz der Service-Klasse erzeugt wird und welche Argumente dafür erforderlich sind. Da
eine Proxy-Klasse über Reflection instanziert wird, sollte eine Implementierung der Proxy-
Klasse einen öffentlichen Standard-Konstruktor haben.
Die Schnittstelle ist deshalb auch nicht von Remote abgeleitet, weil sie als Einstiegspunkt auf
jeden Fall lokal vorhanden und direkt aufrufbar sein muß.
Um den Zugriff auf eine Implementierung des Buchhaltungssystems sauber abschließen zu
können gibt es schließlich noch die Methode close(). Sie wird aufgerufen, damit das System
die Möglichkeit hat Daten zu speichern oder offene Verbindungen zu schließen.


2.2.6. Erweiterte Service-Schnittstelle

Bei der Entwicklung der Beispiele hat sich gezeigt, daß einige Funktionalitäten benötigt wer-
den, die über den Umfang der Service-Schnittstelle hinausgehen, z.B. bei der Verwendung
von Entity-Beans (siehe 6.3).

package de.fhr.vs_iw.model;

public interface IExtendedAccountingService extends IAccountingService {
  IAccount accountChange(IAccount account)
      throws AccountingException, IOException;
  IAccountingEntry accountingEntryChange(IAccountingEntry entry)
      throws AccountingException, IOException;
}

Code 6: Interface IExtendedAccountingService

Durch die beiden Methoden der erweiterten Service-Schnittstelle müssen geänderte Konten-
und Buchungsobjekte an das Buchhaltungssystem zurückgegeben werden, damit die geänder-
ten Daten verarbeitet und gespeichert werden können. Eine Implementierung der Methoden
muß die Daten der zurückgegebenen Objekte auf ihre Gültigkeit hinsichtilich der Geschäfts-
logik überprüfen und im Fehlerfall eine Buchhaltungssausnahme werfen (siehe 2.3.1).



                                               -9-
                         Ausgangsthematik Buchhaltung - Ausnahmen


2.3. Ausnahmen
Für mögliche auftretende Fehler werden entsprechende Ausnahmen definiert, die von den
Implementierungen des Systems geworfen werden können bzw. müssen.


2.3.1. Buchhaltungsfehler

Für Buchhaltungsfehler, also für Fehler die auftreten, weil Daten nicht der Geschäftslogik der
Buchhaltung entsprechen, wird die Klasse AccountingException definiert.

package de.fhr.vs_iw.model;

public class AccountingException extends Exception {
  public AccountingException();
  public AccountingException(String message) {}
  public AccountingException(String message, Throwable cause);
  public AccountingException(Throwable cause);
}

Code 7: Class AccountingException

Ein Buchhaltungsfehler sollte in folgenden Situationen auftreten:

   •   Anlegen eines bereits vorhandenen Kontos
   •   Angabe einer negativen Kontonummer
   •   Erfassen einer Buchung mit identischem Haben- und Soll-Konto
   •   Holen eines nicht vorhandenen Kontenobjekts
   •   Holen eines nicht vorhandenen Buchungsobjekts
   •   Berechnen des Saldos eines nicht vorhandenen Kontos
   •   Angabe eines nicht vorhandenen Haben-Kontos bei einer Buchung
   •   Angabe eines nicht vorhandenen Soll-Kontos bei einer Buchung
   •   Löschen eines nicht vorhandenen Kontos
   •   Löschen einer nicht vorhandenen Buchung
   •   Löschen eines Kontos, das noch für Buchungen verwendet wird


2.3.2. Ein/Ausgabe-Fehler

Alle Methoden der Buchhaltungs-Schnittstellen sind in ihrer Signatur mit einer IOException
versehen. Dies geschah im Hinblick auf die direkte Verwendung der Schnittstellen bei RMI,
wo definiert ist, daß alle Methoden, die entfernt aufrufbar sein sollen mit einer
RemoteException versehen sein müssen, welche eine Unterklasse der IOException ist.
Auch im Hinblick auf andere Technologien zur Datenübertragung ist die Methodendefiniton
mit einer IOException sinnvoll. Andere geprüfte Ausnahmen können bzw. müssen unter Zu-
hilfenahme einer IOException geworfen werden.




                                            - 10 -
                         Ausgangsthematik Buchhaltung - Ausnahmen

try {

  ...

} catch (Exception e) {
  IOException ioe = new IOException(e.getMessage());
  ioe.initCause(e);
  throw ioe;
}

Code 8: "Verpacken" einer beliebigen Exception als IOException

Dies mag zwar vielleicht etwas unsauber erscheinen, aber eine IOException unterstützt die
direkte Verkettung von Ausnahmen nicht über einen Konstruktor, was nützlich wäre und auch
schon zur Aufnahme in die Spezifikation der Java-IO-Klassen vorgeschlagen wurde.


2.3.3. Nicht implementierte Methoden

Für den Fall, daß eine in den Schnittstellen definierte Methode nicht implementiert werden
soll, d.h. daß sie nicht mit der Funktionalität, die von der Geschäftslogik vorgegeben wird,
ausgestattet wird, gibt es eine NotImplemented-Ausnahme.

package de.fhr.vs_iw.model;

public class NotImplemented extends RuntimeException {
  public NotImplemented();
}

Code 9: Class NotImplemented (Exception)

Die Ausnahme ist von RuntimeException abgeleitet, damit sie ungeprüft geworfen werden
kann, weil es sich dabei nicht um einen Fehler bezüglich der Geschäftslogik oder der Daten-
übertragung handelt.


2.3.4. Andere Ausnahmen

Weitere Ausnahmen werden nicht spezifiziert. Jedoch muß beim Zugriff auf eine Implemen-
tierung des Buchhaltungssystems damit gerechnet werden, daß weitere Ausnahmen geworfen
werden können.




                                               - 11 -
           Ausgangsthematik Buchhaltung - Modulares Konzept der Programmteile


2.4. Modulares Konzept der Programmteile
Die Idee besteht darin, nicht nur verschiedenartige Implementierungen des Buchhaltungssy-
stems zu erstellen, sondern diese auch modular zu verbinden, damit sich die Eigenschaften der
jeweils verwendeten Technologien ergänzen.




Tabelle 3: Modulares Konzept der Programmteile

Die Module sollen unabhängig voneinander implementiert und verwendet werden können.


2.4.1. Client

Die Client-Seite ruft die Methoden eines Buchhaltungssystems auf, greift auf die Konten und
Buchungen zu und verarbeitet die zurückgelieferten Daten (siehe 3).


2.4.1.1. Proxy-Instanzierung über Reflection

Als Einstiegspunkt wird eine Klasse, die die Proxy-Schnittstelle implementiert, über Reflecti-
on instanziert. Eine solche Klasse sollte deshalb einen öffentlichen Standard-Konstruktor oh-
ne Argumente besitzen. Für diesen Vorgang gibt es in der Utilities-Klasse die statische
Methode loadProxy(String className) bzw. loadProxy(Class clas).

public static IAccountingProxy loadProxy(String className)
    throws NoInterfaceException, ClassNotFoundException,
    InvocationTargetException {
  return loadProxy(Class.forName(className));
}

public static IAccountingProxy loadProxy(Class clas)
    throws NoInterfaceException, InvocationTargetException {
  Object object = null;
  try {
    object = clas.newInstance(new Object[0]);
  } catch (Exception e) {
    throw new InvocationTargetException(e);
  }
  if (!(object instanceof IAccountingProxy)) {
    throw new NoInterfaceException(clas.getName(), INTERFACE_NAME);
  }
  return (IAccountingProxy)object;
}

Code 10: Methoden loadProxy(…) der Utilities-Klasse


                                                 - 12 -
           Ausgangsthematik Buchhaltung - Modulares Konzept der Programmteile

Für diese Methode wird auch die Ausnahme NoInterfaceException eingeführt, die gewor-
fen wird, wenn die angegebene Klasse zwar geladen werden kann, aber die benötigte Proxy-
Schnittstelle nicht implementiert.

package de.fh.vs_iw.util;

public class NoInterfaceException extends Exception {
  public NoInterfaceException();
  public NoInterfaceException(String className, String interfaceName);
}

Code 11: Class NoInterfaceException

Normalerweise sollte die Methode aber ein verwendbares IAccountingProxy-Objekt zur
weiteren Verwendung zurückliefern, sofern nicht absichtlich der Name einer inkompatiblen
Klasse angegeben wird.
Dies stellt die einfachste Möglichkeit dar, um benannte Klassen zur Laufzeit nachzuladen.
Die Benutzeroberfläche verwendet darüber hinaus weitere Mechanismen, um ein flexibles
Plugin-Konzept zu realisieren (siehe 3.2 und 3.8)


2.4.1.2. Laden einer Service-Klasse

Von dem instanzierten Proxy-Objekt wird dann die connect()-Methode aufgerufen, die mo-
dulspezifische Argumente in einem String[]-Array übernimmt. Dieses Objekt ist nun dafür
zuständig eine Instanz einer IAccountingService-Klasse zu erzeugen und zurückzugeben.
Auf welche Weise diese Instanz erstellt wird ist jeweils der konkreten Implementierung einer
Proxy-Klasse überlassen. Dies ist auch der Grund, warum die Proxy-Schnittstelle spezifiziert
wurde, damit immer ein einheitlicher lokaler Einstiegspunkt für jede Implementierung vor-
handen ist, unabhängig von der konkreten Funktion eines Moduls


2.4.2. Datenhaltung / Geschäftslogik

Bei der ersten Gruppe der vorgestellten Technologien geht es um die Implementierung einer
Datenhaltungsschicht zur konsistenten Verwaltung und persistenten Speicherung der Daten
des Buchhaltungssystems. Diese Module stellen die Grundlage dar, um später damit verteilte
Systeme zu erstellen (siehe 4).


2.4.2.1. Daten und Persistenz

Der zentrale Punkt der Datenhaltungsschicht ist die Speicherung der Daten des Systems in
Java-Objekten, die Verwaltung dieser Objekte und der Zugriff auf die Daten über die Konto-
(siehe 2.2.1) bzw. Buchungsschnittstelle (siehe 2.2.3). Hinzu kommt die Anforderung der
Persistenz, der dauerhaften Speicherung der Daten auch über das Anwendungsende hinaus
und das Laden vorhandener Daten beim Start der Anwendung.




                                           - 13 -
          Ausgangsthematik Buchhaltung - Modulares Konzept der Programmteile


2.4.2.2. Geschäftslogik

Damit die Konsistenz und Integrität der gespeicherten Daten sichergestellt werden kann, muß
vor deren Speicherung die Gültigkeit bezüglich der vorgegebenen Geschäftslogik (siehe 2.1.1
und 2.3.1) überprüft werden. Dazu kann ein Modul der Datenhaltung die Geschäftslogik di-
rekt implementieren, so daß sich ingesamt ein System mit drei Schichten ergibt (3-Tier-
Architecture). Die Geschäftslogik könnte aber auch in einem separaten Modul ausgelagert
werden, das der Datenhaltungsschicht als zusätzliche Schicht vorgeschoben wird (4-Tier-
Architecture) und über eine weitere definierte Schnittstelle von mehreren Datenhaltungsmo-
dulen gemeinsam verwendet werden könnte.


2.4.3. Transport / Middleware

Die weiteren angesprochenen Technologien bauen auf der Datenhaltungsschicht auf und die-
nen dann der Datenübertragung. Sie stellen den Kernpunkt der Thematik „Verteilte Systeme“
dar (siehe 5) und werden als austauschbare Schicht zwischen der Benutzeroberfläche und der
Datenhaltung eingebunden. Ein Modul der Transportschicht besteht immer aus zwei Teilen,
die untereinander eine durch ein Protokoll definierte Kommunikation aufbauen und die Daten
des Systems übertragen.


2.4.3.1. Client

Der Client-Teil eines Transport-Moduls stellt eine Implementierung der Proxy-Schnittstelle
zur Verfügung, die den Zugriff duch die Benutzeroberfläche gestattet, und gibt alle Daten
unverändert an den zugehörigen Server weiter. Die Antworten des Servers werden wiederum
direkt an die Benutzeroberfläche zurückgegeben.


2.4.3.2. Server

Der Server-Teil läuft als eigenständiger Prozess und wartet auf Verbindungen von den
Clients. Er übernimmt dann die Daten von den Client zur weiteren Verarbeitung und schickt
die Antworten zurück. Geschäftslogik und Datenhaltung könnten dierekt im Server imple-
mentiert werden, was aber der gewünschten Modularität des Systems widersprechen würde.
Ein Server sollte so ausgelegt sein, daß er mit mehreren Clients parallel umgehen kann.




                                          - 14 -
          Ausgangsthematik Buchhaltung - Modulares Konzept der Programmteile



2.4.4. Verkettung

Über die jeweiligen Proxy-Klassen werden die verschiedenen Module bzw. Implementierun-
gen untereinander verkettet. So kann die Benutzeroberfläche oder ein anderer Client im ersten
Ansatz direkt auf die Datenhaltung zugreifen, oder aber der Client benutzt eine Implementie-
rung der Transportschicht, um sich mit einem entfernten System zu verbinden, das wiederum
auf die Datenhaltung zugreift. Der Server-Teil eines Transport-Moduls wird dabei seinerseits
wieder zum Client des nächsten Moduls der Kette.




Abbildung 3: Vekettung der Module des Buchhaltungssystems




                                             - 15 -
           Ausgangsthematik Buchhaltung - Modulares Konzept der Programmteile

Durch die einheitliche Proxy-Schnittstelle können auch mehrere Module der Transportschicht
hintereinandergeschaltet werden, und es können sich verschiedene Szenarien ergeben, wie die
Module des System miteinander interagieren und letztendlich das verteilte Buchhaltungssy-
stem realisieren.




Abbildung 4: Mögliche Verkettungsszenarien




                                             - 16 -
                                Benutzeroberfläche - Übersicht


3. Benutzeroberfläche




3.1. Übersicht
Der Ausgangspunkt für das Buchhaltungssystem ist die Swing-Benutzeroberfläche, welche
die Methoden des Schnittstellen-Modells aufruft und so die Buchhaltungsdaten anzeigt.




Abbildung 5: Hauptansicht der Buchhaltungsoberfläche

Das Buchhaltungssystem kann damit vollständig angesprochen werden. Somit dient die Be-
nutzeroberfläche als Client für alle konkreten Implementierungen des Systems mit verschie-
denen Technologien. Die Oberfläche nicht unbedingt auf eine praktische Nutzung zur Buch-
haltung ausgelegt, sondern eher technisch auf den Aufruf der einzelnen Methoden der Schnitt-
stellen des Buchhaltungsmodell, und die direkte und detaillierte Verarbeitung und Anzeige
der Rückgabewerte bzw. Ausnahmen.

                                              - 17 -
                      Benutzeroberfläche - Zugriff über eine Proxy-Klasse


3.2. Zugriff über eine Proxy-Klasse
Gestartet wird das System über die Client-Klasse, die das Hauptfenster der Benutzeroberflä-
che anzeigt:

   java de.fhr.vs_iw.Client [[proxyclass] arguments ...]

oder aus dem Buchhaltungs-Package (siehe 3.9.2):

   java -jar vs_iw_fibu_main_2.0.jar [[proxyclass] arguments ...]

Auf der Kommandozeile kann der Name einer Proxy-Klasse und weitere Argumente zum
Laden der Service-Klasse angegeben werden. Das Buchhaltungssystem wird durch die ange-
gebene Proxy-Klasse sofort geladen, und die angegebenen Argumente werden als String-
Array unverändert an die connect()-Methode übergeben.
Bei diesem direkten Aufruf wird der Java-System-Klassenlader verwendet, und darum muß
sich die angegebene Proxy-Klasse im aktuellen CLASSPATH befinden.
Besser ist es jedoch, die Buchhaltungsanwendung nicht direkt von der Kommandozeile, son-
dern zusammen mit der Eclipse-Entwicklungsumgebung zu verwenden (siehe 3.9).


3.2.1. Laden und Verbinden eines Buchhaltungssystems

Wenn auf der Kommandozeile keine Proxy-Klasse angegeben wurde, kann das Buchhaltungs-
system im Menü unter Schnittstelle->Service-Klasse laden... geladen werden.




Abbildung 6: Schnittstellen-Menü

Dazu sucht die Benutzeroberfläche ausgehend vom aktuellen Anwendungs-Verzeichnis
(System.getProperty("user.dir")) in allen Unterverzeichnissen nach class-Dateien, und
auch in jar- und zip-Archiven nach Klassen, die die Proxy-Schnittstelle implementieren (siehe
auch 3.8). Das Suchverzeichnis kann über die Schaltfläche "Verzeichnis..." auch manuell ein-
gestellt werden.
Jede gefundene Klasse, die als Proxy-Klasse in Frage kommt, wird direkt aus dem Dateisy-
stem geladen und in der aktuellen VM verfügbar gemacht. Dabei kommt ein eigener Klassen-
lader (de.fh.vs_iw.util.ClassFileLoader) zum Einsatz, der auch unabhängig vom einge-
stellten CLASSPATH, zusammen mit den Informationen über die beim Suchvorgang gefunde-
nen Klassen, eine Klasse mit allen referenzierten Klassen laden kann (siehe 3.8).




                                            - 18 -
                      Benutzeroberfläche - Zugriff über eine Proxy-Klasse




Abbildung 7: Service-Klasse laden

Die gefundenen Proxy-Klassen werden aufgelistet, und in der Statuszeile wird eine Statistik
über den Suchvorgang angezeigt. Wie auch auf der Kommandozeile können Argumente zum
Laden eingegeben werden. Durch die Schaltfläche "Datei-Argument…" kann eine Datei als
Argument ausgewählt und angegeben werden.
Wenn die ausgewählte Klasse einen Hinweis auf ihre benötigten Argumente enthält, wird
dieser in der Statuszeile des Fenters und als Tooltip in der Klassenliste angezeigt.




Abbildung 8: Hinweisanzeige bei der Auswahl der Proxy-Klasse

Dazu wird über Reflection, die Klassenvariable ARGS ausgelesen, sofern diese vorhanden ist,
und der darin enthaltene String angezeigt.

package de.fhr.vs_iw.jaxb.v1;

public final class JAXBProxy1 implements IAccountingProxy {

    ...

    private static final String ARGS = "Datei [Schema]";
}

Code 12: Klassenvariable ARGS

                                              - 19 -
                    Benutzeroberfläche - Zugriff über eine Proxy-Klasse

Mit "Ok" wird anschließend die ausgewählte Proxy-Klasse geladen und instanziert. Von der
Instanz der Proxy-Klasse wird dann die Methode connect(String[] args) aufgerufen, die
eine Instanz der Service-Klasse zurückliefern muß.
Hier werden die angegebenen Argumente übergeben, damit die Service-Klasse entsprechend
geladen werden kann (Socket-Verbindung aufbauen, RMI-Remote-Objekt holen, usw.). Wel-
che Argumente angegeben werden müssen ist dabei der Implementierung der Proxy-Klasse
überlassen, sie werden von der Benutzeroberfläche lediglich in ein String-Array umgewandelt
und dann direkt weitergegeben.
Der Name der aktuell geladenen Proxy-Klasse wird im Titelbalken des Hauptfensters ange-
zeigt.


3.2.2. Schließen der Verbindung

Wenn die Anwendung beendet wird, oder aus dem Menü "Schnittstelle" explizit der Punkt
"Schnittstelle schließen" gewählt wird, so wird die Methode close() der Proxy-Schnittstelle
aufgerufen. Auch wenn direkt eine neue Proxy-Klasse geladen werden soll, so wird vorher
von der aktuellen Proxy-Instanz die close()-Methode aufgerufen und deren korrekte Aus-
führung abgewartet.
Durch die Methode wird der Zugriff auf eine Implementierung des Buchhaltungssystems be-
endet, und es können noch Daten gespeichert oder offene Netzwerkverbindungen geschlossen
werden. Die genaue Funktionalität der Methode bleibt der konkreten Implementierung über-
lassen.




                                          - 20 -
                           Benutzeroberfläche - Aufbau der Oberfläche


3.3. Aufbau der Oberfläche
3.3.1. Kontenliste




Abbildung 9: Kontenliste

Die Kontenliste gibt die einzelnen Daten (Nummer, Typ, Beschreibung) aller in einem Buch-
haltungssystem vorhandenen Konten als Tabelle aus. In der Liste kann ein Konto zur Bearbei-
tung ausgewählt werden.


3.3.1.1. Laden der Kontenobjekte

Zum Aufbau der Kontenliste werden alle vorhandenen Kontonummern über die Methode
getAccounts() ermittelt und für jede Nummer mit getAccountByNumber() das zugehörige
Kontenobjekt geholt. Diese werden dann der Tabelle hinzugefügt. Von jedem Kontenobjekt
werden die Methoden getNumber(), getType() und getDescription() zur Anzeige der
Kontendaten aufgerufen.


3.3.1.2. Anlegen eines neuen Kontos

Über die entsprechende Schaltfläche unterhalb der Kontenliste wird ein Dialogfenster zum
Anlegen eines neuen Kontos geöffnet. Dort werden die nötigen Daten eingegeben, wobei das
Textfeldes für die Kontonummer auf einen numerischen Inhalt hin überprüft wird.




Abbildung 10: Neues Konto anlegen




                                             - 21 -
                         Benutzeroberfläche - Aufbau der Oberfläche

Mit den eingegeben Daten wird dann direkt die Methode createAccount() aufgerufen, um
das neue Konto im System anzulegen.


3.3.1.3. Ändern der Daten eines Kontos

Das gleiche Dialogfenster wie zum Anlegen eines Kontos wird auch zum Ändern der Daten
eines Kontos verwendet, nur daß die Kontonummer unveränderbar ist.




Abbildung 11: Konto bearbeiten

Mit den neuen Daten werden die Methoden setType() und setDescription() des ausge-
wählten Kontenobjekts aufgerufen. Wenn die geladene Instanz einer Service-Klasse die er-
weiterte Service-Schnittstelle implementiert wird mit dem geänderten Kontenobjekt die Me-
thode changeAccount() aufgerufen.


3.3.1.4. Löschen eines Kontos

zum Löschen eines Kontos wird vorher in einem Dialogfenster noch die Bestätigung des Be-
nutzers eingeholt.




Abbildung 12: Konto löschen

Bei einer positiven Bestätigung wird die Methode removeAccount() des Buchhaltungssy-
stems mit der Nummer des ausgewählten Kontos aufgerufen und das Konto entfernt.




                                           - 22 -
                           Benutzeroberfläche - Aufbau der Oberfläche



3.3.2. Buchungsliste




Abbildung 13: Buchungsliste

Analog zur Kontenliste werden in der Buchungsliste die Daten (ID, Haben-Konto, Soll-
Konto, Betrag, Text) aller vorhandenen Buchungen tabellarisch angezeigt. Auch hier kann
eine Buchung ausgewählt und bearbeitet werden.


3.3.2.1. Laden der Buchungsobjekte

Zuerst    werden      IDs aller vorhandenen Buchungen über die Methode
                     die
getAccountingEntries() ermittelt und anschließend für jede einzelne der IDs durch
getAccountingEntryById() das zugehörige Buchungsobjekt geholt. Für die Anzeige der
Daten werden die Methoden getID(), getDebitAccount(), getCreditAccount(),
getAmount() und getText() von jedem der Kontenobjekte aufgerufen.


3.3.2.2. Erfassen einer neuen Buchung

Mit der zugehörigen Schaltfläche über der Buchungsliste kann eine neue Buchung erfaßt wer-
den. In dem dafür vorgesehenen Dialogfenster wird das Soll- und Haben-Konto der neuen
Buchung aus einer Liste der vorhandenen Konten ausgewählt und dazu ein Betrag und eine
Beschreibung eingegeben. Der eingegebene Wert für den Betrag der Buchung wird auf seine
numerische Gültigkeit geprüft.




Abbildung 14: Neue Buchung erfassen




                                             - 23 -
                         Benutzeroberfläche - Aufbau der Oberfläche

Die eingegebenen Daten werden direkt dem Aufruf der Methode createAccountingEntry()
zugeführt, womit die neue Buchung vom System erfaßt wird. Die ID einer Buchung wird al-
lerdings nicht vom Benutzer angegeben, sondern von der Methode zurückgeliefert.


3.3.2.3. Ändern der Daten einer Buchung

Auch die Daten einer Buchung können nach Auswahl der Buchung und der Schaltfläche "Bu-
chung bearbeiten" in dem Dialogfenster geändert.




Abbildung 15: Buchung bearbeiten

Für   das  ausgewählte Kontenobjekt werden die Methoden setDebitAccount(),
setCreditAccount(), getAmount() und getText() entsprechend mit den neuen Daten auf-
gerufen. Wenn die geladene Instanz einer Service-Klasse die erweiterte Service-Schnittstelle
implementiert   wird     mit    dem     geänderten     Buchungsobjekt      die    Methode
changeAccountingEntry () aufgerufen.


3.3.2.4. Löschen einer Buchung

Auch vor dem Löschen einer ausgewählten Buchung über die Schaltfläche wird eine Bestäti-
gung eingeholt.




Abbildung 16: Buchung löschen

Zum Löschen wird dann die Methode removeAccountingEntry() des Systems aufgerufen
und die Buchung entfernt.




                                           - 24 -
                          Benutzeroberfläche - Aufbau der Oberfläche



3.3.3. Soll- und Haben-Buchungen je Konto

Sobald man in der Kontenliste ein Konto auswählt, werden die zugehörigen Soll- und Haben-
Buchungen für dieses Konto geladen und in Tabellen angezeigt.




Abbildung 17: Liste der Haben- und Soll-Buchungen eines Kontos

Der Aufbau erfolgt ähnlich wie bei der Haupt-Buchungsliste. Über die Methoden
getDebitAccounts() und getCreditAccounts() des ausgewählten Kontos werden die ent-
sprechenden IDs der Buchungen ermittelt, anschließend wieder die Buchungsobjekte geholt
und die Daten von jedem einzelnen Buchungsobjekt geladen.

Wenn in einer dieser beiden Buchungslisten eine Buchung ausgewählt wird, so wird dieselbe
Buchung auch in der Haupt-Buchungsliste ausgewählt, und auch umgekehrt, damit die Ver-
wendung der Schaltflächen für Buchungen konsistent und logisch ist.


3.3.4. Saldo eines Kontos

Der Saldo eines Kontos wird bei Auswahl in der Kontenliste ebenfalls ermittelt. Dazu wird
die Methode calculateBalance() des Buchhaltungssystems mit der Nummer des Kontos
aufgerufen. Der Saldo wird unterhalb der Kontenliste angezeigt.



Abbildung 18: Saldo eines Kontos




                                              - 25 -
                        Benutzeroberfläche - Behandlung von Ausnahmen


3.4. Behandlung von Ausnahmen
Im Allgemeinen werden von der Benutzeroberfläche alle Ausnahmen, auch Laufzeitfehler
(abgeleitet von RuntimeException) und kritische Fehler (abgeleitet von Error), gefangen
und angezeigt, und zum Teil auch speziell ausgewertet.


3.4.1. Nachrichtenfenster




Abbildung 19: Nachrichtenfenster bei Ausnahmen

Wenn dann bei den Aktionen und Methodenaufrufen Ausnahmen auftreten, werden diese
grundsätzlich in einem Nachrichtenfenster angezeigt. Wie auf der Konsole wird im Textbe-
reich des Fensters der Stacktrace der Ausnahme ausgegeben.

StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
exception.printStackTrace(pw);
pw.close();
textArea.setText(sw.toString());

Code 13: Ausgabe des Stacktrace mit Hilfe von StringWriter und PrintWriter



3.4.2. Statusleiste

Auf der Statusleiste werden kurze Informationen über alle Vorgänge, welche die Oberfläche
ausführt, und deren Ergebnis angezeigt. Bei eher sekundären, rein informativen Meldungen
bleibt die Farbe der Leiste grau.



Abbildung 20: Statusleiste grau


                                               - 26 -
                        Benutzeroberfläche - Behandlung von Ausnahmen

Bei Aufrufen von Methoden des Buchhaltungssystems und deren erfolgreicher Beendigung
wird die Leiste grün hinterlegt.



Abbildung 21: Statusleiste grün

Falls eine der Buchhaltungsmethoden nicht implementiert wurde und die entsprechende Aus-
nahme wirft, wird die Statusleiste gelb.



Abbildung 22: Statusleiste gelb

Bei einem fehlerhaften Methodenaufruf wird die Leiste rot hinterlegt.



Abbildung 23: Statusleiste rot

Außerdem wird am linken Ende der Statusleiste angezeigt in einem Fortschrittsbalken ange-
zeigt, ob gerade asynchrone Vorgänge im Hintergrund laufen, sowie deren Anzahl.


3.4.3. Direkte Einblendung

Wenn eine Methode bei einer konkreten Implementierung des Buchhaltungssystems gezielt
nicht implementiert wurde, und dementsprechend eine NotImplemented-Ausnahme wirft, so
wird diese speziell behandelt und in dem Anzeigebereich, der dem Ergebnis der Methode
normal zugeordnet ist, eingeblendet.




Abbildung 24: Einblendung bei nicht implementierten Methoden (einzelne Werte)




Abbildung 25: Einblendung bei nicht implementierten Methoden (ganze Auflistung)

Dies dient dazu, daß der Programmablauf nicht durch ein Nachrichtenfenster unterbrochen
wird, weil eine solche Ausnahme eben gezielt geworfen wird, und keinen Fehler an sich dar-
stellt.


                                               - 27 -
                       Benutzeroberfläche - Kapselung aller Aufrufe


3.5. Kapselung aller Aufrufe
Die Methoden eines Buchhaltungssystems werden durch Aktionen des Benutzers auf der
Oberfläche nicht direkt aufgerufen, um die korrekte Funktion und Verwendbarkeit der Ober-
fläche nicht von der konkreten (unter Umständen unbekannten) Implementierung der Buch-
haltungsmethoden und dabei möglicherweise auftretenden Fehlern abhängig zu machen.


3.5.1. Adapterschicht

Deshalb wird zwischen Benutzeroberfläche und Buchhaltungsimplementierung eine Adapter-
schicht (Package de.fhr.vs_iw.adapter) eingezogen. Wie das Buchhaltungsmodell besteht
die Schicht aus drei Kern-Komponenten jeweils für die Schnittstellen/Objekte des Modells:

   •   IAccountingService →       ServiceAdapter
   •   IAccount           →       AccountAdapter
   •   IAccountingEntry   →       AccountingEntryAdapter

Jede Adapter-Instanz kapselt genau ein Objekt der Buchhaltung, und die Adapter-Objekte,
sowie die Original-Objekte, werden entsprechend in HashMaps verwaltet, damit immer die
richtige Zuordnung besteht. Die Klassen der Adapterschicht sind generisch aufgebaut und
dann an die Erfordernisse der jeweiligen Objekte (Konten und Buchungen) angepaßt. Außer-
dem sind hier zusätzliche Vorgangsklassen vorhanden, um den Zugriff auf die Objekte der
Buchhaltung in separaten Threads ausführen zu können (siehe 3.6).


3.5.1.1. Swing-MVC-Pattern

Die Swing-Komponenten zur Anzeige von Daten in einer Benutzeroberfläche sind nach dem
Model-View-Controller-Prinzip (MVC) aufgebaut. Für jede der vier vorhandenen Tabellen
der Klasse JTable wird ein eigenes Tabellen-Modell verwendet, das von der Swing-Klasse
AbstractTableModel abgeleitet ist. Zusammen mit der generischen Schnittstelle
RowTableModel ergibt sich die Klasse AbstractRowTableModel zur zeilenweisen Verwal-
tung von Objekten eines beliebigen Typs T.

package de.fhr.vs_iw.util;

public abstract class AbstractRowTableModel<T>
    extends AbstractTableModel implements RowTableModel<T> {
  public final void addDistinctRows(List<T> rows);
  public final void addRow(T row);
  public final void addRows(List<T> rows);
  public final boolean contains(T row);
  public final Class<?> getColumnClass(int column);
  public abstract int getColumnCount();
  public final T getRow(int row);
  public final int getRowCount();
  public final int getRowIndex(T object);
  public abstract String getToolTipAt(int row, int column, boolean ellip);
  public abstract Object getValueAt(int row, int column);
  public final void removeAllRows();
}

Code 14: Class AbstractRowTableModel<T>

                                          - 28 -
                        Benutzeroberfläche - Kapselung aller Aufrufe



Damit werden die Adapter-Objekte direkt den Tabellen-Modellen als Zeilen hinzugefügt und
verwaltet. Die konkreten Modell-Klassen sind dann von dieser abstrakten Klasse abgeleitet
und geben die Spaltendaten entsprechend der zugehörigen Tabelle aus.

package de.fhr.vs_iw.gui;

public final class AccountTableModel
    extends AbstractRowTableModel<AccountAdapter> {
  public int getColumnCount();
  public String getColumnName(int column);
  public String getToolTipAt(int row, int column, boolean ellip);
  public Object getValueAt(int row, int column);
}

Code 15: Class AccountTableModel

Da die Basis-Klasse AbstractRowTableModel entsprechend generisch ist, kann sie für Kon-
ten (AccountAdapter) und für Buchungen (AccountingEntryAdapter) gleichermaßen ver-
wendet werden kann.

package de.fhr.vs_iw.gui;

public final class AccountingEntryTableModel
    extends AbstractRowTableModel<AccountingEntryAdapter> {
  public int getColumnCount();
  public String getColumnName(int column);
  public String getToolTipAt(int row, int column, boolean ellip);
  public Object getValueAt(int row, int column);
}

Code 16: Class AccountingEntryTableModel

Die beiden weiteren Tabellen-Modelle für die Soll- und Haben-Buchungen eines Kontos ver-
halten sich analog zum diesem Buchungs-Tabellen-Modell.


3.5.1.2. Zwischenspeicherung der Daten

Weil bei jeder Aktualisierung der Anzeige ein Zugriff auf die Daten des Tabellen-Modells,
und damit auch der Adapterschicht, erfolgt, werden die Daten der Buchungen und Konten in
der Adapterschicht in Holder-Klassen zwischengespeichert. Sonst müßte bei jeder Aktualisie-
rung der Anzeige auf das Buchhaltungssystems zugegriffen werden, was ineffizient und unnö-
tig ist. Die Klassen AccountAdapter und AccountingEntryAdapter beinhalten ein Status-
flag für das gekapselte Objekt, mit dem der Stand der zwischengespeicherten Daten beschrie-
ben wird. So können die Daten bei Bedarf und bei Änderungen erneut aus dem gekapselten
Objekt geladen werden.


3.5.2. Aufteilung in separate Vorgänge

Aber auch über die Adapterschicht erfolgt nicht einfach ein Aufruf der Methoden des Buch-
haltungssystems. Jeder Methodenaufruf wird in einer eigenen Vorgangsklasse gekapselt, und
dann entsprechend einer dieser Vorgänge angestoßen. Der Grund für diese Aufteilung und
den Aufbau der Vorgänge wird später unter Punkt 3.6 näher erläutert.

                                           - 29 -
                         Benutzeroberfläche - Kapselung aller Aufrufe


3.5.2.1. Vorgänge zum Zugriff auf die Buchhaltung

Jede      derdrei    Adapter-Klassen     (ServiceAdatper,     AccountingEntryAdapter,
AccountAdapter) ist an entsprechende Vorgänge für den Zugriff auf die zugehörigen Metho-
den der Buchhaltungsschnittstellen gebunden. Diese Vorgangsklassen sind von der gemein-
samen Basis-Klasse AbstractServiceTask abgeleitet.

package de.fhr.vs_iw.threading;

public abstract class AbstractServiceTask implements RunnableServiceTask {
  public final String getDuration();
  public final String getName();
  public final boolean isCanceled();
  public final void run();
  public final void start(final RunnableSwingTask n);
  public final void throwNow();
  protected abstract void task();
}

Code 17: Class RunnableServiceTask

Die Klasse implementiert die Schnittstelle RunnableServiceTask, und definitert die abstrak-
te Methode task(), die von einer Unterklasse implementiert werden muß, um separat ausge-
führt werden zu können. Ein Vorgang wird mit der Methode start() gestartet. Die run()-
Methode der Klasse ruft dann die task()-Methode der Unterklasse auf. Da die run()-
Methode der Schnittstelle Runnable keine Ausnahmen werfen darf, werden Ausnahmen ge-
fangen und gespeichert, und können durch den Aufruf von throwNow() zu einem beliebigen
Zeitpunkt erneut geworfen werden. Die generischen Vorgänge der Adapterschicht werden von
der Klasse abgeleitet.

package de.fhr.vs_iw.adapter;

final class MethodCallTask<P, R> extends AbstractServiceTask {

    ...

    protected void task() throws Throwable {
      try {
        Logger.entering();

          if (isCanceled()) {
            return;
          }

          Logger.calling(getName(), param);
          result = caller.callMethod(method, param, this);
          if (result == null) {
            Logger.returnedNull(getName());
          }

        } finally {
          Logger.leaving();
        }
    }
}

Code 18: Class MethodCallTask (Adapterschicht)

                                                 - 30 -
                         Benutzeroberfläche - Kapselung aller Aufrufe

Die run()-Methode läuft entsprechend dem angegebenen Verhalten ab und ruft die Methode
calculateBalance() auf. Das Ergebnis der Methode (Rückgabewert oder Ausnahme) wird
in der Adapterschicht zwischengespeichert. Außerdem enthalten die implementierten Vor-
gangsklassen weitere Methoden um die spezifischen Parameter für den jeweiligen Aufruf des
Buchhaltungssystems anzugeben, wie hier die Methode setNumber().


3.5.2.2. Vorgänge zur Aktualisierung der Benutzeroberfläche

Bei       Aufruf   der      Methode    start(RunnableSwingTask task)         der    Klasse
AbstraktServiceTask wird ein weiterer Vorgang angegeben, der nach dem Ende des Ser-
vice-Vorgangs gestartet wird. Dies ist dann ein Vorgang zur Aktualisierung der Anzeige der
Daten in der Benutzeroberfläche, der von der Basis -Klasse AbstractSwingTask abgeleitet
ist und die Schnittstelle RunnableSwingTask implementiert..

package de.fhr.vs_iw.threading;

public abstract class AbstractSwingTask implements RunnableSwingTask {
  public final void addTaskListener(SwingTaskListener listener) {
  public final void cancel();
  public final String getName();
  public final boolean isCanceled();
  public final boolean isStarted();
  public final void notifyServiceTaskRunning();
  public final void removeTaskListener(SwingTaskListener listener);
  public final void run();
  public final void setThread(Thread thread);
  public final void start();
  protected final void setName(String name);
  protected abstract void task();
}

Code 19: Class AbstractSwingTask

Auch hier muß von einer Unterklasse die task()-Methode implementiert werden, die dann
durch den Vorgang aufgerufen wird.

private final class Caller<T> extends AbstractSwingTask {
  protected void task() throws Throwable {
    try {
      Logger.entering();

         if (serviceAdapter == null) {
           Logger.println(STR_SERVICE_NULL);
           return;
         }

         T result = serviceAdapter.resultMethod(method);

          afterMethodSuccess(method, result);
        } finally {
          afterMethodAlways(method);
          Logger.leaving();
        }
    }
}

Code 20: Class Caller (GUI-Vorgang)

                                            - 31 -
                         Benutzeroberfläche - Kapselung aller Aufrufe

Ein solcher Vorgang wird als Nachfolgevorgang eines Service-Vorgangs gestartet. Er holt das
zwischengespeicherte Ergebnis des Methodenaufrufs aus der Adapterschicht und zeigt es in
der Benutzeroberfläche an.


3.5.3. Kontrolle der Vorgänge

Die Vorgänge sollen zwar grundsätzlich eigenständig ablaufen (siehe 3.6.2), aber dennoch
sollte die Oberfläche den Status der Vorgänge anzeigen, und einen Vorgang gegebenenfalls
abbrechen können.


3.5.3.1. Status-Benachrichtigung über Listener-Interface

Deshalb kann jeder GUI-Vorgang entsprechende Empfänger (die Benutzerpberfläche) über
seinen Status informieren. Der Empfänger muß dafür die SwingTaskListener-Schnittstelle
implementieren.

package de.fhr.vs_iw.threading;

public   interface SwingTaskListener {
  void   notifyError(RunnableSwingTask task, Throwable e);
  void   notifyFinish(RunnableSwingTask task);
  void   notifyServiceTaskRunning(RunnableSwingTask task);
  void   notifyStart(RunnableSwingTask task);
}

Code 21: Interface SwingTaskListener

Ein Empfänger, der die Schnittstelle implementiert, wird dann bei einem zugehörigen Vor-
gang über die Methode addTaskListener() registriert, und der Vorgang ruft die Methoden
der Schnittstelle am entsprechenden Punkt seines Ablaufs auf:

   •   notifyStart()     wenn der Vorgang durch seine start()-Methode gestartet wird
   •   notifyServiceTaskRunning()      wenn der zugehörige Service-Vorgang abläuft
   •   notifyFinish()    am Ende der run()-Methode
   •   notifyError()     bei einer Ausnahme durch den Methodenaufruf


3.5.3.2. Auflistung der laufenden Vorgänge

Die Klasse des Hauptfensters der Benutzeroberfläche (de.fhr.vs_iw.gui.MainWindow) im-
plementiert die SwingTaskListener-Schnittstelle und registriert sich als Empfänger bei den
Vorgängen, welche die Benutzeroberfläche aktualisieren. Durch die daraus resultierenden
Informationen über den Beginn und das Ende der einzelnen Vorgänge wird eine Liste der
jeweils laufenden Vorgänge vom Typ RunnableSwingTask, die einem Service-Vorgang zu-
geordnet sind, erstellt.




                                            - 32 -
                         Benutzeroberfläche - Kapselung aller Aufrufe




Abbildung 26: Liste der laufenden Vorgänge

Die Vorgänge werden entsprechend ihrem Ablauf der Liste hinzugefügt oder entfernt. Wenn
der zugeordnete Service-Vorgang gerade abläuft, wird die Anzeige gelb hinterlegt. Alle ande-
ren aufgelisteten Vorgänge befinden sich in der Warteschlange.
Die Anzahl laufender Vorgänge in der Statusleiste stimmt nicht unbedingt mit der Anzahl
Vorgänge in der Liste überein, da es zusätzliche Vorgänge gibt, die nicht auf die Buchhal-
tungsschnittstellen bzw. die Adapterschicht zugreifen, sondern nur mit den Komponenten der
Oberfläche arbeiten, aber auch durch das Threading-System ausgeführt werden.




Abbildung 27: Ansicht-Menü

Über das Ansicht-Menü kann eingestellt werden, wie die Liste der laufenden Vorgänge ange-
zeigt werden soll:

   •   Vorgangsliste automatisch:      Das Fenster wird ein- und ausgeblendet, sobald Vorgän-
                                       ge laufen und länger als eine Sekunde aktiv bzw. inaktiv
                                       sind.
   •   Vorgangsliste anzeigen.         Das Fenster wird immer angezeigt.
   •   Vorgangsliste ausblenden:       Das Fenster wird niemals angezeigt.



3.5.3.3. Abbruch eines Vorgangs

Neben der Bezeichnung des Vorgangs wird eine Schaltfläche angezeigt, mit dem der Benutzer
den jeweiligen Vorgang abbrechen kann. Dazu wird die Methode cancel() der Klasse
RunnableSwingTask aufgerufen (siehe 3.6.3).

                                             - 33 -
                                  Benutzeroberfläche - Threading


3.6. Threading

Diese Art der Trennung in eigenständige Vorgänge, die alle die Schnittstelle Runnable und
eine run()-Methode implementieren, dient schließlich dazu, daß die Vorgänge vollständig
entkoppelt und gezielt von separaten Threads ausgeführt werden können.


3.6.1. Zentraler Threading-Service

Jeder Vorgang wird über eine Methode (execute(), run(), swing()) der Klasse
ThreadingService ausgeführt.

package de.fhr.vs_iw.threading;

public final class ThreadingService {
  public static enum Mode {
    ASYNCHRONOUS, MULTITHREADED, SINGLETHREADED, SYNCHRONOUS
  }
  public static void execute(RunnableServiceTask task);
  public static void execute(RunnableSwingTask task);
  public static Mode getMode();
  public static void run(Runnable run)
  public static void setMode(Mode mode);
  public static void shutdown();
  public static void swing(Runnable task);
}

Code 22: Class ThreadingService

Die Implementierung der Klasse ThreadingService sorgt dafür, daß jeder Vorgang passend
ausgeführt wird (siehe auch 3.6.2). Die im Menü einstellbare Option zur Log-Ausgabe von
Debug-Informationen der Threads wird hier verwaltet und angewandt.


3.6.1.1. AWT-Event-Dispatching-Thread

Um das Design der Swing-Implementierung einfach zu halten sind die Swing-Komponenten
nicht für den parallelen Zugriff durch mehrere Threads ausgelegt. Deshalb gibt es die Sin-
gle-Thread-Regel [29]:

   "Once a Swing component has been realized, all code that might affect or depend
   on the state of that component should be executed in the event-dispatching thread."

Dieser eine Thread ist normalerweise der AWT-Event-Dispatch-Thread, und Anwendungen
mit einer Benutzeroberfläche, die trotzdem mehrere Threads benötigen, sollten dafür sorgen,
daß Zugriffe auf Elemente der Oberfläche nur durch den diesen Thread erfolgen. Dazu gibt es
in der Klasse SwingUtilities die statische Methode

   SwingUtilities.invokeLater(Runnable doRun);

wodurch Runnable-Objekte in der AWT-Event-Queue für den AWT-Thread eingereiht und
dann passend durch diesen Thread ausgeführt werden können. Der ThreadingService über-
gibt grundsätzlich alle Vorgänge der Klasse RunnableSwingTask an diese Methode.


                                              - 34 -
                               Benutzeroberfläche - Threading


3.6.1.2. Worker-Threads

Für Vorgänge die länger dauern oder sogar blockieren können, sollten eigene Threads be-
nutzt werden, weil sonst die Benutzeroberfläche nicht reagiert, solange der AWT-Event-
Dispatch-Thread mit der Ausführung eines Vorgangs beschäftigt ist. Deshalb gibt es für die
Vorgänge die mit der Benutzeroberfläche interagieren, sondern nur innerhalb der Adapter-
schicht ablaufen, separate Threads.
Dazu werden vom ThreadingService die Möglichkeiten des ExecutorService aus dem
Paket java.util.concurrent verwendet.

private static final ExecutorService MULTI;
private static final ExecutorService SINGLE;

static {
  MULTI = Executors.newFixedThreadPool(5);
  SINGLE = Executors.newSingleThreadExecutor();
}

Code 23: Verwendung von ExecutorService im ThreadingService

Über die Hilfs-Klasse Executors werden zwei Instanzen von ExecutorService erzeugt,
einmal mit einem festen Pool von 5 parallelen Threads, und dann noch mit nur einem einzel-
nen Thread und einer Warteschlange zur Ausführung von Vorgängen.


3.6.2. Threading-Modelle

Der zentrale ThreadingService sorgt grundsätzlich dafür, daß jeder Vorgang
(RunnableSwingTask bzw. RunnableServiceTask) durch den richtigen Thread bzw.
ExecutorService ausgeführt wird. Er bietet dafür die Möglichkeit, die Threads auf unter-
schiedliche Weise den verschiedenen Vorgängen zuzuweisen (execute()) oder aber ganz auf
zusätzliche Threads zu verzichten.




Abbildung 28: Threading-Menü

So gibt es vier unterschiedliche Threading-Modelle, die in der Benutzeroberfläche ausgewählt
werden können, und dann beim ThreadingService über die Methode setMode() entspre-
chend mit einer der Enum-Konstanten eingestellt werden.

   •   ThreadingService.Mode.SINGLETHREADED;
   •   ThreadingService.Mode.SYNCHRONOUS;
   •   ThreadingService.Mode.ASYNCHRONOUS;
   •   ThreadingService.Mode.MULTITHREADED;

                                             - 35 -
                                Benutzeroberfläche - Threading


3.6.2.1. Singlethreaded

Bei diesem Modell wird ganz auf zusätzliche Threads verzichtet und alles wird durch den
AWT-Event-Dispatch-Thread ausgeführt. Dabei reagiert allerdings die Benutzeroberfläche
solange nicht, bis die Ausführung abgeschlossen ist.




Abbildung 29: Ablauf im Modus Singlethreaded

Der ThreadingService ruft die run()-Methoden der beteiligten Vorgänge direkt auf

   task.run();

so wie jede andere Methode, egal um welche Art von Vorgang es sich handelt. Hierfür wäre
es allerdings unnötig gewesen, jede Funktionalität in einem eigenen separaten Vorgang zu
kapseln und die Vorgänge auch noch in zwei Arten zu unterscheiden, deshalb folgen nun die
Modelle, die dieses Prinzip der separierten Vorgänge wirklich ausnutzen.




                                               - 36 -
                               Benutzeroberfläche - Threading


3.6.2.2. Synchron einreihen

Auch hier werden alle Vorgänge, unabhängig von ihrer Art, vom AWT-Event-Dispatch-
Thread ausgeführt, allerdings nicht wie im vorigen Modus, durchgängig in einem Aufruf,
sondern über die oben angesprochene Möglichkeit (siehe 3.6.1.1) durch

   SwingUtilities.invokeLater(Runnable doRun);

nacheinander, jeder für Vorgang für sich abgeschlossen, eingereiht in der AWT-Event-Queue.




Abbildung 30: Ablauf im Modus Synchron einreihen

Dadurch hat der AWT-Event-Dispatch-Thread prinzipiell die Möglichkeit zwischen den
Vorgängen des Buchhaltungssystems andere Events auszuführen. In der Praxis bedeutet das
zwar, daß die Oberfläche Benutzereingaben nicht wirklich gut annimmt, aber zumindest noch
teilweise aktualisiert wird, solange wie eben Vorgänge ablaufen.


                                             - 37 -
                               Benutzeroberfläche - Threading


3.6.2.3. Asynchron mit zusätzlichem Thread

Hier kommt ein zusätzlicher Worker-Thread zum Einsatz. Wenn der AWT-Event-Dispatch-
Thread eine Benutzereingabe verarbeitet, die zum Zugriff auf das Buchhaltungssystem führt,
wird ein Service-Vorgang (RunnableServiceTask) durch den ThreadingService in eine
separate Queue eingereiht, und dann von einem eigenen Worker-Thread ausgeführt.




Abbildung 31: Ablauf im Modus Asynchron und Multithreaded

Hier kehrt der Aufruf des Vorgangs gleich zurück und der AWT-Thread ist sofort wieder ver-
fügbar, so daß die Benutzeroberfläche nicht einfriert. Der Worker-Thread führt den Aufruf
einer Methode des Buchhaltungssystems aus, speichert das Ergebnis in der Adapterschicht
und startet einen Nachfolgevorgang. Dabei handelt es sich um einen Vorgang zur Aktualisie-
rung der Benutzeroberfläche (RunnableSwingTask) der vom ThreadingService mit
invokeLater() wieder in der AWT-Event-Queue eingereiht und dann vom AWT-Event-
Dispatch-Thread ausgeführt wird, um die Single-Thread-Regel (siehe 3.6.1.1) einzuhalten.
Anhang B stellt den genauen Ablauf in erweiterter Form dar.


3.6.2.4. Multithreaded

Der Unterschied zum asynchronen Modus (siehe 3.6.2.3) besteht lediglich darin, daß mehrere
Service-Vorgänge nicht in einer Queue eingereiht, und dann von einem Worker-Thread se-
quentiell, sondern gleichzeitig durch mehrere Worker-Threads parallel ausgeführt werden.

                                            - 38 -
                                Benutzeroberfläche - Threading



3.6.3. Abbruch eines laufenden Vorgangs

Durch die in den späteren Kapiteln aufgezeigten, verschiedenartigen Möglichkeiten das Buch-
haltungssystem zu implementieren, kann es beim Zugriff auf die Methoden der Buchhaltungs-
schnittstellen technologiebedingt zu Problemen oder Verzögerungen kommen, oder der Me-
thodenaufruf blockiert und kehrt gar nicht zurück. Deshalb kann ein Vorgang prinzipiell
durch den Benutzer abgebrochen werden (siehe 3.5.4.3), was aber nur im Asynchronen oder
Multithreaded-Modus möglich ist, weil nur dabei der AWT-Thread frei ist, um die Benutzer-
eingabe zu verarbeiten.




Abbildung 32: Ablauf beim Abbruch eines Vorgangs


                                             - 39 -
                             Benutzeroberfläche - Threading

Sobald ein Service-Vorgang (RunnableServiceTask) vom ThreadingService durch einen
Thread ausgeführt wird, teilt er dem zugehörigen GUI-Vorgang (RunnableSwingTask) die-
sen Thread durch den Aufruf von setThread() mit, bevor er die Methode des Buchhaltungs-
systems aufruft.
Wenn nun der Benutzer die Schaltfläche "Abbrechen" im Dialogfenster der laufenden Vor-
gänge betätigt, wird die Methode cancel() des GUI-Vorgangs aufgerufen. Dieser kennt den
(evtl. blockierten) Thread und unterbricht ihn mit interrupt(). Der GUI-Vorgang wird dann
sofort für abgeschlossen erklärt (finish()).
Wenn der Methodenaufruf durch den Service-Vorgang z.B. wegen Ein-/Ausgabe blockiert
hatte, sollte er zurückkehren und der Service-Vorgang zu Ende laufen. Eine Implementierung
des Buchhaltungssystems sollte unter Umständen durch

   Thread.currentThread().isInterrupted();

auch manuell prüfen, ob der ausführende Thread unterbrochen wurde. Wenn der Thread auf
etwas wartet, kann an der entsprechenden Stelle eine InterruptedException oder
ClosedByInterruptedException geworfen werden, die folgendermaßen weitergegeben (a-
ber dabei nicht erneut geworfen) werden soll [31].

   try {

     ...

   } catch (InterruptedException e) {
     Thread.currentThread().interrupt();
   }

Da der zugehörige GUI-Vorgang durch den Abbruch für abgeschlossen erklärt wurde, wird er
nicht wie üblich nach dem Service-Vorgang aufgerufen, und das Ergebnis des Methodenauf-
rufs wird nicht angezeigt.

Außerdem wird noch durch die Klasse javax.Swing.Timer ein Timer gestartet, der eine Ak-
tion (ActionListener, actionPerformed()) verzögert ausführen kann.

   Timer timer = new Timer(5000, this);
   timer.start();

Falls ein Thread bis zum Ablauf der Verzögerungszeit nicht auf interrupt() reagiert hat,
wird er durch den Timer mit

   public void actionPerformed(final ActionEvent e) {
     thread.stop();
   }

auf die "harte Tour" beendet. Die Verwendung dieser Methode ist zwar problematisch und
wird abgelehnt [31], weshalb sie auch nur verzögert als Notlösung ausgeführt wird, und im
Menü erst aktiviert werden muß.




                                          - 40 -
                           Benutzeroberfläche - Weitere Funktionen


3.7. Weitere Funktionen
3.7.1. Testfunktionen

Über das Test-Menü bietet die Benutzeroberfläche die Möglichkeit verschiedene Testfunktio-
nen zur Validierung der Funktionalitäten einer Implementierung des Buchhaltungssystems
aufzurufen.




Abbildung 33: Test-Menü

In Gegensatz zu allen anderen Aufrufen umgehen die Testfunktionen dabei die Adapter-
schicht und greifen in einem separaten Thread direkt auf die Methoden der Buchhaltung zu.


3.7.1.1. Daten erzeugen und aufräumen

Diese beiden Funktionen sind keine Testfunktionen an sich. Durch den Menüpunkt "Da-
ten erzeugen" wird von der Utilities-Klasse ein Satz Buchhaltungsdaten generiert und mit
createAccount() bzw. createAccountingEntry() an das Buchhaltungssystem übergeben,
damit ein Benutzer das System zu nicht manuell mit Testdaten füllen muß. Der Menüpunkt
"Rückstände aufräumen" ist für die folgenden Tests gedacht und entfernt Konten
(removeAccount()) und Buchungen (removeAccountingEntry()), die nach Abschluß einer
der Testfunktionen aus irgendeinem Grund übriggeblieben sind.


3.7.1.2. Test der Funktionalität

Der Funktionstest prüft die Methoden der Buchhaltung auf eine korrekte Ausführung der im
Modell spezifizierten Geschäftslogik. Dazu werden der Reihe nach Konten und Buchungen
angelegt, geprüft ob diese danach in den Auflistungen erscheinen, und wieder gelöscht. Au-
ßerdem werden die Methoden des Buchhaltungssystems auch gezielt mit ungültigen Werten
aufgerufen, um zu prüfen, ob auch die entsprechenden Ausnahmen geworfen werden.




Abbildung 34: Funktions-Test-Dialog




                                           - 41 -
                            Benutzeroberfläche - Weitere Funktionen

Nach Abschluß der Tests wird die erfolgreiche Durchführung angezeigt. Wenn allerdings
Fehler auftreten oder ein ungültiges Verhalten des Buchhaltungssystems festgestellt wird, so
wird der genaue Testverlauf in einem Nachrichtenfenster angezeigt.


3.7.1.3. Test der Geschwindigkeit

Der Geschwindigkeits-Test soll die Dauer von 2000 Methodenaufrufen bei einem Buchhal-
tungssystem ermitteln. Dazu werden zuerst 1000 Buchungen angelegt, und danach diese 1000
Buchungen wieder gelöscht. Durch die Schaltfläche "Schließen" kann der Testlauf bei zu lan-
ger Dauer vorzeitig abgebrochen werden.




Abbildung 35: Geschwindigkeits-Test-Dialog

Angezeigt werden "Minuten:Sekunden:Millisekunden" der Testdauer. Dieser Test kann
zur Bewertung der verschiedenen Technologien, mit denen das Buchhaltungssystem später
implementiert, wird dienen.


3.7.1.4. Test auf Threadsicherheit

Hierbei wird eine Implementierung des Buchhaltungssystems auf ihre Threadsicherheit gete-
stet. Dazu werden gleichzeitig 10 Threads gestartet, die auf zwei Testkonten parallel Buchun-
gen anlegen und löschen. Am Ende wird der Saldo der Testkonten auf den korrekten Wert
geprüft.




Abbildung 36: Threadsicherheits-Testdialog

Jede Implementierung des Buchhaltungssystems sollte entsprechend threadsicher sein, und
diesen Test bestehen. Zum Einen sollte am Ende bei den Testkonten der richtige Saldo be-
rechnet werden, und zum Anderen sollte die Anwendung bei den parallelen Zugriffen auch
nicht hängenbleiben (Deadlock).




                                             - 42 -
                         Benutzeroberfläche - Weitere Funktionen



3.7.2. Logging

Zur Ausgabe des Programmablaufs der Benutzeroberfläche auf der Konsole gibt es eine eige-
ne Logger-Klasse, die über das Logging-Menü gesteuert wird.




Abbildung 37: Logging-Menü

Das Logging kann im Allgemeinen an- oder ausgeschaltet werden. Weiterhin kann man ein-
zeln nur den Ablauf des AWT-Thread, der Worker-Threads oder von Beiden ausgeben lassen.
Für den Asynchronen und Multithreaded-Modus besteht die Möglichkeit die Ausgaben für
jeden Thread separat zu puffern und gesammelt auszugeben, damit sich die Ausgaben der
verschiedenen Threads nicht beliebig vermischen.


3.7.2.1. Logger-Klasse

Die Logger-Klasse verwaltet die Logging-Einstellungen der Benutzeroberfläche aus dem
Logging-Menü und erlaubt über ihre Methoden eine gut lesbar formatierte Ausgabe von De-
bug-Informationen.

package de.fhr.vs_iw.util;

public final class Logger {
  public static synchronized      void calling(String method, Object... args);
  public static synchronized      void caught(Throwable e);
  public static synchronized      void enableLogging(boolean log);
  public static synchronized      void entering(Object... args);
  public static synchronized      boolean getAutoSeparator();
  public static synchronized      boolean getBuffering();
  public static synchronized      boolean getLogEventThread();
  public static synchronized      boolean getLogWorkerThreads(),
  public static synchronized      void leaving();
  public static synchronized      boolean loggingEnabled();
  public static synchronized      void println(Object object);
  public static synchronized      void println(String text);
  public static synchronized      void registerClass();
  public static synchronized      void registerClass(Class<?> clas);
  public static synchronized      void returnedNull(String method);
  public static synchronized      void separator();
  public static synchronized      void setAutoSeparator(boolean auto);
  public static synchronized      void setBuffering(boolean buffering);
  public static synchronized      void setLogEventThread(boolean log);
  public static synchronized      void setLogWorkerThreads(boolean log);
}

Code 24: Class Logger



                                         - 43 -
                           Benutzeroberfläche - Weitere Funktionen

Eine Klasse registriert sich zuerst durch registerClass() beim Logger, der den Namen der
Klasse feststellt, um die Breite der Spalte, die die Klassennamen ausgibt anzupassen. Danach
kann die Klasse beliebige Logger-Objekte instanzieren.
Es wird gezielt nicht das verbreitete Logging-Framework "Log4j" verwendet, damit die An-
wendung einzeln für sich ohne zusätzliche notwendige Bibliotheken verteilt und verwendet
werden kann.


3.7.2.2. Auswertung des Stacktrace

Um Ordnung und Übersicht in die Ausgaben zu bringen, greift die Logger-Klasse auf den
Stacktrace des aufrufenden Thread zurück. Wenn eine Klasse einen Logger instanziert, dann
wird ihr Name über den Stacktrace ermittelt. Das vierte Element des Stacktrace stellt dabei
die aufrufende Methode dar, die Elemente davor sind die Aufrufe der Logger-Methoden
selbst.

StackTraceElement[] stack
  = Thread.currentThread().getStackTrace();
String className = stack[4].getClassName();
String threadName = Thread.currentThread().getName();

Code 25: Ermittlung des Klassennamens und des Threadnamens aus dem Stacktrace

So wird bei jeder Ausgabe der Name der aufrufenden Klasse und des aufrufenden Threads in
einer formatierten Spalte mit ausgegeben.

11:45:45|AWT-EventQueue-0|MainWindow$AccountsLoader|...
11:45:45|AWT-EventQueue-0|RunnableSwingTask        |...
11:45:45|AWT-EventQueue-0|MainWindow               |...
11:45:45|AWT-EventQueue-0|RunnableSwingTask        |...
11:45:45|worker-0        |ServiceAdapter           |...
11:45:45|worker-0        |RunnableServiceTask      |...

Code 26: In Spalten formatierte Log-Ausgabe

Außerdem wird zur Orientierung, vor allem im gepufferten Logging-Modus (siehe 3.8.2.4),
noch die Uhrzeit vorangestellt.


3.7.2.3. Hierarchische Methodenaufrufe

Der Logger soll die Aufrufe aller relevanten Methoden hierarchisch eingerückt ausgeben. Der
Name der Methode wird ebenfalls aus dem Stacktrace ermittelt.

StackTraceElement[] stack
  = Thread.currentThread().getStackTrace();
String methodName = stack[4].getMethodName();

Code 27: Ermittlung des Methodennamens aus dem Stacktrace

Jede der Methoden teilt dem Logger über den Aufruf von entering() ihren Beginn mit.
Wird die Methode normal (mit einem Rückgabewert) beendet, muß vorher leaving() aufge-
rufen werden. Die Beendigung einer Methode durch eine Ausnahme wird durch den Aufruf
von exception() beim Logger angezeigt.

                                              - 44 -
                             Benutzeroberfläche - Weitere Funktionen

...|AccountCreator     |+run()
...|RunnableSwingTask | +running()
...|MainWindow         |    +notifyRunning(<AccountCreator@23756>)
...|MainWindow         |    -notifyRunning
...|RunnableSwingTask | -running
...|ServiceAdapter     | +resultCreateAccount()
...|RunnableServiceTask|    +throwException()
...|RunnableServiceTask|    -throwException, throw AccountingException
...|ServiceAdapter     |    caught AccountingException
...|ServiceAdapter     | -resultCreateAccount, throw AccountingException
...|RunnableSwingTask | +error()
...|MainWindow         |    +notifyError(<AccountCreator@23756>)
...|MainWindow         |    -notifyError
...|RunnableSwingTask | -error
...|RunnableSwingTask | +finish()
...|MainWindow         |    +notifyFinish(<AccountCreator@23756>)
...|MainWindow         |    -notifyFinish
...|RunnableSwingTask | -finish
...|AccountCreator     |-run

Code 28: Hierarchische Methodenausgabe des Loggers

Wie hier am Beispiel des Aufrufs der Methoden zum Anlegen eines Kontos (mit ungültiger
Kontonummer und dadurch geworfener Ausnahme) erkennbar wird, entsteht eine übersichtli-
che Auflistung der Methodenaufrufe in den verschiedenen Klassen.


3.7.2.4. Unterscheidung nach Thread

Der Logger ermittelt nicht nur den Namen des aufrufenden Threads, auch die Einrückung
wird auf den jeweiligen Thread bezogen gespeichert. Je nach Einstellung
(siehe Abbildung 31) erfolgt für bestimmte Threads überhaupt keine Ausgabe. Außerdem
wird für jeden Thread ein eigener Ausgabepuffer verwendet, damit wie o.g. die Ausgaben
nach Thread zusammengefaßt und gesammelt ausgegeben werden können.


3.7.3. Look & Feel

Eines der Features von Swing ist die Verfügbarkeit verschiedener Look & Feel Klassen zur
Darstellung der Benutzeroberfläche, damit die Oberfläche jeweils zum aktuellen System paßt.
Standardmäßig wird auch die zugehörige Look & Feel Klasse des Systems initialisiert und
verwendet.
{
    try {
      String systemLF = UIManager.getSystemLookAndFeelClassName();
      UIManager.setLookAndFeel(systemLF);
    } catch (Exception e) {
      ...
    }
}

Code 29: Look & Feel Initialisierung

Diese API erlaubt aber den einfachen Austausch der Look & Feel Klasse, auch dynamisch zur
Laufzeit eines Programms.


                                             - 45 -
                           Benutzeroberfläche - Weitere Funktionen

Durch den Aufruf der Methode

   UIManager.getInstalledLookAndFeels();

erhält man eine Auflistung der verfügbaren Look & Feel Klassen.




Abbildung 38: Look & Feel Menü

Über das Look & Feel Menü kann nun jederzeit eine Look & Feel Klasse ausgewählt werden,
welche die Oberfläche dann entsprechend darstellt.




Abbildung 39: Benutzeroberfläche im "Metal" Look & Feel


                                             - 46 -
                            Benutzeroberfläche - Weitere Funktionen



Der Einstellung der im Menü gewählten Klasse erfolgt durch den Aufruf der
setLookAndFeel()-Methode der Utilities-Klasse mit dem Index des gewählten Menüein-
trags, welche die gleichnamige Methode des UIManager aufruft.

public static void setLookAndFeel(int index) throws Exception {
  final LookAndFeelInfo[] l = UIManager.getInstalledLookAndFeels();
  if (index >= 0 && index < l.length) {
    UIManager.setLookAndFeel(l[index].getClassName());
  }
}

Code 30: Utilities-Methode setLookAndFeel()

Im Anschluß muß aber die Darstellung von bereits angezeigten Swing-Komponenten auf den
neu eingestellten Look & Feel aktualisiert werden. Dafür erbt jede Komponente die Methode
updateUI() bei deren Aufruf die Darstellung auf Grundlage des aktuellen Look & Feel neu
initialisiert wird. Durch Aufruf von

   SwingUtilities.updateComponentTreeUI(this);

im Hauptfenster der Anwendung wird der gesamte Komponenten-Baum des Fensters durch
den Aufruf der updateUI()-Methode bei allen Sub-Komponenten aktualisiert. Bei Zusatzfen-
stern muß die Methode auch für diese Fenster aufgerufen werden, z.B.

   SwingUtilities.updateComponentTreeUI(taskListDialog);

und bei eigenen Komponenten muß unter Umständen die updateUI()-Methode überschrie-
ben und selbst implementiert werden.


3.7.4. Hilfe




Abbildung 40: Hilfe-Menü

Über das Hilfe-Menü können Informationen zur Buchhaltung direkt aufgerufen werden:

   •   Beschreibung:     Öffnet die HTML-Kurzbeschreibung zur Buchhaltung in einem
                         externen Web-Browser, sofern diese im Unterverzeichnis "des-
                         cription" vorhanden ist
   •   JavaDoc anzeigen: Sucht nach einer vorhandenen JavaDoc-Beschreibung und zeigt
                         diese im einem Web-Browser an
   •   Info:             Öffnet ein Informationsfenster mit einigen Angaben zur Erstel-
                         lung der Benutzeroberfläche



                                              - 47 -
                   Benutzeroberfläche - Suchen und Laden der Proxy-Klasse


3.8. Suchen und Laden der Proxy-Klasse
Wie bereits in Abschnitt 3.2 beschrieben, kann von der Benutzeroberfläche eine beliebige
Proxy-Klasse, welche die vorgegebene Proxy-Schnittstelle implementiert, geladen werden.


3.8.1. Suchverzeichnis

Standardmäßig werden die Klassen ausgehend vom aktuellen Verzeichnis gesucht. Man kann
aber jederzeit über die Schaltfläche "Verzeichnis..." ein anderes Verzeichnis auswählen.




Abbildung 41: Verzeichnis-Auswahl-Dialog

Dann wird der Verzeichnisbaum ausgehend vom gausgewählten Verzeichns rekursiv durch-
laufen, und jede gefundene class-Datei, auch innerhalb von Archiven, wird verarbeitet.


3.8.2. Klassenlader für den Suchvorgang

Der Klassenlader de.fhr.vs_iw.util.FileClassLoader führt zum einen den Suchvorgang
durch und ist zum Anderen von der Klasse ClassLoader abgeleitet, damit er die Möglichkeit
hat, mit den überschriebenen Methoden findClass() und loadClass() Klassen von der
JVM nachladen zu lassen.

public final class ClassFileLoader extends ClassLoader {
  public Class<?> findClass(String name);
  public Class<?> loadClass(String name);
  public static void cancel();
  public static boolean isSearching();
  public static void search(String dir, ClassFileLoaderListener listener);
}

Code 31: Class ClassFileLoader


                                           - 48 -
                   Benutzeroberfläche - Suchen und Laden der Proxy-Klasse

Die weiteren Methoden kontrollieren den Suchvorgang. Um Inkonsistenzen bei mehreren
Suchvorgängen über mehrere Verzeichnisse zu vermeiden sind diese Methoden statisch und
der Klassenlader wird nach dem Singleton-Design-Pattern nur einmal instanziert und gekap-
selt. Außerdem ist der Suchvorgang threadsicher synchronisiert.


3.8.3. Listener-Schnittstelle für den Klassenlader

Die aufrufende Anwendung wird mit Hilfe der ClassFileLoaderListener-Schnittstelle vom
Klassenlader über die Ereignisse beim Suchvorgang informiert.

package de.fhr.vs_iw.util;

import java.io.File;

public interface ClassFileLoaderListener {
  boolean classFound(ClassFileDecoder cfd);
  void classLoaded(ClassFileEntry entry);
  boolean classUpdate(ClassFileEntry entry, ClassFileDecoder cfd);
  boolean errorOccured(File file, Throwable e);
  void otherPath(File directory);
}

Code 32. Interface ClassFileLoaderListener

Die Anwendung wird dadurch über die folgenden Ereignisse informiert und kann das weitere
Vorgehen beim Suchvorgang durch einen entsprechenden Rückgabewert beeinflussen.

    •   otherPath         wird aufgerufen, wenn das aktuelle Suchverzeichnis gewechselt
                          wurde, oder in innerhalb eines Archivs gesucht wird
    •   classFound        wird aufgerufen, wenn eine class-Datei gefunden wurde; der
                          Rückgabewert gibt an ob die Klasse in die JVM geladen werden
                          soll
    •   classUpdate       wird aufgerufen, wenn eine neu gefundene Klasse den gleichen
                          Namen hat, wie eine bereits zuvor gefundene; der Rückgabewert gibt
                          an, ob nun die neue Klasse verwendet werden soll
    •   classLoaded       wird aufgerufen, wenn eine Klasse erfolgreich in die JVM geladen
                          wurde, die Klasse darf aber hier noch nicht verwendet werden,
                          sondern erst nach dem Abschluß des Suchvorgangs
    •   errorOccured      wird aufgerufen, wenn bei der Suche eine Ausnahme auftritt; der
                          Rückgabewert gibt an, ob der Vorgang fotgesetzt werden soll

Eine gefundene Klasse wird aber nicht unbedingt sofort geladen, auch wenn dies von der An-
wendung gewünscht wird. Es kann vorkommen, daß referenzierte Klassen noch nicht gefun-
den wurden, weshalb das Laden verzögert wird, bis alle Abhängigkeiten aufgelöst werden
können. Deshalb sind die gewünschten Klassen gezielt erst nach dem Abschluß des Suchvor-
gangs verwendbar.

Anders als bei üblichen Listener-Schnittstellen kann hier nur der eine Empfänger benachrich-
tigt werden, der den Suchvorgang gestartet hat, und der Suchvorgang kann auch nur einmal
und nicht mehrfach parallel gestartet werden.



                                             - 49 -
                    Benutzeroberfläche - Suchen und Laden der Proxy-Klasse



3.8.4. Eigenschaften gefundener Klassen

Die Eigenschaften der gefundenen Klassen werden ausgelesen, ohne daß diese von der JVM
geladen werden müssen. Die Schnittstelle de.fh.vs_iw.util.ClassFileReflection stellt
dann entsprechende Auswertungsmethoden zur Verfügung, um diese Eigenschaften gezielt
abzufragen.

package de.fhr.vs_iw.util;

public interface ClassFileReflection {
  String[] getFields();
  File getFile();
  String[] getInterfaces();
  String[] getMethods();
  String getName();
  String getSuperclass();
  ZipEntry getZipEntry();
  boolean hasField(String field);
  boolean hasInterface(String iface);
  boolean hasMethod(String method);
  boolean isAbstract();
  boolean isFinal();
  boolean isInterface();
  boolean isPublic();
}

Code 33: Interface ClassFileReflection

Ein Objekt der Klasse ClassFileDecoder dekodiert die Informationen in einer class-Datei
und stellt sie über die o.g. Schnittstelle zur Verfügung. Die Datei kann auf eine beliebige
Weise eingelesen werden, und wird der Klasse dann als byte[]-Array übergeben. Die Byte-
Daten der Klasse werden anhand der Format-Spezifikation für class-Dateien [83] dekodiert
und strukturiert aufbereitet.

package de.fhr.vs_iw.util;

public final class ClassFileDecoder implements ClassFileReflection {
  private final ZipEntry entry;
  private final File file;

    ClassFileDecoder(byte[] classbytes);
    ClassFileDecoder(byte[] classbytes, File file, ZipEntry entry);

    public   String getErrors();
    public   long getMagic();
    public   int getMajorVersion();
    public   int getMinorVersion();
    public   int getSize();
    public   boolean isValid();

    <interface methods>
}

Code 34: Class ClassFileDecoder

Zusätzlich zu den Eigenschaften der Java-Klasse an sich, wird auch noch die Herkunft der
Daten (class-Datei bzw. Teil eines Archivs) festgehalten.

                                            - 50 -
                   Benutzeroberfläche - Suchen und Laden der Proxy-Klasse



3.8.5. Laden gefundener Klassen

In Listener-Methode classFound() prüft die Anwendung, ob eine gefundene Klasse als
Proxy-Klasse in Frage kommt.

public boolean classFound(final ClassFileDecoder clas) {
  if (clas.hasInterface("de.fhr.vs_iw.model.IAccountingProxy")) {
    return true;
  }
  return false;
}

Code 35: Method classFound() in ServiceSelectDialog

Wenn dies durch den Rückgabewert true bestätigt wird, dann wird die gefundene Klasse in
die JVM geladen. Der Methode defineClass() von ClassLoader werden die eingelesen
Byte-Daten übergeben, damit die JVM daraus eine verwendbare Laufzeit-Java-Klasse
(Class-Objekt) erstellt.

     protected final Class<?> defineClass(String name, byte[] b,
         int off, int len) throws ClassFormatError

In einem Objekt von ClassFileEntry wird die geladene Klasse dann zusammen mit den In-
formationen zu ihrer Herkunft verwaltet, für den Eintrag in der Liste der gefundenen Proxy-
Klassen und auch intern für den Klassenlader.

public final class ClassFileEntry implements ClassFileReflection {
  private Class<?> clas;
  private File file;
  private String name;
  private ZipEntry zipe;

    ClassFileEntry(String name, File file, ZipEntry zipe);

    public Class<?> getTheClass();
    void setTheClass(Class< ? > c);

    <interface methods>
}

Code 36: Class ClassFileEntry

Diese Klasse implementiert auch wieder die Schnittstelle ClassFileReflection zur Abfrage
der Eigenschaften der gekapselten Klasse. Hier erfolgt die Abfrage dann aber mit Java-
Reflection und nicht mehr über die selbst dekodierten Byte-Daten der Klasse.
Alle gefundenen und erfolgreich geladenen Proxy-Klassen werden beim Aufruf der Listener-
Methode classLoaded() jeweils in die Tabelle im Auswahldialog aufgenommen


3.8.6. Anmerkung

Der Klassenladevorgang ist hier eine durchaus komplizierte Angelegenheit, da Java intern nur
Klassen kennt, aber keine Dateien im Dateisystem. Das Laden einer Klasse aus einer beliebi-
gen vorliegenden Datei ohne weitere Strukturierung und Pfade war so nicht vorgesehen.

                                               - 51 -
                Benutzeroberfläche - Verwendung der Buchhaltung in Eclipse


3.9. Verwendung der Buchhaltung in Eclipse
Gearbeitet wird mit der Buchhaltungsoberfläche in der freien Java-Entwicklungsumgebung
Eclipse [30]. Zu Beginn ist keine spezielle Version davon oder besondere zusätzliche Plugins
notwending. Die aktuelle Version 3.3.0 (Europa) des Eclipse-SDK kann unter

   http://www.eclipse.org/downloads/

heruntergeladen werden. Das Paket muß nur in ein beliebiges Verzeichnis entpackt und unter
Windows die eclipse.exe ausgeführt werden.

Es kann aber zusammen mit dem JBoss-Application-Server eine spezielle Eclipse-Version
[38] verwendet werden, die von den JBoss-Entwicklern zur Verfügung gestellt wird und unter

   http://java.fh-regensburg.de/download/jboss-eclipse-ide-2.0.zip

heruntergeladen werden kann (siehe auch 6.2.7). Alternativ kann man die JBoss-Plugins spä-
ter auch bei einem Standard-Eclipse nachträglich hinzufügen.


3.9.1. Neues Projekt anlegen

Zuerst muß in Eclipse ein neues Projekt für die Arbeit mit der Buchhaltungsanwendung ange-
legt werden. Dazu wird unter

   File -> New -> Project...

der Projekt-Assistent aufgerufen. Im ersten Dialog wird ein "Java Project" ausgewählt.




Abbildung 42: Eclipse-Dialog "New Project"




                                             - 52 -
                Benutzeroberfläche - Verwendung der Buchhaltung in Eclipse

Im nächsten Dialog wird ein Name für das Projekt (z.B. VS_IW_FiBu) vergeben und die
Grundeinstellungen vorgenommen.




Abbildung 43: Eclipse-Dialog "New Java Project"

Die Einstellung "Create separate folders for source and class files" ist sinnvoll, damit das Pro-
jekt besser strukturiert wird. Mit der Schaltfläche "Finish" wird der Dialog geschlossen.




Abbildung 44: Eclipse Package Explorer

                                              - 53 -
                Benutzeroberfläche - Verwendung der Buchhaltung in Eclipse

Das Projekt wird erzeugt und im "Package Explorer" angezeigt. Der Ordner "src" wurde au-
tomatisch für die eigenen Packages und Klassen erstellt.


3.9.2. Importieren des Package mit der Buchhaltungsoberfläche

Um die Strukturierung des Projekts weiterzuführen, wird nun ein Ordner für eigene Biblio-
theken angelegt. Der entsprechende Dialog wird im Menü unter

   File -> New -> Folder

aufgerufen. Der neue Ordner erhält den Namen "lib".




Abbildung 45: Eclipse-Dialog "New Folder"

Der neue Ordner wird im Package-Explorer angezeigt und auch gleich ausgewählt.




Abbildung 46: Eclipse Package Explorer




                                            - 54 -
                 Benutzeroberfläche - Verwendung der Buchhaltung in Eclipse

Anschließend wird das Package für die Buchhaltung benötigt. Es kann als JAR-Datei unter

    http://java.fh-regensburg.de/download/vs_iw_fibu_main_2.0.jar

heruntergeladen werden, und enthält die Benutzeroberfläche, die Buchhaltungsschnittstellen,
einige Hilfsklassen und Dummy-Proxy-Klassen zu Testzwecken.

Über die Import-Funktion von Eclipse, die im Menü unter

    File -> Import...

zu finden ist, wird die Datei importiert. Dazu wird im ersten Dialog unter "General" als Quel-
le das "File System" ausgewählt.




Abbildung 47: Eclipse-Dialog "Import" - Select

Im zweiten Dialog wird dann durch die Schaltfläche "Browse…" oder über manuelle Eingabe
das Verzeichnis angegeben, in welches die JAR-Datei mit der Buchhaltung heruntergeladen
wurde. Auf der rechten Seite zeigt Eclipse dann die Dateien in diesem Verzeichnis an. Vor die
gewünschte Datei vs_iw_fibu_mein_2.0.jar wird ein Häckchen gesetzt.
Als Zielordner ("Into folder:") sollte der neue Ordner für Bibliotheken "VS_IW_FiBu/lib"
eingetragen werden. Durch die Schaltfläche "Finish" wird der Import angeschlossen.




                                                 - 55 -
                 Benutzeroberfläche - Verwendung der Buchhaltung in Eclipse




Abbildung 48: Eclipse-Dialog "Import" - File system

Im Package Explorer wird die soeben importierte Bibliothek angezeigt, und sie wurde von
Eclipse auch dateimäßig ins Projektverzeichnis kopiert.




Abbildung 49: Eclipse Package Explorer

In den Projekteigenschaften muß nun eingestellt werden, daß die Bibliothek von Eclipse bei
der Compilierung von Java-Code einbezogen werden soll. Das entsprechende Dialogfenster
wird im Menü unter

    Project -> Properties

aufgerufen. Unter der Rubrik "Java Build Path" und dem Reiter "Libraries" sind die bei der
Compilierung verwendeten Bibliotheken aufgelistet.


                                                - 56 -
                 Benutzeroberfläche - Verwendung der Buchhaltung in Eclipse




Abbildung 50: Eclipse-Dialog "Properties for VS_IW_FiBu"

Von der Erstellung des Projektes her ist bereits der Eintrag für die Java-Standard-
Bibliotheken (z.B. "JRE System Library [jre1.5.0_12]") vorhanden. Mit der Schaltfläche
"Add JARs…" werden Bibliotheken aus dem aktuellen Workspace hinzugefügt. In einem
separaten Fenster kann die JAR-Datei der Buchhaltung ausgewählt werden.




Abbildung 51: Eclipse-Dialog "JAR Selection"

Nach dem Schließen der Dialogfenster wird im Package Explorer die Anzeige aktualisiert.
Die JAR-Datei wird nicht mehr unter "lib", sondern als Java-Bibliothek unter "References
Libraries" angezeigt. Man kann sich nun auch die enthaltenen Packages und Klassen anzeigen
lassen.



                                               - 57 -
                Benutzeroberfläche - Verwendung der Buchhaltung in Eclipse




Abbildung 52: Eclipse Package Explorer



3.9.3. Start der Anwendung

Gestartet wird die Anwendung über die Klasse de.fhr.vs_iw.Client (siehe auch 3.2), die
jetzt nach dem Importieren im CLASSPATH von Eclipse verfügbar ist. Über den Menüpunkt

   Run -> Open Run Dialog...

wird das Dialogfenster zum Erstellen einer Startkonfiguration aufgerufen.




Abbildung 53: Eclipse-Dialog "Run" - Empty



                                             - 58 -
                 Benutzeroberfläche - Verwendung der Buchhaltung in Eclipse

Als Typ wird "Java Application" ausgewählt und durch die Schaltfläche "New" wird eine
neue Startkonfiguration erzeugt.
Für die Konfiguration kann ein Name (z.B. Client) vergeben werden. Unter dem Punkt
"Main class" wird die o.g. Klasse de.fhr.vs_iw.Client eingetragen.




Abbildung 54: Eclipse-Dialog "Run" - Java Application

Auf dem Reiter "Aguments" könnten noch weitere Argumente zum Start der Anwendung
angegeben werden, wie auf der Kommandozeile auch. Dies könnte eben der Name einer vor-
handenen Proxy-Klasse sein, die sofort beim Aufruf der Benutzeroberfläche geladen werden
soll.
Durch die Schaltfläche "Run" wird das Dialogfenster geschlossen und die Buchhaltungsan-
wendung gestartet.


3.9.4. Erstellung eines eigenen Moduls für die Buchhaltung

Sobald die Benutzeroberfläche der Buchhaltungsanwendung abblauffähig ist, kann mit der
Erstellung eigener Buchhaltungsklassen begonnen werden. Dazu wird günstigerweise zuerst
über den Menüpunkt

    File -> New -> Package

eine neues Package (z.B. de.fhr.vs_iw.local) angelegt, um die Übersicht zu behalten.



                                               - 59 -
                Benutzeroberfläche - Verwendung der Buchhaltung in Eclipse




Abbildung 55: Eclipse-Dialog "New Java Package"

Das neue Package wird im Package Explorer standardmäßig im Ordner "src" angelegt.




Abbildung 56: Eclipse Package Explorer

Anschließend werden die eigenen Klassen über den entprechenden Dialog im Menü unter

   File -> New -> Class

erstellt. Für jede neue Klasse muß eine Name vergeben werden. Als Package wird über die
zweite Schaltfläche "Browse…" die zuvor angelegte Package de.fhr.vs_iw.local ausge-
wählt. Im Bereich "Interfaces:" wird jeder neuen Klasse mit der Schaltfläche "Add…" eine
der Buchhaltungsschnitststellen aus dem FiBu-Paket hinzugefügt (z.B. IAccountingProxy).




                                             - 60 -
                 Benutzeroberfläche - Verwendung der Buchhaltung in Eclipse




Abbildung 57: Eclipse-Dialog "Implemented Interfaces Selection"




Abbildung 58: Eclipse-Dialog "New Java Class"



                                                - 61 -
                 Benutzeroberfläche - Verwendung der Buchhaltung in Eclipse

Dieser Vorgang wird für alle vier Schnittstellen der Buchhaltung (siehe 2.2) wiederholt, bis
für jede Schnittstelle jeweils eine zugehörige implementierende Klasse angelegt wurde.

    IAccount                    ->    de.fhr.vs_iw.local.Account
    IAccountingEntry            ->    de.fhr.vs_iw.local.AccountingEntry
    IAccountingService          ->    de.fhr.vs_iw.local.AccountingService
    IAccountingProxy            ->    de.fhr.vs_iw.local.LocalProxy




Abbildung 59: Eclipse Package Explorer

Eclipse erstellt für jede Methode jeder der Schnittstellen eine leere Methode in der zugehöri-
gen Klasse, z.B. in der Klasse de.fhr.vs_iw.IAccountingService:

    public Double calculateBalance(Integer number) throws IOException,
        AccountingException {
      // TODO Auto-generated method stub
      return null;
    }

Die Methoden müssen nun mit der entsprechenden Funktionalität ausgestattet werden, um die
Geschäftslogik bzw. die Datenübertragung des Buchhaltungssystems zu realisieren.




Abbildung 60: Eclipse-Gesamtansicht des neu erstellten Projekts


                                                - 62 -
                                  Datenhaltung - Übersicht


4. Datenhaltung




4.1. Übersicht
Der erste Abschnitt über die verschiedenen möglichen Module der Buchhaltung beschäftigt
sich mit der Datenhaltungsschicht als Endpunkt des Systems (siehe 2.4).


4.1.1. Datenspeicherung

Der Hauptthematik dabei ist die Speicherung der Buchhaltungsdaten. Eine Service-Klasse
muß eine Anzahl von Konten und Buchungen verwalten, die ja wiederum ihre einzelnen Da-
ten besitzen. Neu angelegte Konten oder Buchungen müssen später in der Auflistung erschei-
nen und auch wieder abrufbar sein.


4.1.2. Geschäftslogik

Ein weiterer Punkt, der die Datenhaltungsschicht betrifft, ist die korrekte Implementierung der
Geschäftslogik, damit bei ungültigen Aufrufen die entsprechenden Ausnahmen geworfen
werden. Dies erfordert eine bidirektionale Beziehung zwischen der Service-Klasse, den Kon-
ten und den Buchungen.

Das bedeutet, daß die Service-Klasse nicht einfach nur alle vorhandenen Konten und Buchun-
gen verwalten muß, sondern jedes Konten- bzw. Buchungsobjekt muß seine zugehörige Ser-
vice-Klasse kennen. Nur so kann z.B. ein Buchungsobjekt, dessen Haben-Konto neu festge-
legt wird, feststellen, ob dieses Konto auch vorhanden und gültig ist, indem es auf die Kon-
tenliste der Service-Klasse zugreift.

Beim Laden und Speichern der Objekte muß dieser Bezug hergestellt und vor allem auch
wieder aufgelöst werden, damit der Vorgang nicht in eine endlose Rekursion läuft. Dies ist
bei allen im Folgenden Technologien ein Punkt der besondere Aufmerksamkeit erfordert.




                                            - 63 -
                        Datenhaltung - Einfache lokale Datenhaltung


4.2. Einfache lokale Datenhaltung
4.2.1. Allgemeines

Als Einstieg in die Datenhaltung werden die Daten des Buchhaltungssystems in Objekten der
laufenden Virtuellen Maschine gespeichert, wobei die Daten aber flüchtig sind, und verloren
gehen, wenn die VM terminiert. Die Geschäftslogik wird vollständig implementiert.


4.2.2. Daten in HashMaps

Zur Verwaltung werden die Objekte mit den Daten in HashMaps abgelegt, damit effizient dar-
auf zugegriffen werden kann.


4.2.2.1. Konten und Buchungen

Die Service-Klasse benötigt zwei HashMaps jeweils für die Buchungen und Konten

private Map<Integer, AccountingEntry> accountingEntries
  = new HashMap<Integer, AccountingEntry>();

private Map<Integer, Account> accounts
  = new HashMap<Integer, Account>();

Code 37: HashMaps für Konten- und Buchungs-Objekte

Ein Objekt wird dabei direkt der ihm vergebenen Nummer bzw. ID zugeordnet, damit es spä-
ter direkt gefunden zurückgegeben werden kann.


4.2.2.2. Soll- und Haben-Buchungen

Die Konto-Klasse verwaltet ihre zugeordneten Soll- und Haben-Buchungen ebenfalls in zwei
HashMaps.

private Map<Integer, AccountingEntry> creditEntries
  = new HashMap<Integer, AccountingEntry>();

private Map<Integer, AccountingEntry> debitEntries
  = new HashMap<Integer, AccountingEntry>();

Code 38: HashMaps für Soll- und Haben-Buchungen

Dabei werden natürlich dieselben Objekte referenziert, wie in der entsprechenden HashMap in
der Service-Klasse.


4.2.2.3. Bidirektionaler Bezug

Die Service-Klasse stellt den bereits angesprochenen bidirektionalen Bezug her, indem sie
einen Verweis auf sich selbst (this) an alle neuen Konten- und Buchungs-Objekte übergibt.



                                            - 64 -
                        Datenhaltung - Einfache lokale Datenhaltung



4.2.3. Implementierung der Schnittstellen

Es werden alle Schnittstellen des Buchhaltungsmodels in Klassen implementiert und mit der
nötigen Funktionalität im Sinne der Geschäftlogik versehen.


4.2.3.1. Service-Klasse

Die Service-Klasse implementiert die Service-Schnittstelle (siehe 2.2.4) und die HashMaps zur
Aufnahme der Konten- und Buchungs-Objekte. Die ID für neue Buchungen wird mit dem
Feld nextID einfach hochgezählt.

package de.fhr.vs_iw.local;

public final class AccountingService implements IAccountingService {

  private   Map<Integer, AccountingEntry> accountingEntries
    = new   HashMap<Integer, AccountingEntry>();
  private   Map<Integer, Account> accounts
    = new   HashMap<Integer, Account>();

  private int nextId = 0;

  private AccountingService() {}

  public Double calculateBalance(Integer number)
      throws AccountingException, IOException {
    return ((Account)getAccountByNumber(number)).balance();
  }

  public void createAccount(Integer number, AccountType type,
      String description) throws AccountingException {
    if (number <= 0) {
      throw new AccountingException("Die Nummer darf nicht negativ sein");
    }
    synchronized (accounts) {
      checkAccountNumber(number, false);
      Account acc = new Account(this, number, description, type);
      accounts.put(number, acc);
    }
  }

  public Integer createAccountingEntry(Integer debitAccount,
      Integer creditAccount, Double amount, String text)
      throws AccountingException {
    synchronized (accountingEntries) {
      Integer id = nextId++;
      AccountingEntry = new AccountingEntry(this, id, debitAccount,
      creditAccount, amount, text);
      accountingEntries.put(id, entry);
      ((Account)getAccountByNumber(debitAccount)).addDebitEntry(entry);
      ((Account)getAccountByNumber(creditAccount)).addCreditEntry(entry);
    }
    return id;
  }




                                           - 65 -
                 Datenhaltung - Einfache lokale Datenhaltung

public IAccount getAccountByNumber(Integer number)
    throws AccountingException {
  synchronized (accounts) {
    checkAccountNumber(number, true);
    return accounts.get(number);
  }
}

public Set<Integer> getAccountingEntries() {
  synchronized (accountingEntries) {
    return accountingEntries.keySet();
  }
}

public IAccountingEntry getAccountingEntryById(Integer id)
    throws AccountingException {
  synchronized (accountingEntries) {
    IAccountingEntry entry = accountingEntries.get(id);
    if (entry == null) {
      throw new AccountingException("Buchung nicht vorhanden");
    }
    return entry;
  }
}

public Set<Integer> getAccounts() {
  synchronized (accounts) {
    return accounts.keySet();
  }
}

public void removeAccount(Integer accountNumber)
    throws IOException, AccountingException {
  synchronized (accountingEntries) {
    checkAccountNumber(accountNumber, true);
    for (IAccountingEntry entry : accountingEntries.values()) {
      if (entry.getCreditAccount().equals(accountNumber)
          || entry.getDebitAccount().equals(accountNumber)) {
        throw new AccountingException("Konto wird noch verwendet");
      }
    }
    synchronized (accounts) {
      accounts.remove(accountNumber);
    }
  }
}

public void removeAccountingEntry(Integer id)
    throws IOException, AccountingException {
  synchronized (accountingEntries) {
    IAccountingEntry entry = getAccountingEntryById(id);
    Account credit
      = (Account)getAccountByNumber(entry.getCreditAccount());
    Account debit
      = (Account)getAccountByNumber(entry.getDebitAccount());
    credit.removeCreditEntry(id);
    debit.removeDebitEntry(id);
    accountingEntries.remove(id);
  }
}




                                   - 66 -
                          Datenhaltung - Einfache lokale Datenhaltung

    protected void checkAccountNumber(Integer number, boolean should)
        throws AccountingException {
      synchronized (accounts) {
        if (accounts.containsKey(number)) {
          if (!should) {
            throw new AccountingException("Konto bereits vorhanden");
          }
        } else {
          if (should) {
            throw new AccountingException("Konto nicht vorhanden");
          }
        }
      }
    }
}

Code 39: Class AccountingService (Lokal)

Die Klasse implementiert alle spezifizierten Funktionalitäten im Sinne der Geschäftslogik und
besitzt dafür die Methode checkAccountNumber(), um den Status eines Kontos zu überprü-
fen und gegebenenfalls eine Ausnahme zu werfen. Das Erzeugen und Entfernen von Buchun-
gen wird an die dabei involvierten Konten weitergegeben.


4.2.3.2. Konto-Klasse

Hier wird die Konto-Schnittstelle (siehe 2.2.1) implementiert und die Buchungen eines Kon-
tos in den angesprochenen HashMaps verwaltet.

package de.fhr.vs_iw.local;

public final class Account implements IAccount {
  private String accountDesc = null;
  private Integer accountNumber = null;
  private AccountType accountType = null;

    private   Map<Integer, AccountingEntry> creditEntries
      = new   HashMap<Integer, AccountingEntry>();
    private   Map<Integer, AccountingEntry> debitEntries
      = new   HashMap<Integer, AccountingEntry>();

    protected Account(Integer number, String description, AccountType type) {
      accountNumber = number;
      accountType = type;
      accountDesc = description;
    }

    public Set<Integer> getCreditEntries() {
      return new HashSet<Integer>(creditEntries.keySet());
    }

    public Set<Integer> getDebitEntries() {
      return new HashSet<Integer>(debitEntries.keySet());
    }
    public String getDescription() {
      return accountDesc;
    }




                                            - 67 -
                          Datenhaltung - Einfache lokale Datenhaltung

    public Integer getNumber() {
      return accountNumber;
    }

    public AccountType getType() {
      return accountType;
    }

    public void setDescription(String description) {
      accountDesc = description;
    }

    public void setType(AccountType type) {
      accountType = type;
    }

    protected Double balance() throws IOException {
      double balance = 0.0;
      for (IAccountingEntry entry : debitEntries.values()) {
        balance += entry.getAmount().doubleValue();
      }
      for (IAccountingEntry entry : creditEntries.values()) {
        balance -= entry.getAmount().doubleValue();
      }
      return balance;
    }

    protected void addCreditEntry(AccountingEntry entry) {
      creditEntries.put(entry.getId(), entry);
    }

    protected void addDebitEntry(AccountingEntry entry) {
      debitEntries.put(entry.getId(), entry);
    }

    protected void removeCreditEntry(Integer entryId) {
      creditEntries.remove(entryId);
    }

    protected void removeDebitEntry(Integer entryId) {
      debitEntries.remove(entryId);
    }
}

Code 40: Class Account (Lokal)

Die Klasse enthält von der Konto-Schnittstelle unabhängige Methoden zur Verwaltung der
Objekte in den HashMaps:

     •   addCreditEntry()         - Haben-Buchung hinzufügen
     •   addDebitEntry()          - Soll-Buchung hinzufügen
     •   removeCreditEntry()      - Haben-Buchung entfernen
     •   removeDebitEntry()       - Soll-Buchung entfernen

Dadurch kann die Service-Klasse Informationen über die vorhandenen Buchungen weiterge-
ben. Da die Konto-Klasse dadurch vollständige Informationen über ihre Buchungen besitzt
wird auch die Berechnung des Saldos (balance()) hier durchgeführt und von der Service-
Klasse aufgerufen.


                                            - 68 -
                     Datenhaltung - Einfache lokale Datenhaltung


4.2.3.3. Buchungs-Klasse

Die Buchungs-Klasse nimmt die Daten einer Buchung auf und bietet über die Buchungs-
Schnittstelle Zugriff darauf.

package de.fhr.vs_iw.local;

public final class AccountingEntry implements IAccountingEntry {

  private   Integer creditAccount = null;
  private   Integer debitAccount = null;
  private   Double entryAmount = null;
  private   Integer entryId = null;
  private   String entryText = null;

  private AccountingService serviceClass = null;

  protected AccountingEntry(AccountingService service, Integer id,
      Integer debit, Integer credit, Double amount, String text)
      throws AccountingException {
    serviceClass = service;
    setDebitAccount(debit);
    setCreditAccount(credit);
    entryAmount = amount;
    entryId = id;
    entryText = text;
  }

  public Double getAmount() {
    return entryAmount;
  }

  public Integer getCreditAccount() {
    return creditAccount;
  }

  public Integer getDebitAccount() {
    return debitAccount;
  }

  public Integer getId() {
    return entryId;
  }

  public String getText() {
    return entryText;
  }

  public void setAmount(Double amount) {
    entryAmount = amount;
  }

  public void setText(String text) {
    entryText = text;
  }




                                       - 69 -
                            Datenhaltung - Einfache lokale Datenhaltung

    public void setCreditAccount(Integer number) throws AccountingException {
      serviceClass.checkAccountNumber(number, true);
      if (number.equals(debitAccount)) {
        throw new AccountingException("Haben-Konto und Soll-Konto identisch")
      }
      creditAccount = number;
    }

    public void setDebitAccount(Integer number) throws AccountingException {
      serviceClass.checkAccountNumber(number, true);
      if (number.equals(creditAccount)) {
        throw new AccountingException("Haben-Konto und Soll-Konto identisch")
      }
      debitAccount = number;
    }
}

Code 41: Class AccountingEntry (Lokal)

Über den Rückbezug zur Service-Klasse (Feld serviceClass), die im Konstruktor übergeben
wird, kann die Funktion checkAccountNumber() aufgerufen werden, um die Gültigkeit der
zugeordneten Konten zu überprüfen.


4.2.3.4. Proxy-Klasse

Die Proxy-Klasse hat hier noch kaum Funktionalität, sie muß im Prinzip nur vorhanden sein
um instanziert werden zu können.

package de.fhr.vs_iw.local;

public final class LocalProxy implements IAccountingProxy {
  public LocalProxy() {}
  public void close() {}
  public IAccountingService connect(String[] args) throws IOException {
    return new AccountingService();
  }
}

Code 42: Class LocalProxy

Bei Aufruf der connect()-Methode wird lediglich eine neue Instanz der Service-Klasse er-
zeugt und zurückgegeben.


4.2.4. Fazit

Die erfolgte erste Implementierung der Geschäftslogik ist effektiv und einfach, jedoch die
Tatsache, daß die Daten des Systems nach dem Ende der Anwendung verloren gehen, paßt
nicht unbedingt zu den Erfordernissen der Datenhaltungsschicht. Deshalb geht es im folgen-
den um die dauerhafte Speicherung der Buchhaltungsdaten.




                                              - 70 -
                                 Datenhaltung - Java-Serialisierung


4.3. Java-Serialisierung
4.3.1. Allgemeines

Eine grundlegende Möglichkeit um Daten mit Java zu speichern, ist das Serialisieren der Ob-
jekte in eine Datei. Dazu müssen die vorhandenen Klassen des "lokalen" Moduls lediglich
etwas erweitert werden.


4.3.2. Serialisierbare Klassen

Im Prinzip muß bei der Konto-Klasse und der Buchungs-Klasse nur die Schnittstelle
java.io.Serializable implementiert werden, damit die Konten- und Buchungs-Objekte
durch einen ObjectOutputStream gespeichert und durch einen ObjectInputStream einge-
lesen werden können.


4.3.2.1. Konto-Klasse

Die vorhandene Konto-Klasse muß lediglich durch die Implementierung der Serializa-
ble-Schnittstelle als serialisierbar deklariert werden.

package de.fhr.vs_iw.serialize;

public final class Account implements IAccount, Serializable {
  private static final long serialVersionUID = 1L;

    ...

    <Fields and Methods>
}

Code 43: Class Account (Serialize)

Mit der Implementierung der Schnittstelle geht einher, daß die Klasse ein statisches Feld
serialVersionUID definieren sollte, das Versionsänderungen bei serialisierten Objekten an-
gibt. So kann die Klasse mit ihren Daten von Java bereits vollständig serialisiert werden.


4.3.2.2. Buchungs-Klasse

Auch die vorhandene Buchungs-Klasse wird um die Serializable-Schnittstelle und die zu-
gehörige Serialisierungs-ID erweitert.

package de.fhr.vs_iw.serialize;

public final class AccountingEntry
    implements IAccountingEntry, Serializable {
  private static final long serialVersionUID = 1L;

    ...

    <Fields and Methods>

    ...

                                               - 71 -
                                Datenhaltung - Java-Serialisierung

    private void readObject(java.io.ObjectInputStream stream)
        throws IOException, ClassNotFoundException {
      entryId=stream.readInt();
      entryAmount=stream.readDouble();
      entryText=(String)stream.readObject();
      debitAccount=stream.readInt();
      creditAccount=stream.readInt();
    }

    private void writeObject(java.io.ObjectOutputStream stream)
        throws IOException {
      stream.writeInt(entryId);
      stream.writeDouble(entryAmount);
      stream.writeObject(entryText);
      stream.writeInt(debitAccount);
      stream.writeInt(creditAccount);
    }
}

Code 44: Class AccountingEntry (Serialize)

Hier besteht allerdings ein Problem mit dem vorhandenen Bezug zur Service-Klasse, die nicht
serialisierbar ist, und es auch nicht sein soll, und deshalb einen Fehler hervorrufen würde.
Deshalb muß die Serialisierung "manuell" durchgeführt werden, durch die Methoden

     •   private void writeObject(java.io.ObjectOutputStream stream)
     •   private void readObject(java.io.ObjectInputStream stream)

die vom Serialisierungs-Vorgang aufgerufen werden, wenn sie mit genau dieser Signatur vor-
liegen. Damit werden alle Daten eines Buchungsobjekts, außer dem Bezug zur Service-
Klasse, verarbeitet.


4.3.3. De-/Serialisierungs-Vorgang

Nachdem die Konto- und Buchungs-Klasse entsprechend vorbereitet wurden, müssen nun die
Objekte dieser Klasse in eine Datei serialisiert und wieder geladen werden.


4.3.3.1. Service-Klasse

Dazu wird die Service-Klasse um zwei Methoden zum Laden und Speichern erweitert.

protected void loadData(File file)
    throws FileNotFoundException, IOException, ClassNotFoundException {
  if (file.exists()) {
    ObjectInputStream stream = new ObjectInputStream(
        new FileInputStream(file));
    nextId = stream.readInt();
    accounts = (Map<Integer, Account>)stream.readObject();
    accountingEntries
      = (Map<Integer, AccountingEntry>)stream.readObject();
    stream.close();
  }
}

Code 45: Method loadData() (Serialize)


                                              - 72 -
                                Datenhaltung - Java-Serialisierung

protected void storeData(File file)
    throws FileNotFoundException, IOException {
  ObjectOutputStream stream
    = new ObjectOutputStream(new FileOutputStream(file));
  stream.writeInt(nextId);
  stream.writeObject(accounts);
  stream.writeObject(accountingEntries);
  stream.close();
}

Code 46: Method storeData() (Serialize)

Die Methode loadData() lädt die serialisierten Objekte aus der angegebenen Datei, die vor-
her durch storeData() gespeichert wurden. Dabei werden die HashMaps der Konten und
Buchungen als Einheit behandelt und auf einmal verarbeitet. Java übernimmt automatisch die
Verarbeitung der enthaltenen Objekte. Außerdem wird die ID der nächsten Buchung (nextId)
mitgespeichert, damit der Zustand des Systems nach dem Laden wieder konsistent ist.


4.3.3.2. Proxy-Klasse

Die Proxy-Klasse muß nun feststellen in welcher Datei die Objekte abgelegt werden sollen
und damit die Methoden zum Laden und Speichern der Service-Klasse aufrufen.

package de.fhr.vs_iw.serialize;

public final class SerializeProxy implements IAccountingProxy {

    private AccountingService service = null;
    private File file = null;

    public void close() throws IOException {
      service.storeData(file);
      service = null;
      file = null;
    }

    public IAccountingService connect(String[] args) throws IOException {
      if (args.length <= 0) {
        throw new IllegalArgumentException("Datei muß angegeben werden");
      }
      file = new File(args[0]);
      service = new AccountingService();
      try {
        service.loadData(file);
      } catch (ClassNotFoundException e) {
        IOException ioe = new IOException(e.getMessage());
        ioe.initCause(e);
        throw ioe;
      }
      return service;
    }
}

Code 47: Class SerializeProxy




                                              - 73 -
                             Datenhaltung - Java-Serialisierung


4.3.4. Fazit

Die Serialisierung von Java-Objekten stellt zwar eine einfache Möglichkeit dar, Daten zu
speichern, zumal auch ohne großartiges Zutun Objekthierarchien verarbeitet werden können.
Auch der Zugriff auf die Daten erfolgt sehr schnell, da alle Daten während der Verwendung
des Systems im Speicher gehalten werden, und die De-/Serialisierung nur am Anfang bzw. am
Ende erfolgt.
Für eine dauerhafte Verarbeitung von Daten ist dieses Verfahren jedoch nicht geeignet, da die
serialisierten Objekte an die Implementierung der Klassen zum Zeitpunkt der Serialisierung
gebunden sind, und schnell eine Situation auftreten kann, in der vorhandene Daten nicht wie-
der eingelesen werden können, wenn an Klassen funktionale Änderungen durchgeführt wor-
den sind. Das System ist daher zu unflexibel, weil die Implementierung der Geschäftslogik
und die letztendliche Datenspeicherung zu sehr ineinander verwoben sind.




                                           - 74 -
                       Datenhaltung - XML-Serialisierung über JAXB


4.4. XML-Serialisierung über JAXB
4.4.1. Allgemeines

Die Java Architecture für XML Binding (JAXB) [69] ist ein Framework, um die Daten aus
einem XML-Schema an Java-Klassen zu binden und sogar aus einer XML-Schema-Instanz
heraus zu erzeugen. Dadurch kann mit XML-Daten direkt über Java-Objekte gearbeitet wer-
den, und ein Entwickler muß nicht manuell auf XML-Parsing-Technologien wie DOM (Do-
cument Object Model) oder SAX (Simple API for XML) zurückgreifen [70].

Das Framework ist als Teil des Java-Web-Service-Developer-Pack von Sun unter

   http://java.sun.com/webservices/downloads/webservicespack.html

oder separat als Referenz-Implementierung unter

   https://jaxb.dev.java.net/

erhältlich. Ein Satz von Bibliotheken aus dem jeweiligen Paket muß zum CLASSPATH der An-
wendung hinzugefügt werden.

   •   jaxb1-impl.jar
   •   jaxb-api.jar
   •   jaxb-impl.jar
   •   jaxb-xjc.jar

Die Bibliotheken enthalten die notwendigen Schnittstellen, Annotations und Werkzeuge zur
Verwendung von JAXB und der automatischen Erzeugung der notwendigen Artefakte und
Dateien.


4.4.2. Annotations

Die Verbindung des XML-Schemas zu den Java-Klassen wird durch JSR-250-Annotations
[65] aus dem Paket javax.xml.bind.annotations [71] im Quellcode beschrieben. Stan-
dardmäßig werden die öffentlichen Eigenschaften einer Klasse, für die entsprechende Getter-
Methoden vorhanden sind (JavaBean-Style) und einen bekannten Datentyp zurückgeben in
das XML-Schema abgebildet, auch wenn diese Eigenschaften nicht explizit durch Annotati-
ons ausgezeichnet sind.
Ausgehend von einer bestimmten Klasse als Wurzel-Element wird dadurch eine beliebige
komplexe Objekthierarchie auf eine XML-Datei abgebildet. Durch die Annotations wird die-
ser Vorgang gesteuert.


4.4.2.1. Package-Annotations

Die erste Gruppe von XML-Annotations hat für eine gesamte Java-Package Gültigkeit und
beschreibt allgemeine Einstellung für die Zuordnung zum XML-Schema und die Verarbei-
tung der Java-Klassen.




                                          - 75 -
                        Datenhaltung - XML-Serialisierung über JAXB



Annotation                Beschreibung und Standardwerte
                          Ordnet ein Java-Paket einem XML-Namensraum zu.

                          @XmlSchema (
@XmlSchema                  xmlns = {},
                            namespace = "",
                            elementFormDefault = XmlNsForm.UNSET;
                            attributeFormDefault = XmlNsForm.UNSET,
                          )
                          Gibt die standardmäßige Verarbeitungsvariante für Felder und
                          Eigenschaften an.
@XmlAccessorType
                          @XmlAccessorType (
                            value = AccessType.PUBLIC_MEMBER
                          )
                          Stellt die Anordnung der XML-Elemente für Felder und Eigen-
                          schaften einer Klasse ein.
@XmlAccessorOrder
                          @XmlAccessorOrder (
                            value = AccessorOrder.UNDEFINED
                          )
                          Eine eigene Zuordnung zu einem integrierten XML-Schema-Typ.

@XmlSchemaType            @XmlSchemaType (
                            namespace = "http://www.w3.org/2001/XMLSchema",
                            type = DEFAULT.class
                          )
@XmlSchemaTypes           Faßt mehrere @XmlSchemaType Annotations zusammen.

Tabelle 4: JAXB Paket-Annotations


4.4.2.2. Klassen-Annotations

Eine Java-Klasse wird auf einen komplexen Typ des XML-Schemas abgebildet, wobei eine
Klasse dabei als Wurzel des XML-Baumes bzw. als "Document-Element" beschreiben wird.

Annotation                    Beschreibung und Standardwerte
                              Bildet eine Java-Klasse auf einen Schema

                              @XmlType (
                                name = "##default",
@XmlType                        propOrder = {""},
                                namespace = "##default" ,
                                factoryClass = DEFAULT.class,
                                factoryMethod = ""
                              )
                              Definiert eine Klasse als Wurzel-Element des Schemas.

@XmlRootElement               @XmlRootElement (
                                name = "##default",
                                namespace = "##default"
                              )

Tabelle 5: JAXB Klassen-Annotations


                                            - 76 -
                       Datenhaltung - XML-Serialisierung über JAXB


4.4.2.3. Enum-Annotations

Die folgenden Annotations können bei einer Java-Enumeration verwendet werden.

Annotation                  Beschreibung und Standardwerte
                            Ordnet eine Enumeratioen einem einfachen XML-Typ zu.
@XmlEnum                    @XmlEnum (
                              value = String.class
                            )
                            Bildet eine einzelne Konstante einer Enumeration in XML ab.
@XmlEnumValue               @XmlEnumValue (
                              value = "##default"
                            )
                            Bildet eine Enumeration durch einen komplexen Typ im
                            XML-Schema ab.

                            @XmlType (
@XmlType                      name = "##default",
                              propOrder = {""},
                              namespace = "##default" ,
                              factoryClass = DEFAULT.class,
                              factoryMethod = ""
                            )
                            Gibt die Enumeration als Wurzel-Element an.

@XmlRootElement             @XmlRootElement (
                              name = "##default",
                              namespace = "##default"
                            )

Tabelle 6: JAXB Enum-Annotations


4.4.2.4. Field-Annotations

Die Eigenschaften einer Klasse (Getter-Methoden) bzw. die Felder werden durch die folgen-
den Annotations ausgezeichnet.

Annotation                  Beschreibung und Standardwerte
                            Bildet ein Feld als fest benanntes XML-Element ab.

                            @XmlElement (
                              name = "##default",
@XmlElement                   nillable = false,
                              namespace = "##default",
                              type = DEFAULT.class,
                              defaultValue = "\u0000"
                            )
@XmlElements                Faßt mehrere @XmlElement Annotations zusammen.




                                          - 77 -
                        Datenhaltung - XML-Serialisierung über JAXB



                              Bildet ein Feld als dynamisch zugeordnetes XML-Element ab.

                              @XmlElementRef (
@XmlElementRef                  name = "##default",
                                namespace = "##default",
                                type = DEFAULT.class,
                              )
@XmlElementRefs               Faßt mehrere @XmlElementRef Annotations zusammen.
                              Wird für eine Liste verwendet, die XML-Elemente aufnimmt,
                              die keinem Feld direkt zugeordnet werden können.
@XmlAnyElement                @XmlAnyElement (
                                lax = false,
                                value = W3CDomHandler.class
                              )
                              Ordnet ein Feld einem XML-Attribut zu.

                              @XmlAttribute (
@XmlAttribute                   name = ##default,
                                required = false,
                                namespace = "##default"
                              )
@XmlAnyAttribute
                              Wird für eine Liste verwendet, die XML-Attribute aufnimmt,
                              die keinem Feld direkt zugeordnet werden können.
@XmlTransient                 Verhindert daß ein Feld im XML-Schema abgebildet wird.
@XmlValue                     Ordnet ein Feld einem einfachen XML-Typ zu.
@XmlID                        Verwendet ein Feld als XML-ID.
@XmlIDREF                     Verwendet ein Feld als Referenz auf eine XML-ID.
@XmlList
                              Bildet die Objekte einer Kollektion nicht als XML-Elemente,
                              sondern als zusammengesetzte Liste ihrer Werte ab.
@XmlMixed                     Gibt ein Feld zur Aufnahme von XML-Mixed-Content an.
@XmlMimeType                  Assoziiert einen MIME-Typ mit einem Feld.
@XmlAttachmentRef             Spezifiziert ein Feld als URI zu einem MIME-Anhang.
                              Gibt an, daß binäre Daten in base64-codierter Form direkt im
@XmlInlineBinaryData          XML-Element stehen sollen, und nicht XOP-codiert werden
                              sollen.
                              Bewirkt die Erstellung eines zusätzlichen Elements um die
                              XML-Darstellung einer Java-Klasse herum. Wird typischer-
                              weise bei Kollektionen verwendet.
@XmlElementWrapper            @XmlElementWrapper (
                                name = "##default",
                                namespace = "##default",
                                nillable = false
                              )

Tabelle 7: JAXB Feld-Annotations




                                            - 78 -
                       Datenhaltung - XML-Serialisierung über JAXB


4.4.2.5. Objekt-Fabrik-Annotations

Die folgenden Annotations zeichnen eine eigene Objekt-Fabrik aus, die Objekte für bestimm-
te XML-Elemente mit Daten instanzieren soll.

Annotation                   Beschreibung und Standardwerte
@XmlRegistry
                             Markiert eine Klasse als Objekt-Fabrik zur Instanzierung von
                             Objekten für XML-Elemente.
                             Ordnet eine Fabrik-Methode einem XML-Element zu.

                             @XmlElementDecl (
@XmlElementDecl                scope = GLOBAL.class,
                               namespace = "##default",
                               substitutionHeadNamespace = "##default",
                               substitutionHeadName = ""
                             )

Tabelle 8: JAXB Objekt-Fabrik-Annotations


4.4.2.6. Adapter-Annotations

Mit diesen Annotations werden eigene Adapter-Klassen für die XML-Verarbeitung definiert.

Annotation                   Beschreibung und Standardwerte
                             Verwendet eine Adapter-Klasse, die die Schnittstelle
                             XmlAdapter implementiert, um eine Java-Klasse manuell auf
                             das XML-Schema abzubilden.
@XmlJavaTypeAdapter
                             @XmlJavaTypeAdapter (
                               type = DEFAULT.class
                             )
@XmlJavaTypeAdapters         Faßt mehrere @XmlJavaTypeAdapter Annotations zusammen.

Tabelle 9: JAXB Adapter-Annotations



4.4.3. Anwendung der Annotations bei den Buchhaltungs-Klassen

Die beschrieben Annotations werden nun auf die bereits vorhandenen Klassen aus dem loka-
len Modul angewandt. Die Implementierung der Geschäftslogik wird direkt übernommen, und
es werden eine nötige Erweiterungen der Klassen durchgeführt.


4.4.3.1. Service-Klasse

Die Service-Klasse wird als @XmlRootElement gekennzeichnet. Wichtig ist die zusätzliche
Angabe der Ordnung der Elemente. Die Konten müssen vor den Buchungen verarbeitet wer-
den, damit es beim Einlesen nicht zu Ausnahmen kommt. Für die HashMaps werden Getter
und Setter-Methoden erzeugt, wodurch sie von JAXB bereits verarbeitet werden können. Au-
ßerdem ist noch ein Standard-Konstruktor ohne Argumente nötig. Die ID der nächsten Bu-
chung (nextId) wird als XML-Attribut (@XmlAttribute) gespeichert.

                                            - 79 -
                        Datenhaltung - XML-Serialisierung über JAXB

package de.fhr.vs_iw.jaxb.v1;

@XmlRootElement
@XmlType(propOrder = { "accountMap", "accountingEntryMap" })
public final class AccountingService implements IAccountingService {

    private   Map<Integer, AccountingEntry> accountingEntryMap
      = new   HashMap<Integer, AccountingEntry>();
    private   Map<Integer, Account> accountMap
      = new   HashMap<Integer, Account>();

    @XmlAttribute
    private int nextId = 0;

    @XmlID
    @XmlAttribute
    @SuppressWarnings("unused")
    private final String xmlid = "service";

    public AccountingService() {}

    public Map<Integer, AccountingEntry> getAccountingEntryMap();
    public Map<Integer, Account> getAccountMap();

    public void setAccountingEntryMap(
        Map<Integer, AccountingEntry> accountingEntryMap);
    public void setAccountMap(Map<Integer, Account> accountMap);

    ...
}

Code 48: Class AccountingService (JAXB - Variante 1)

Ein zusätzliches Datenfeld muß noch eingeführt werden, und zwar eine XML-ID, die später
als Referenz für den notwendigen Rückbezug der Buchungen zu einer Service-Instanz dient.
Diese ID muß ein String sein, wird als @XmlID gekennzeichnet und als Attribut gespeichert.


4.4.3.2. Buchungs-Klasse

Auch die bereits vorhandene Buchungsklasse wird im Prinzip direkt übernommen und ledig-
lich um einige Punkte erweitert.

package de.fhr.vs_iw.jaxb.v1;

public final class AccountingEntry implements IAccountingEntry {

    @XmlIDREF
    @XmlAttribute
    private AccountingService service = null;

    public AccountingEntry() {}

    public void setId(Integer id);
    public void afterUnmarshal(Unmarshaller u, Object parent);

    ...
}

Code 49: Class AccountingEntry (JAXB - Variante 1)

                                               - 80 -
                       Datenhaltung - XML-Serialisierung über JAXB

Durch @XmlIDREF wird der Bezug zur Service-Klasse im XML-Schema abgebildet. Anson-
sten muß nur noch ein Standard-Konstruktor und eine Setter-Methode für die ID hinzugefügt
werden. Da von der Buchungs-Schnittstelle her bereits für alle Felder öffentliche Getter- und
Setter-Methoden vorhanden sind, können alle Eigenschaften der Klasse bereits von JAXB
verarbeitet werden.
Da die Buchungs-Klasse als Wert-Typ in der Buchungs-HashMap der Service-Klasse verwen-
det wird, wird sie im XML-Schema implizit als XML-Typ (@XmlType) behandelt, auch ohne
daß die Annotation explizit vorhanden ist.


4.4.3.3. Callback-Methoden

Neben den semantischen Erweiterungen der Buchungs-Klasse ist auch noch eine rein funktio-
nale Erweitung notwendig. Da im lokalen Buchhaltungsmodul eine mehrfache Datenspeiche-
rung, der Buchungen, sowohl in der Service-Klasse, als auch in Soll- und Haben-Buchungen
getrennt beim jedem Konto erfolgt, muß auch bei den Konten diese Zuordnung, beim Einle-
sen der Buchhaltungsdaten aus einer XML-Datei, wiederhergestellt werden. Dazu wird eine
Callback-Methode verwendet.

public void afterUnmarshal(Unmarshaller u, Object parent)
    throws AccountingException {

((Account)service.getAccountByNumber(debitAccount)).addDebitEntry(this);
((Account)service.getAccountByNumber(creditAccount)).addCreditEntry(this);

}

Code 50: Methode afterUnmarshal()

Wenn diese Methode in einer Klasse vorhanden ist, so wird sie von JAXB immer nach dem
Erzeugen eines neuen Objekts dieser Klasse und dem Setzen aller Eigenschaften aufgerufen.
Diese Funktionalität wird in der Buchungs-Klasse genutzt, um die Buchung jeweils beim zu-
gehörigen Soll- und Haben-Konto einzutragen, genauso wie beim Erfassen einer neuen Bu-
chung.

Es gibt entsprechend noch weitere Callback-Methoden, die in einer Klasse für die Verarbei-
tung mit JAXB verwendet werden können, in diesem Fall aber nicht benötigt werden.

    // Wird direkt nach dem Instanzieren eines "leeren" Objekts aufgerufen
    public void beforeUnmarshal(Unmarshaller u, Object parent);

    // Wird vor dem Auslesen der Eigenschaften eines Objekts aufgerufen
    public boolean beforeMarshal(Marshaller m, Object parent);

    // Wird nach der Verarbeitung zu einem XML-Element aufgerufen
    public void afterMmarshal(Marshaller m, Object parent);

Das Object parent ist dabei immer eine zugeordnete Objektinstanz, die dem gerade verar-
beiteten Element im XML-Baum übergeordnet ist, und deren Verarbeitung je nach Aufbau
des Schemas und des aktuellen Vorgangs unter Umständen noch nicht abgeschlossen wurde.




                                           - 81 -
                        Datenhaltung - XML-Serialisierung über JAXB


4.4.3.4. Konto-Klasse

Die Konto-Klasse wird wie die Buchungs-Klasse nur übernommen und angepaßt. Da die
komplexen Funktionalitäten, die bei den Buchhaltungsdaten notwendig sind, bereits vollstän-
dig von der Buchungs-Klasse abgedeckt werden, ist hier kaum Aufwand nötig.

package de.fhr.vs_iw.jaxb.v1;

public final class Account implements IAccount {

    public Account() {}

    public void setNumber(Integer number);

    ...
}

Code 51: Class Account (JAXB - Variante 1)

Die Klasse wird um den fehlenden Standard-Konstruktor und die Setter-Methode für die Kon-
tonummer erweitert, was bereits ausreicht, damit alle Daten eines Kontos in das Schema auf-
genommen werden können.


4.4.4. Erzeugung des XSD-Schemas aus den annotierten Klassen

Eigentlich könnten Daten mit den so annotierten Klassen bereits in einer XML-Datei abgebil-
det werden. Günstiger ist es jedoch, zuerst ein XSD-Schema aus den Klassen erzeugen zu
lassen. Dadurch wird die Korrektheit der Annotations überprüft, und das erzeugte Schema
kann später Verwendet werden, um eine XML-Datei mit Buchhaltungsdaten vor dem Einle-
sen zu validieren.


4.4.4.1. Generierung durch einen Ant-Vorgang

Erzeugt   wird   das   Schema     durch      einen  entsprechenden      Ant-Vorgang
(com.sun.tools.jxc.SchemaGenTask), der in den JAXB-Bibliotheken enthalten ist.

<?xml version="1.0" standalone="yes"?>
<project basedir=".." default="generate-schema">
  <property file="${basedir}/ant/build.properties" />
  <target name="generate-schema">
    <path id="classpath">...</path>
    <taskdef name="schemagen" classname="com.sun.tools.jxc.SchemaGenTask"
             classpathref="classpath" />
    <delete file="${basedir}/generated/de/fhr/vs_iw/jaxb/schema-v1.xsd" />
    <schemagen srcdir="${basedir}/src/de/fhr/vs_iw/jaxb/v1/"
               destdir="${basedir}/generated/de/fhr/vs_iw/jaxb/">
      <schema namespace="" file="schema-v1.xsd" />
      <classpath refid="classpath" />
      <include name="AccountingService.java" />
    </schemagen>
  </target>
</project>

Resource 1: Ant-Build-Datei jaxb-generate-schema-v1.xml

                                               - 82 -
                     Datenhaltung - XML-Serialisierung über JAXB


4.4.4.2. Das Schema zu den HashMaps

Das erzeugte Schema bildet die Java-HashMaps jeweils als paarweise XML-Elemente <key>
und <value> mit dem entsprechenden XML-Typ ab.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="accountingService" type="accountingService"/>
  <xs:complexType name="accountingService" final="extension restriction">
    <xs:sequence>
      <xs:element name="accountMap">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="entry" minOccurs="0" maxOccurs="unbounded">
              <xs:complexType>
                <xs:sequence>
                  <xs:element name="key" minOccurs="0" type="xs:int"/>
                  <xs:element name="value" minOccurs="0" type="account"/>
                </xs:sequence>
              </xs:complexType>
            </xs:element>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
      <xs:element name="accountingEntryMap">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="entry" minOccurs="0" maxOccurs="unbounded">
              <xs:complexType>
                <xs:sequence>
                  <xs:element name="key" minOccurs="0" type="xs:int"/>
                  <xs:element name="value" minOccurs="0"
                               type="accountingEntry"/>
                </xs:sequence>
              </xs:complexType>
            </xs:element>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
    <xs:attribute name="xmlid" type="xs:ID"/>
  </xs:complexType>
  <xs:complexType name="account" final="extension restriction">
    <xs:sequence>
      <xs:element name="description" type="xs:string" minOccurs="0"/>
      <xs:element name="number" type="xs:int" minOccurs="0"/>
      <xs:element name="type" type="accountType" minOccurs="0"/>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="accountingEntry" final="extension restriction">
    <xs:sequence>
      <xs:element name="amount" type="xs:double" minOccurs="0"/>
      <xs:element name="creditAccount" type="xs:int" minOccurs="0"/>
      <xs:element name="debitAccount" type="xs:int" minOccurs="0"/>
      <xs:element name="id" type="xs:int" minOccurs="0"/>
      <xs:element name="text" type="xs:string" minOccurs="0"/>
    </xs:sequence>
    <xs:attribute name="service" type="xs:IDREF"/>
  </xs:complexType>



                                        - 83 -
                          Datenhaltung - XML-Serialisierung über JAXB

  <xs:simpleType name="accountType">
    <xs:restriction base="xs:string">
      <xs:enumeration value="PROCEEDS"/>
      <xs:enumeration value="PASSIVE"/>
      <xs:enumeration value="EXPENSE"/>
      <xs:enumeration value="AMOUNT"/>
      <xs:enumeration value="ACTIVE"/>
    </xs:restriction>
  </xs:simpleType>
</xs:schema>

Resource 2: schema-v1.xsd


4.4.4.3. Resultierende XML-Datei

Aus diesem Schema ergibt sich vom Aufbau her folgende XML-Datei.

<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
<accountingService xmlid="service">
    <accountMap>
        <entry>
            <key>2800</key>
            <value>
                <description>Bank</description>
                <number>2800</number>
                <type>ACTIVE</type>
            </value>
        </entry>

          ...

     </accountMap>
     <accountingEntryMap>
         <entry>
             <key>2</key>
             <value service="service">
                 <amount>100.0</amount>
                 <creditAccount>4400</creditAccount>
                 <debitAccount>6000</debitAccount>
                 <id>2</id>
                 <text>Kauf von Rohstoffen 1</text>
             </value>
         </entry>

          ...

    </accountingEntryMap>
</accountingService>

Resource 3: fibu-v1.xml


4.4.4.4. Probleme

Die HashMaps sind eine etwas problematische Angelegenheit, da die XML-Struktur unnötig
komplex wird. Außerdem hat die aktuelle Version 2 von JAXB einen Fehler, der erst in Ver-
sion 3 behoben werden soll, durch den HashMaps nur dann richtig verarbeitet werden, wenn
sie public (Feld oder Getter/Setter) und nicht mit Annotations versehen sind [72].

                                             - 84 -
                        Datenhaltung - XML-Serialisierung über JAXB



4.4.5. Implementierung der Klassen nach einem vorgegebenen Schema

Wegen den Problemen mit den HashMaps werden im zweiten Ansatz die Klassen auf eine
andere Weise neu implementiert, die für die Verarbeitung mit JAXB besser geeignet ist und
eine einfachere XML-Struktur ergibt.


4.4.5.1. Service-Klasse

Der wesentliche Aufbau der Service-Klasse entspricht der ersten Variante. Die Annotations
für JAXB (@XmlRootElement, @XmlType(propOrder...), @XmlID, @XmlAttribute) sind
gleichermaßen vorhanden, aber die HashMaps werden durch Set bzw. HashSet (möglich wäre
auch List) ersetzt und als @XmlElement ausgezeichnet.

package de.fhr.vs_iw.jaxb;

@XmlRootElement
@XmlType(propOrder = { "accountSet", "accountingEntrySet" })
public final class AccountingService implements IAccountingService {
  @XmlAttribute
  private int nextId = 0;

    @XmlID
    @XmlAttribute
    private String xmlid = "service";

    @XmlElement(name = "accountingEntry")
    protected Set<AccountingEntry> accountingEntrySet
      = new HashSet<AccountingEntry>();

    @XmlElement(name = "account")
    protected Set<Account> accountSet = new HashSet<Account>();

    public AccountingService() {}

    ...

    public IAccount getAccountByNumber(Integer number)
        throws AccountingException {
      checkAccountNumber(number, true);
      for (Account account : accountSet) {
        if (account.getNumber().equals(number)) {
          return account;
        }
      }
      return null;
    }

    ...
}

Code 52: Class AccountingService (JAXB - Variante 2)

Der Nachteil ist natürlich, daß fast alle Methoden neu implementiert werden müssen, und auf
Buchungs- und Kontenobjekte nicht mehr direkt über einen Identifikator zugegriffen werden
kann. Die beiden HashSets müssen jeweils manuell nach einem bestimmten Objekt in einer
Schleife durchsucht werden.

                                               - 85 -
                        Datenhaltung - XML-Serialisierung über JAXB


4.4.5.2. Konto-Klasse

Die Konto-Klasse wird im Vergleich zur vorigen Variante etwas stärker abgeändert. Sie soll
keine eigenen HashMaps für die Zuordnung der Soll- und Haben-Buchungen mehr enthalten,
um die redundante Verwaltung dieser Informationen zu vermeiden. Dafür erhält nun auch
diese Klasse einen Rückbezug (@XmlIDREF) zu einer Instanz der Service-Klasse.

package de.fhr.vs_iw.jaxb.v2;

public final class Account implements IAccount {

    @XmlIDREF
    @XmlAttribute
    private AccountingService service = null;

    public Account() {}

    public Set<Integer> getCreditEntries() {
      Set<Integer> credit = new HashSet<Integer>();
      for (AccountingEntry entry : service.accountingEntrySet) {
        if (entry.getCreditAccount().equals(number)) {
          credit.add(entry.getId());
        }
      }
      return credit;
    }

    public Set<Integer> getDebitEntries() {
      Set<Integer> debit = new HashSet<Integer>();
      for (AccountingEntry entry : service.accountingEntrySet) {
        if (entry.getDebitAccount().equals(number)) {
          debit.add(entry.getId());
        }
      }
      return debit;
    }

    ...
}

Code 53: Class Account (JAXB - Variante 2)

Zum Abruf der Soll- und Haben-Buchungen wird auf den Set der Buchungen in der Service-
Klasse zurückgegriffen und jeweils die entsprechenden Buchungen herausgesucht. Dies be-
deutet zwar zur Laufzeit einen etwas höheren Aufwand, vor allem wenn die Anzahl der Bu-
chungen zunimmt, aber wie bereits erwähnt soll das Ziel dieser Implementierung eine einfa-
chere XML-Struktur sein.


4.4.5.3. Buchungs-Klasse

Bei der neuen Buchungs-Klasse ändert sich am wenigsten. Hier wird lediglich die Callback-
Methode afterUnmarshall() wieder entfernt, da Aufgrund der nun nicht mehr vorhandenen
HashMaps in der Konto-Klasse die Buchungen nach dem Laden in den Set der Service-Klasse
nicht nochmals zugeordnet werden müssen.



                                             - 86 -
                        Datenhaltung - XML-Serialisierung über JAXB

package de.fhr.vs_iw.jaxb.v2;

public final class AccountingEntry implements IAccountingEntry {

    @XmlIDREF
    @XmlAttribute
    private AccountingService service = null;

    public AccountingEntry();

    public void setId(Integer id);

    ...
}

Code 54: Class AccountingEntry (JAXB - Variante 2)

Da beim Laden einer Buchung aus der XML-Datei die angegebenen Konten aber immer noch
in den Setter-Methoden überprüft werden, ist es weiterhin wichtig daß die Konten vor den
Buchungen in der XML-Datei stehen, was bei der Service-Klasse auch angegeben wurde.


4.4.5.4. Schema und Daten

Zur Erzeugung des neuen Schemas wird im Prinzip derselbe Ant-Vorgang wie vorher ver-
wendet (siehe 4.4.4.1) nur mit dem anderen Paket als Quelle (de.fhr.vs_iw.jaxb.v2) und
einem anderen Namen für die Schema-Datei (schema-v2.xsd). Das erzeugte Schema ist dann
durchaus weniger komplex als im vorigen Fall.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="accountingService" type="accountingService"/>
  <xs:complexType name="accountingService" final="extension restriction">
    <xs:sequence>
      <xs:element name="account" type="account" maxOccurs="unbounded"
                  minOccurs="0"/>
      <xs:element name="accountingEntry" type="accountingEntry"
                  maxOccurs="unbounded" minOccurs="0"/>
    </xs:sequence>
    <xs:attribute name="xmlid" type="xs:ID"/>
    <xs:attribute name="nextId" type="xs:int" use="required"/>
  </xs:complexType>
  <xs:complexType name="account" final="extension restriction">
    <xs:sequence>
      <xs:element name="description" type="xs:string" minOccurs="0"/>
      <xs:element name="number" type="xs:int" minOccurs="0"/>
      <xs:element name="type" type="accountType" minOccurs="0"/>
    </xs:sequence>
    <xs:attribute name="service" type="xs:IDREF"/>
  </xs:complexType>
  <xs:complexType name="accountingEntry" final="extension restriction">
    <xs:sequence>
      <xs:element name="amount" type="xs:double" minOccurs="0"/>
      <xs:element name="creditAccount" type="xs:int" minOccurs="0"/>
      <xs:element name="debitAccount" type="xs:int" minOccurs="0"/>
      <xs:element name="id" type="xs:int" minOccurs="0"/>
      <xs:element name="text" type="xs:string" minOccurs="0"/>
    </xs:sequence>
    <xs:attribute name="service" type="xs:IDREF"/>
  </xs:complexType>

                                              - 87 -
                          Datenhaltung - XML-Serialisierung über JAXB

  <xs:simpleType name="accountType">
    <xs:restriction base="xs:string">
      <xs:enumeration value="PROCEEDS"/>
      <xs:enumeration value="PASSIVE"/>
      <xs:enumeration value="EXPENSE"/>
      <xs:enumeration value="AMOUNT"/>
      <xs:enumeration value="ACTIVE"/>
    </xs:restriction>
  </xs:simpleType>
</xs:schema>

Resource 4: schema-v2.xsd

Es basiert nun nur noch auf den zugehörigen XML-Elementen und XML-Typen zu den drei
Klassen der Buchhaltung und kommt ohne zusätzliche oder zum Teil gar überflüssige Ele-
mente aus. Daraus ergibt sich nach dem Speichern von Buchhaltungsdaten eine XML-Datei
mit dem folgenden Aufbau.

<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
<accountingService nextId="12" xmlid="service">
    <account service="service">
        <description>Bank</description>
        <number>2800</number>
        <type>ACTIVE</type>
    </account>
    <account service="service">
        <description>Schlussbilanzkonto</description>
        <number>8010</number>
        <type>AMOUNT</type>
    </account>

     ...

     <accountingEntry service="service">
         <amount>10000.0</amount>
         <creditAccount>2400</creditAccount>
         <debitAccount>2800</debitAccount>
         <id>9</id>
         <text>Bezahlung aus Verkauf 3</text>
     </accountingEntry>
     <accountingEntry service="service">
         <amount>100.0</amount>
         <creditAccount>4400</creditAccount>
         <debitAccount>6000</debitAccount>
         <id>10</id>
         <text>Kauf von Rohstoffen 3</text>
     </accountingEntry>

     ...

</accountingService>

Resource 5: fibu-v2.xml

Diese XML-Datei enthält nun nur noch die wirklich relevanten Elemente für die Buchhal-
tungsdaten, was auch einen Kernpunkt bei der Arbeit mit JAXB darstellt. Die Philosophie von
JAXB geht eher in die Richtung, eine bestimmte XML-Struktur mit einem günstigen Schema
vorzugeben, und die Java-Klassen zur Verarbeitung der XML-Daten an das Schema anzupas-
sen und nicht umgekehrt. JAXB würde auch die Möglichkeit bieten die Klassen aus einem
XSD-Schema automatisiert zu generieren.
                                             - 88 -
                       Datenhaltung - XML-Serialisierung über JAXB



4.4.6. Proxy- und Utility-Klasse

Nachdem nun das XML-Schema und die Zuordnung der XML-Daten zu Java-Klassen und
Objekten definiert wurde, müssen die Daten noch eine XML-Datei gespeichert bzw. daraus
gelesen werden.


4.4.6.1. Proxy-Klasse

Eine angegebene XML-Datei mit Daten wird in der connect()-Methode der JAXB-Proxy-
Klasse ausgelesen, sofern sie vorhanden ist. Die Proxy-Klasse liefert eine Instanz der Service-
Klasse zurück, mit der dann das Buchhaltungssystem wie gewohnt arbeiten kann. Die Daten
werden nur im Speicher der VM gehalten. Erst in der close()-Methode werden die Instanz
der Service-Klasse und damit alle Daten in die XML-Datei zurückgeschrieben.

package de.fhr.vs_iw.jaxb.v1;

public final class JAXBProxy1 implements IAccountingProxy {
  private String filename = null;
  private AccountingService service = null;

    public JAXBProxy1() {}

    public void close() throws IOException {
      if (service != null && filename != null) {
        try {
          JAXBUtil.writeXML(AccountingService.class, service, filename);
        } catch (Exception e) {
          IOException ioe = new IOException();
          ioe.initCause(e);
          throw ioe;
        }
      }
    }

    public IAccountingService connect(String[] args) throws IOException {
      if (args.length <= 0) {
        throw new IllegalArgumentException("Ein Dateiname muß "
            + "als erstes Arguemnt angegeben werden");
      }
      filename = args[0];
      if (!(new File(filename).exists())) {
        service = new AccountingService();
      } else {
        try {
          service = JAXBUtil.readXML(AccountingService.class,
              filename, "schema-v1.xsd");
        } catch (Exception e) {
          IOException ioe = new IOException();
          ioe.initCause(e);
          throw ioe;
        }
      }
      return service;
    }
}

Code 55: Class JAXBProxy1

                                            - 89 -
                          Datenhaltung - XML-Serialisierung über JAXB

Die Klasse JAXBProxy2 der zweiten Implementierungs-Variante ist grundsätzlich völlig iden-
tisch, nur daß sie zu einem anderen Java-Paket gehört und daher die andere Service-Klasse,
eben der zweiten Variante verwendet.
Jede der beiden Proxy-Klassen verwendet die Methoden der Utility-Klassen zum eigentlichen
Zugriff auf die Daten.


4.4.6.2. Utility-Klasse

Die notwendigen Aufrufe der JAXB-Klassen zur Laufzeit werden in den Methoden der Klas-
se JAXBUtil ausgeführt.

package de.fhr.vs_iw.jaxb;

public final class JAXBUtil {
  public static <T> T readXML(Class<T> clas, String filename,
      String schemaname) throws SAXException, JAXBException {
  public static void writeXML(Class clas, Object object, String filename)
      throws FileNotFoundException, JAXBException {
  private static Schema getSchema(String schemaResourceName)
      throws SAXException {
}

Code 56: Class JAXBUtil

Die Methoden dieser Klasse werden von den Proxy-Klassen zur Verarbeitung der Buchhal-
tungsdaten in den XML-Dateien aufgerufen.


4.4.6.3. Speichern der Objekte

In der Methode writeXML() der Utility-Klasse werden die Daten Objekte der Buchhaltungs-
klassen in eine XML-Datei gespeichert.

public static void writeXML(Class clas, Object object,
    String filename) throws FileNotFoundException, JAXBException {

    JAXBContext cont = JAXBContext.newInstance(clas);
    Marshaller marshaller = cont.createMarshaller();
    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
    marshaller.setProperty(Marshaller.JAXB_ENCODING, "ISO-8859-1");

    PrintStream ps = new PrintStream(new File(filename));
    marshaller.marshal(object, ps);
    ps.close();
}

Code 57: Method writeXML() (JAXBUtil)

Zuerst wird der JAXB-Kontext mit der angegebenen Klasse (die Service-Klasse) instanziert.
Die angegebene Klasse wird verarbeitet, und Aufgrund ihres Aufbaus und mit den vorhande-
nen Annotations wird der Kontext konfiguriert. Aus dem Kontext kann ein Marshaller für
die Objekte der Klasse erzeugt werden, der die Umwandlung der Daten in das passende
XML-Format durchführt. Über einen OutputStream erfolgt die Ausgabe des Objekts in die
angegebene Datei.


                                             - 90 -
                       Datenhaltung - XML-Serialisierung über JAXB


4.4.6.4. Laden der Objekte

Zum Laden von Objekten mit den Daten aus einer XML-Datei enthält die Utility-Klasse die
Methode readXML().

public static <T> T readXML(Class<T> clas, String filename,
    String schemaname) throws SAXException, JAXBException {
  JAXBContext ctx = JAXBContext.newInstance(clas);
  Unmarshaller um = ctx.createUnmarshaller();
  if (schemaname != null) {
    um.setSchema(getSchema(schemaname));
  }
  return (T)um.unmarshal(new File(filename));
}

Code 58: Method readXML() (JAXBUtil)

Auch hier wird zuerst ein JAXB-Kontext für die angegebene Klasse konfiguriert. Über einen
Unmarshaller wird ein Objekt der angegebenen Klasse mit den Daten aus der angegebenen
XML-Datei erzeugt. Wenn der Name einer Schema-Datei angegeben wird, wird diese mit der
Methode getSchema() als Ressource geladen, und verwendet um die XML-Datei beim Einle-
sen zu validieren.

private static Schema getSchema(String schemaResourceName)
    throws SAXException {
  SchemaFactory sf = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI);
  return sf.newSchema(JAXBUtil.class.getResource(schemaResourceName));
}

Code 59: Method getSchema() (JAXBUtil)

Die Schema-Datei wird als Ressource in der Umgebung der Virtuellen Maschine gesucht und
über eine Instanz der Schema-Fabrik als Objekt zur weiteren Verwendung geladen.


4.4.7. Fazit

Die direkte Verwendung von Java-Objekten, die durch Annotations ausgezeichnet werden,
stellt für den Entwickler eine einfach zu handhabende Möglichkeit zur Datenspeicherung dar.
Allerdings kann in diesem Fall die Tatsache, daß die Datenverarbeitung eigentlich ausgehend
von einer XML-Datei und den zugehörigen Schema erfolgt, die Flexibilität beim Aufbau der
eigenen Java-Klassen einschränken.
Außerdem ist die Datenspeicherung im XML-Format zwar sehr flexibel, und die Daten kön-
nen fast beliebig weiterverarbeitet werden, aber bei großen Datenmengen auch sehr ineffi-
zient, und man muß sich um den Lade- und Speichervorgang, und die Datenintegrität, selbst
kümmern.




                                          - 91 -
                 Datenhaltung - Direkter SQL-Datenbankzugriff mit JDBC


4.5. Direkter SQL-Datenbankzugriff mit JDBC
4.5.1. Allgemeines

Das Ziel jeder Datenhaltung ist heute aber in der Regel ein SQL-Datenbanksystem. Auch die
Buchhaltungsdaten sollen in einer Datenbank gespeichert werden. Zum Zugriff auf eine SQL-
Datenbank unter Java ist der entsprechende JDBC-Treiber, in unserem Beispiel für das
MySQL-Datenbanksystem [11], nötig, der unter

   http://dev.mysql.com/downloads/connector/j/5.0.html

heruntergeladen werden kann (siehe auch 6.2.3.2), und in den aktuellen classpath aufge-
nommen werden muß. Auf den Betrieb des MySQL-Datenbanksystems an sich soll hier aber
nicht näher eingegangen werden, da es den Umfang dieses Tutorials übersteigen würde.


4.5.2. JDBC-Datenbankanbindung

Der Zugriff auf Datenbank erfolgt bei Java über einheitlich über die JDBC-Schnittstelle (Ja-
va Database Connectivity) [37]. Für die jeweils gewünschte Datenbank muß nur der passende
JDBC-Treiber vorhanden sein.


4.5.2.1. Verbindung aufbauen

Die Proxy-Klasse baut über die Klasse java.sql.Connection eine Verbindung zum
MySQL-Datenbanksystem auf. Dazu wird eine URL der Form

   java:mysql://host[:port]/datenbankname

ein Benutzername und ein Paßwort angegeben. Der DriverManager liefert mit diesen Anga-
ben durch Aufruf von getConnection() eine Instanz von Connection.

package de.fhr.vs_iw.mysql;

public final class MySQLProxy implements IAccountingProxy {
  private Connection con = null;
  public MySQLProxy() {}

  public IAccountingService connect(String[] args) throws IOException {
    if (args.length < 4) {
      throw new IllegalArgumentException(
          "use MySQLProxy host[:port] database user password");
    }
    try {
      Class.forName("com.mysql.jdbc.Driver");
      con = DriverManager.getConnection(
         "jdbc:mysql://" + args[0] + "/" + args[1], args[2], args[3]);
    } catch (Exception e) {
      IOException ioe = new IOException(e.getMessage());
      ioe.initCause(e);
      throw ioe;
    }
    return new AccountingService(con);
  }


                                           - 92 -
                 Datenhaltung - Direkter SQL-Datenbankzugriff mit JDBC

    public void close() throws IOException {
      try {
        con.close();
        con = null;
      } catch (SQLException e) {
        IOException ioe = new IOException(e.getMessage());
        ioe.initCause(e);
        throw ioe;
      }
    }
}

Code 60: Class MySQLProxy

Durch den Aufruf von Class.forName("com.mysql.jdbc.Driver") wird der MySQL-
Treiber zuvor geladen, damit er zum Aufbau der Verbindung verfügbar ist. Bei JDBC-
Zugriffen wird bei Fehlern eine SQLException geworfen, die hier in eine IOException ver-
packt wird, um der Schnittstellen-Spezifikation zu entsprechen (siehe 2.3.2). Die Verbindung
(Connection) wird einer neuen Instanz der Service-Klasse übergeben, damit darüber die
SQL-Anfragen und Anweisungen ausgeführt werden können.


4.5.2.2. Einzelne Werte

Für Anfragen nach einzelnen Werten wird die Service-Klasse um eine parametrische Query-
Methode erweitert, die für Anfragen an die Datenbank gedacht ist, die einen einzelnen Wert
als Ergebnis zurückliefern (SELECT ... WHERE ...).

protected synchronized <T> T query(Class<T> clas, String query,
    Object... args) throws IOException {
  try {
    PreparedStatement stmt = prepare(query, args);
    ResultSet rs = stmt.executeQuery();
    T result = null;
    if (rs.next()) {
      result = (T)rs.getObject(1);
    }
    rs.close();
    stmt.close();
    return result;
  } catch (SQLException e) {
    IOException ioe = new IOException(e.getMessage());
    ioe.initCause(e);
    throw ioe;
  }
}

Code 61: Method query() (MySQL)

Die Funktion liefert den ersten Wert der ersten Ergebniszeile einer Abfrage mit dem Typ der
angegebenen Klasse (Class<T>) zurück. Beim Aufruf von getObject() eines ResultSet
wird ein Java-Objekt, das zum Datentyp der angefragten Spalte einer Tabelle in der Daten-
bank paßt, zurückgegeben. Wenn die SQL-Anfrage passend formuliert wird, erlaubt dies ei-
nen effizienten Zugriff auf einzelne spezifische Werte in der Datenbank.




                                           - 93 -
                  Datenhaltung - Direkter SQL-Datenbankzugriff mit JDBC


4.5.2.3. Auflistungen

Die Abfrage von einzelnen Werten ist jedoch nicht ganz ausreichend, um die spezifizierten
Funktionalitäten der Buchhaltung abzudecken. Auch eine Auflistung mit mehreren Ergebnis-
sen (SELECT ...) muß abgefragt werden können, wozu eine eigene Query-Methode dient, die
alle Zeilen eines ResultSet verarbeitet.

protected synchronized <T> HashSet<T> querySet(Class<T> clas, String query,
    Object... args) throws IOException {
  try {
    PreparedStatement stmt = prepare(query, args);
    ResultSet rs = stmt.executeQuery();
    HashSet<T> set = new HashSet<T>();
    while (rs.next()) {
      set.add((T)rs.getObject(1));
    }
    rs.close();
    stmt.close();
    return set;
  } catch (SQLException e) {
    IOException ioe = new IOException(e.getMessage());
    ioe.initCause(e);
    throw ioe;
  }
}

Code 62: Method querySet() (MySQL)

Die Methode liefert einen HashSet<T> mit Elementen des angegebenen Typs zurück. Dazu
werden die Werte aus der ersten Spalte aller Ergebniszeilen einer entsprechend formulierten
Anfrage verwendet.


4.5.2.4. Anweisungen

Schließlich gibt ein noch SQL-Befehle, die kein Ergebnis liefern (INSERT, UPDATE, DELETE).
Für diese Art von Anweisungen wird eine andere Methode verwendet.

protected synchronized void execute(String query, Object... args)
    throws IOException {
  try {
    PreparedStatement stmt = prepare(query, args);
    stmt.execute();
    stmt.close();
  } catch (SQLException e) {
    IOException ioe = new IOException(e.getMessage());
    ioe.initCause(e);
    throw ioe;
  }
}

Code 63: Method execute() (MySQL)




                                          - 94 -
                  Datenhaltung - Direkter SQL-Datenbankzugriff mit JDBC


4.5.2.5. PreparedStatements

Alle SQL-Befehle werden über die JDBC-Klasse PreparedStatement ausgeführt, wodurch
beliebige Java-Objekte als SQL-Parameter verwendet werden.

private PreparedStatement prepare(String query, Object... args)
    throws SQLException {
  PreparedStatement stmt = con.prepareStatement(query);
  for (int i = 0; i < args.length; i++) {
    stmt.setObject(i + 1, args[i]);
  }
  return stmt;
}

Code 64: Method prepare() (MySQL)

Der Query-String sollte formuliert werden, daß er Platzhalter ? für Parameter enthält, z.B.

   UPDATE accountingentry SET text=? WHERE id=?

die dann der Reihe nach mit den Werten der angegebenen Objekte gefüllt werden. Die Me-
thode setObject() konvertiert die Java-Objekte nach internen Vorgaben in SQL-
Datentypen. Eine SQL-Anweisung muß natürlich nicht zwingend Parameter enthalten.


4.5.3. SQL-Anweisungen

Da nun die notwendigen Zugriffsfunktionen erstellt wurden, können die Daten des Buchhal-
tungssystems abgefragt und verändert werden. Die Daten stammen dann immer direkt aus der
Datenbank, die Geschäftslogik bleibt in den Klassen implementiert.


4.5.3.1. Datenbank-Schema

Zuerst muß ein Datenbank-Schema für die Buchhaltung erstellt werden. Es ist jeweils eine
Tabelle für Konten und eine Tabelle für Buchungen nötig, die mit den entsprechenden SQL-
Befehlen angelegt werden.

CREATE TABLE `account` (
  `number` int(10) NOT NULL,
  `accounttype` int(10) default NULL,
  `description` varchar(255) default NULL,
  PRIMARY KEY (`number`)
) Type=MyISAM;

CREATE TABLE `accountingentry` (
  `id` int(10) NOT NULL auto_increment,
  `text` varchar(255) default NULL,
  `amount` double default NULL,
  `creditaccount` int(10) default NULL,
  `debitaccount` int(10) default NULL,
  PRIMARY KEY (`id`)
) Type=MyISAM;

Resource 6: SQL-Befehle zum Anlegen der Konten- und Buchungs-Tabelle

                                             - 95 -
                  Datenhaltung - Direkter SQL-Datenbankzugriff mit JDBC


4.5.3.2. Service-Klasse

Jede Methode der Service-Klasse führt SQL-Abfragen aus, und arbeitet immer direkt mit den
Daten der Datenbank.

   •   calculateBalance()
         SELECT SUM(amount) FROM accountingentry
           WHERE debitaccount=? GROUP BY debitaccount
         SELECT SUM(amount) FROM accountingentry
           WHERE creditaccount=? GROUP BY creditaccount

   •   createAccount()
         INSERT INTO account SET number=?, accounttype=?, description=?

   •   createAccountingEntry()
         LOCK TABLES accountingentry WRITE
         INSERT INTO accountingentry
           SET debitaccount=?, creditaccount=?, amount=?, text=?
         SELECT LAST_INSERT_ID() FROM accountingentry
         UNLOCK TABLES

   •   getAccountingEntries()
         SELECT id FROM accountingentry

   •   getAccounts()
         SELECT number FROM account

   •   removeAccount()
         DELETE FROM account WHERE number=?

   •   removeAccountingEntry()
         DELETE FROM accountingentry WHERE id=?

Resource 7: SQL-Befehle der Service-Klasse (MySQL)

Bei der Methode createAccountingEntry() sind besondere Maßnahmen nötig, da die ID
einer neuen Buchung von der Datenbank vergeben wird. Zuerst wird die Tabelle der Buchun-
gen gesperrt, damit keine anderen Befehle dazwischen kommen können. Nach dem Einfügen
der neuen Buchung kann mit der MySQL-Funktion LAST_INSERT_ID() die ID der neuen Bu-
chung abgerufen werden. Danach wird die Tabelle wieder entsperrt.
Die Konten- und Buchungsobjekte werden wie vorher schon in HashMaps verwaltet und je-
weils für eine bestimmte Nummer bzw. ID erzeugt und zurückgegeben.

AccountingEntry entry = accountingEntries.get(id);
if (entry == null) {
  entry = new AccountingEntry(this, id);
  accountingEntries.put(id, entry);
}
return entry;

Account account = accounts.get(number);
if (account == null) {
  account = new Account(this, number);
  accounts.put(number, account);
}
return account;

Code 65: Verwaltung der Konten- und Buchungsobjekte (MySQL)

                                             - 96 -
                  Datenhaltung - Direkter SQL-Datenbankzugriff mit JDBC


4.5.3.3. Konto-Klasse

Allerdings speichert ein Objekt der Konto-Klasse nicht wie in den vorherigen Abschnitten
seine Daten selbst, sondern lediglich die Kontonummer. Alle Daten werden direkt aus der
Kontentabelle der Datenbank geholt und auch wieder dort abgelegt, wobei die Kontonummer
als Schlüssel (... WHERE number=?) dient.

   •   getCreditEntries()
         SELECT id FROM accountingentry WHERE creditaccount=?

   •   getDebitEntries()
         SELECT id FROM accountingentry WHERE debitaccount=?

   •   getDescription()
         SELECT description FROM account WHERE number=?

   •   getType()
         SELECT accounttype FROM account WHERE number=?

   •   setDescription()
         UPDATE account SET description=? WHERE number=?

   •   setType()
         UPDATE account SET accounttype=? WHERE number=?

Resource 8: SQL-Befehle der Konto-Klasse (MySQL)

Neben der Kontonummer enthält jedes Kontenobjekt auch einen Rückbezug zur Service-
Klasse, um die drei Abfrage-Methoden aufrufen zu können.


4.5.3.4. Buchungs-Klasse

Auch ein Buchungsobjekt speichert selbst nur die ID und greift damit auf die Datenbank zu.

   •   getAmount()
         SELECT amount FROM accountingentry WHERE id=?

   •   getCreditAccount()
         SELECT creditaccount FROM accountingentry WHERE id=?

   •   getDebitAccount()
         SELECT debitaccount FROM accountingentry WHERE id=?

   •   getText()
         SELECT text FROM accountingentry WHERE id=?

   •   setAmount()
         UPDATE accountingentry SET amount=? WHERE id=?

   •   setCreditAccount()
         UPDATE accountingentry SET creditaccount=? WHERE id=?

   •   setDebitAccount()
         UPDATE accountingentry SET debitaccount=? WHERE id=?



                                            - 97 -
                  Datenhaltung - Direkter SQL-Datenbankzugriff mit JDBC

   •   setText()
         UPDATE accountingentry SET text=? WHERE id=?

Resource 9: SQL-Befehle der Buchungs-Klasse (MySQL)

Die Daten stammen immer direkt aus der Buchungstabelle der Datenbank und werden bei
Änderungen auch wieder direkt dort gespeichert. Darüber hinaus ist auch nur wieder der
Rückbezug zur Service-Klasse, zum Aufruf der Abfrage-Methoden, als Feld vorhanden.


4.5.4. Fazit

Auf dem MySQL-Datenbanksystem und der JDBC-Schnittstelle basierend lassen sich die
Daten ordentlich und sicher speichern und abrufen. Auch der gleichzeitige Zugriff durch meh-
rere Clients auf die Daten ist möglich wodurch sich bereits ein erster Ansatz eines verteilten
Systems ergibt, das nicht explizit aufgebaut werden muß.
Nachteilig ist allerdings, daß die SQL-Abfragen manuell angegeben und fest im Programm-
code enthalten sind. Wenn sich Änderungen am Aufbau der Daten ergeben, müssen die SQL-
Anweisungen und das Datenbank-Schema manuell angepaßt werden.
Und auch die Portabilität ist eingeschränkt, da durch die JDBC-Schnittstelle zwar eine ein-
heitliche Schnittstelle zum Zugriff auf verschiedene Datenbanksysteme bereitgestellt wird,
aber die unterstützten SQL-Anweisungen doch unterschiedlich sein können, obwohl SQL
eigentlich standardisiert ist..
Deshalb muß an dieser Stelle noch etwas mehr Aufwand betrieben werden, um eine umfas-
sende und einfach verwendbare Datenhaltung zu realisieren, die vom verwendeten Daten-
banksystem unabhängig ist oder aber auf die speziellen Eigenheiten der verschiedenen Daten-
banksysteme eingehen kann.




                                            - 98 -
                                     Datenhaltung - Hibernate


4.6. Hibernate
Da das Hibernate-Persistenz-Framework [27] auch im JBoss-Application-Server (siehe 6.2)
integriert ist, und später bei den Enterprise Java Beans (siehe 6.3) auch wieder verwendet
wird, soll hier auf die Funktionalität ausführlicher eingegangen werden, als bei den vorheri-
gen Themen.


4.6.1. Allgemeines

                                            Hibernate ist ein Persistenz-Framework, das zwi-
            Anwendung                       schen einer Anwendung und der darunterliegenden
                                            JDBC-Datenbank [37] als zusätzliche Abstraktions-
            Persistente                     Schicht fungiert und einem Entwickler einen Groß-
             Objekte                        teil der Aufgaben bezüglich der Datenhaltung ab-
                                            nimmt.
             Hibernate
                                            Dabei werden auch andere etablierte Java-Standards
  hibernate.        XML-Mapping             wie JTA (Java Transaction API) [51] für Transak-
  properties         Annotations            tionen und JNDI (Java Naming and Directory Inter-
                                            face) [17] zur Verbindung mit einer Datenbank un-
             Datenbank                      terstützt.


Tabelle 10: Einbindung von Hibernate als zusätzliche Schicht

Das Hibernate-Framework ist sehr flexibel, und man kann mehr oder weniger Funktionalitä-
ten der API verwenden, und entsprechend den fehlenden Teil in der eigenen Anwendung im-
plementieren. Bei vollständiger Verwendung von Hibernate wird die eigene Anwendung voll-
ständig von der Datenbank abstrahiert, wobei man jedoch immer noch jederzeit direkt auf die
Datenbank zugreifen kann.


              Transiente Objekte                                 Anwendung

                                                           Persistente
                                                            Objekte
                SessionFactory
                                                                               Transaction
                                                               Session
 TransactionFactory       ConnectionProvider

             JNDI                                JDBC                           JTA


                                             Datenbank


Tabelle 11: Schematisher Aufbau der Hibernate-Komponenten

Die Funktion und Verwendung der einzelnen Komponenten wird im Folgenden schrittweise
näher erläutert.

                                                - 99 -
                                  Datenhaltung - Hibernate


4.6.1.1. Persistenz von Objekten

Die Funktionalität von Hibernate ist auf den objektorientierten Aufbau von Java ausgelegt, so
daß man die meiste Zeit nur mit Java-Objekten arbeitet und sich um die genauen Vorgänge
zum Zugriff auf die Datenbank, wie Transaktionen und SQL-Anweisungen, nach der einmali-
gen Konfiguration von Hibernate nicht mehr kümmern muß. Hibernate übernimmt das Spei-
chern der Objekte und stellt diese später wieder zur Verfügung. Auch Sammlungen von Ob-
jekten und komplexe Objekthierarchien können von Hibernate verwaltet und gespeichert wer-
den.


4.6.1.2. Arbeitsweise mit POJOs

Hibernate arbeitet mit beliebigen Java-Klassen, die wenig spezielle Anforderungen (z.B. Im-
plementierung einer bestimmten Schnittstelle) erfüllen müssen (POJO - Plain Old Java Ob-
ject), sich aber an folgende Vorgaben halten können bzw. sollten, um ein optimales Verhalten
bei der Verwendung mit Hibernate zu erzielen [67]:

   •   Einen Standard-Konstruktor ohne Parameter sollte eine persistente Klasse definieren,
       damit Hibernate ein Objekt über Constructor.newInstance() instanzieren kann.
       Der Konstruktor muß dafür nicht einmal als public deklariert sein.

   •   Außerdem ist es sinnvoll jeder Klasse einen Identifikator mitzugeben, der (unter Um-
       ständen unabhängig von der Geschäftslogik) später als Primär-Schlüssel in der Daten-
       bank dient. Ein solcher Identifikator ist aber auf jeden Fall optional, d.h. man kann es
       auch Hibernate überlassen, die Objekte intern zu identifizieren. Bestimmte Funktiona-
       litäten sind allerdings nur für Klassen verfügbar, die einen Identifikator definieren
       (Transitive Persistenz, siehe 4.6.2.6, Session.saveOrUpdate(), Session.merge(),
       siehe 4.6.5.2).

   •   In diesem Sinne ist auch das Überschreiben der euqals() und hashCode() Methoden
       sinnvoll (in speziellen Fällen sogar erforderlich), damit sich die Objekte richtig Ver-
       halten (z.B. in einem Set). Dabei sollte man sich jedoch nicht unbedingt auf die Iden-
       tifikatoren beziehen, die auch als Datenbank-Schlüssel dienen bzw. von Hibernate
       vergeben werden, sondern eigene Schlüssel und Vergleichsmöglichkeiten im Sinne
       der gewünschten Geschäftslogik verwenden.

   •   Es bietet sich an, Zugriffmethoden auf die Felder der Klasse (Getter/Setter) im Java-
       Bean-Style (z.B. getBla(), isBla(), setBla()) zu definieren, die dann von Hiberna-
       te automatisch verwendet werden. Man kann Hibernate aber auch anweisen, direkt auf
       die Felder zuzugreifen.

   •   Eine Klasse sollte nicht als final deklariert sein, oder aber alle Zugriffsmethoden ü-
       ber eine Schnittstelle implementieren, damit Hibernate zur Laufzeit dynamisch Proxy-
       Klassen dafür (bzw. für die Schnittstelle) erzeugen kann, zur Verwendung von "lazy
       association fetching" (wird hier nicht näher beschrieben, siehe 6.3.3.3).




                                           - 100 -
                                     Datenhaltung - Hibernate


4.6.1.3. OR-Mapping

Der Kernpunkt von Hibernate ist allerdings das OR-Mapping, die Objekt/relationale Zuord-
nung der Java-Klassen zu den Tabellen in einer relationalen Datenbank. Diese Zuordnung ist
auf Java bezogen und baut auf den persistenten Klassen auf, nicht auf den Tabellen der Da-
tenbank.
Dabei werden die Felder von persistenten Klassen, ihre Beziehungen zueinander, und die ge-
wünschte Abbildung in die Datenbank abstrakt beschrieben. Es wird festgelegt wie Beziehun-
gen oder Vererbungshierarchien gespeichert werden, ob bei Sammlungen von Objekten, alle
Objekte zusammen automatisch gespeichert werden sollen, oder ob jedes Objekt für sich ma-
nuell persistiert werden muß.
Die Beschreibung des OR-Mapping kann entweder in einer separaten XML-Datei stehen,
oder aber durch Annotations [65] direkt im Quellcode erfolgen.


4.6.2. Annotations

Die speziellen Annotations zur Beschreibung von Persistenz werden durch die Enterprise Ja-
vaBeans 3.0 Spezifikation (JSR-220 - EJB3) [66] und die Java Persistence API (JPA) im Pa-
ket javax.persistence definiert. Hibernate unterstützt dabei alle Aspekte dieser Spezifika-
tion vollständig, und darüber hinaus noch eigene Erweiterungen [68] auf die hier aber nicht
weiter eingegangen wird.


4.6.2.1. Entitäten

Durch die Annotation @Entity wird eine POJO-Klasse als persistente Klasse (Entitiy-Bean)
deklariert. Normal würden die Klassen dann in einer Tabelle der Datenbank mit demselben
Namen gespeichert. Dies kann durch die zusätzliche Angabe von @Table angepaßt werden.

@Entity
@Table(name = "Konten")
public class Account {
  Long number;

    @Id
    public Long getNumber() {
      return number;
    }

    public void setNumber(Long number) {
      this.number = number;
    }
}

Code 66: Class Account (Hibernate-Beispiel)


4.6.2.2. Identifikatoren - Schlüssel

Die Kontonummer wird im obigen Beispiel durch @Id als Primär-Schlüssel definiert und muß
von der eigenen Anwendung vergeben werden. Man kann den Identifikator aber auch von
Hibernate erzeugen lassen, durch die Annotation @GeneratedValue.

                                              - 101 -
                                 Datenhaltung - Hibernate

Dafür gibt es mehrere mögliche Strategien zur Erzeugung des Wertes:

   •   TABLE     benutzt einen @TableGenerator für den Identifikator
   •   IDENTITY die Datenbank erzeugt eine Identitäts-Spalte
   •   SQEUENCE fortlaufende Numerierung
   •   AUTO      automatische Auswahl von TABLE, IDENTITY oder SEQUENCE ja nach
                 zugrundeliegender Datenbank, z.B:
          o MySQL IDENTITY mit Spalten-Eigenschaft auto_increment
          o Resin     IDENTITY mit Spalten-Eigenschaft auto_increment
          o Postgres SEQUENCE
          o Oracle    SEQUENCE

   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   public Long getId() {
     return id;
   }

Allerdings ist es am günstigsten den Typ AUTO zu verwenden, damit die Anwendung für ver-
schiedene Datenbanksysteme portabel bleibt.


4.6.2.3. Einfache Daten

Von allen als @Entity gekennzeichneten Klassen ließt Hibernate die zu speichernden Felder
aus. Standardmäßig werden alle Eigenschaften mit Getter-Methode verarbeitet, wobei man
bei jeder dieser Methoden durch Annotations explizit angeben kann, wie das zugehörige Da-
tum behandelt werden soll.

   •   @Basic              das Feld wird einer Spalte in der Datenbank mit dem passenden
                           Datentyp zugeordnet
   •   @Transient          das Feld wird von Hibernate ignoriert
   •   @Lob                Large Object - das Feld wird als CLOB oder serialisiert als BLOB in
                           der Datenbank gespeichert
   •   @Temporal           für Datums- und Zeitangaben
   •   @Enumerated         für Java-Enumerationen
   •   Keine Annotation    wird behandelt wie @Basic

   @Basic
   public Integer getNumber();

   @Transient
   public Set<Integer> getDebitAccounts();

Zusätzlich kann bei den obigen Annotations (außer bei @Transient, dafür aber bei @Id und
@Version) durch @Column die Spalte manuell genauer angegeben werden. Die Parameter für
diese Annotation orientieren sich an gängigen Eigenschaften von Spalten in einer Datenbank.

   •   name = "columnName";                          der Name der Spalte
   •   boolean unique default false;                 ob die Werte Spalte einzigartig sein
                                                     müssen (Unique-Constraint)


                                           - 102 -
                                     Datenhaltung - Hibernate


    •   boolean nullable default true;                     ob die Spalte keinen Wert (d.h. NULL)
                                                           enthalten darf
    •   boolean insertable default true;                   ob die Spalte bei INSERT-
                                                           Anweisungen verwendet wird
    •   boolean updatable default true;                    ob die Spalte in UPDATE-
                                                           Anweisungen angegeben wird
    •   String columnDefinition default "";                DDL-Anweisung zum Erzeugen der
                                                           Spalte
    •   String table default "";                           Tabelle zu der die Spalte gehört
    •   int length default 255;                            maximale Länge des Inhalts
    •   int precision default 0;                           dezimale Genauigkeit
    •   int scale default 0;                               dezimaler Maßstab

    @Id
    @Column(name = "Buchungs-ID")
    public Integer getId();

    @Basic
    @Column(unique = true, nullable = false, length = 42)
    public String getDescription();

Hibernate kann ohne weitere Angaben die meisten Standard-Java-Datentypen (primitive Ty-
pen, viele der JDK-Klassen, Kollektionen) verarbeiten.


4.6.2.4. Beziehungen

Falls eine persistente Klasse ein Feld enthält, das den Typ einer anderen persistenten Klasse
hat, so haben diese beiden Entitäten eine Beziehung zueinander. Die einfachste Form ist eine
1:1-Beziehung von zwei Objekten der beiden Klassen zueinander, die mit der Annotation
@OneToOne angegeben wird.

@Entity
public class Customer {
  @Id
  public Long getId() {
    return id;
  }
  @OneToOne
  @PrimaryKeyJoinColumn
  public Account getAccount() {
    return account;
  }
  ...
}

@Entity
public class Account {
  @Id
  public Long getNumber() {
    return number;
  }
  ...
}

Code 67: Beispiel für 1:1-Beziehung mit Primär-Schlüssel

                                               - 103 -
                                     Datenhaltung - Hibernate

Die zusätzliche Angabe von @PrimaryKeyJoinColumn bedeutet, daß der Primär-Schlüssel
(@Id) von Account für die Zuordnung zu Customer verwendet wird, d.h. daß zusammengehö-
rige Objekte beider Klassen denselben Wert für den Primär-Schlüssel erhalten.
Im Gegensatz dazu kann auch ein zusätzlicher Fremd-Schlüssel für die Zuordnung eingeführt
werden, der auf einer Seite der bidirektionalen Beziehung durch @JoinColumn deklariert wird,
damit die Primär-Schlüssel der Klassen unabhängig voneinander vergeben werden können.

@Entity
public class Customer {
  @OneToOne
  @JoinColumn(name = "account_fk")
  public Account getAccount() {
    return account;
  }
  ...
}

@Entity
public class Account {
  @OneToOne(mappedBy = "account")
  public Account getAccount() {
    return account;
  }
  ...
}

Code 68: Beispiel für 1:1-Beziehung mit Fremd-Schlüssel

Auf der anderen Seite der bidirektionalen Beziehung wird die Annotation @OneToOne mit dem
Parameter mappedBy verwendet, der angibt, welches Feld der ersten Klasse die Beziehung
aufbaut. Diese Beziehung könnte jederzeit auch umgekehrt definiert werden.


4.6.2.5. Kollektionen

Im nächsten Schritt werden 1:n und n:n-Beziehungen betrachtet, die durch die Java-Klassen
Collection, List, Set und Map realisiert werden, wenn deren enthaltene Elemente wie bei
1:1-Beziehungen vom Typ einer persistenten Klasse sind. Kollektionen mit primitiven Typen
oder Basis-Klassen werden dabei aber nicht unterstützt.


Semantik      Java Darstellung              Annotation
              java.util.List                @OneToMany oder @ManyToMany
Bag           java.util.Collection          (u.U. mit @CollectionId)
List          java.util.List                @OneToMany oder @ManyToMany

Set           java.util.Set                 @OneToMany oder @ManyToMany

Map           java.util.Map                 @MapKey


Tabelle 12: Hibernate-Beziehungs-Semantik




                                               - 104 -
                                      Datenhaltung - Hibernate

Auch hier ist die Einführung eines Fremd-Schlüssels, wie oben bereits durchgeführt, notwen-
dig. Ohne die Angabe von @JoinColumn wird der Name des Schlüssels von Hibernate auto-
matisch aus den Namen der beteiligten Entitäten erzeugt.

@Entity
public class Account {
  @OneToMany(mappedBy = "account")
  public Set<AccountingEntry> getAccountingEntries() {
    return accountingEntries;
  }
  ...
}

@Entity
public class AccountingEntry {
  @ManyToOne
  public Account getAccount() {
    return account;
  }
  ...
}

Code 69: Beispiel für 1:n-Beziehung

Die Klasse Account enthält eine Kollektionen von mehreren Objekten der Klasse
AccountingEntry, so daß sich die beiden Entitäten in einer 1:n-Beziehung zueinander befin-
den, die durch die Annotations @OneToMany bzw. @ManyToOne auf der jeweiligen Seite der
Beziehung definiert wird. Durch mappedBy wird wieder die zuordnende Eigenschaft auf der
referenzierten Seite angegeben.

Bei einer n:n-Beziehung zweier persistenter Klassen verhält es sich ähnlich, nur daß in der
Datenbank dafür eine separate Tabelle und ein zugehöriger Fremdschlüssel bei jeder der Enti-
täten notwendig sind.

@Entity
public class Store {
  @ManyToMany
  public Set<Customer> getCustomers() {
    ...
  }
}

@Entity
public class Customer {
  @ManyToMany(mappedBy = "customers")
  public Set<Store> getStores() {
    ...
  }
}

Code 70: Beispiel für n:n-Beziehung

Durch die Annotation @ManyToMany, die auch durch Parameter wie mappedBy oder cascade
ergänzt werden kann, wird die Beziehung etabliert. Durch eine weitere Annotation
@JoinTable kann die zusätzlich erzeugte Tabelle manuell genau definiert werden [68].




                                              - 105 -
                                 Datenhaltung - Hibernate


4.6.2.6. Kaskadierung

Durch Kaskadierung, auch transitive Persistenz genannt, kann Hibernate angewiesen werden
alle Objekte der Objekthierachie, von durch Beziehungen verbundenen persistenten Klassen,
automatisch zu speichern. Dabei kann durch den Parameter cascade bei @OneToOne,
@OneToMany und @ManyToMany genau angegeben werden, in welchen Fällen die referenzierten
Objekte automatisch verarbeitet werden sollen.

   •   CascadeType.PERSIST          ruft die persist()-Methode für die Objekte auf
   •   CascadeType.MERGE            ruft die merge()-Methode für die Objekte auf
   •   CascadeType.REMOVE           ruft die delete()-Methode für die Objekte auf
   •   CascadeType.REFRESH          ruft die refresh()-Methode für die Objekte auf
   •   CascadeType.ALL              ruft all diese Methoden auf

Es können auch mehrere der Werte als Array gemeinsam angegeben werden.

   @OneToMany(cascade = CascadeType.ALL)

   @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE} )

Wenn kein Kaskadierungstyp angegeben wird, müssen alle Objekte durch die Anwendung,
durch expliziten Aufruf von persist() oder save() gespeichert werden, wobei bei der Rei-
henfolge auf die Abhängigkeiten von Fremd- und Primär-Schlüsseln geachtet werden muß.


4.6.3. Installation von Hibernate

Zur Installation von Hibernate müssen die Hibernate-Pakete im Download-Bereich von

   http://www.hibernate.org

heruntergeladen werden. Für die Verwendung der beschriebenen Funktionalitäten sind die
Pakete "Hibernate Core" und "Hibernate Annotations" in der aktuellen Version 3.2.1.GA
notwendig, die jeweils eine Vielzahl von Bibliotheken enthalten, die in den CLASSPATH der
Anwendung aufgenommen werden müssen (siehe auch Anhang C).


4.6.3.1. Hibernate-Einstellungen

Da Hibernate für die mögliche Verwendung in vielen verschiedenen Umgebungen konzipiert
wurde, gibt es eine Vielzahl von Einstellungen. Dabei gilt jedoch der Grundsatz
"Configuration by Exception", d.h. daß für fast alle Einstellungen die Standardwerte verwen-
det werden können, und nur sowenig wie nötig zusätzlich konfiguriert werden muß. Der Ent-
wickler kann aber jederzeit in alle Aspekte der Konfiguration eingreifen.

Die Konfiguration kann in der Anwendung programmiert oder in separaten XML- und Pro-
perty-Dateien erfolgen. Die Datei hibernate.properties enthält meistens die Grundeinstel-
lungen und wird von Hibernate automatisch als Ressource gesucht und verarbeitet.




                                          - 106 -
                                   Datenhaltung - Hibernate


4.6.3.2. Datenbankanbindung

Die wichtigste Einstellung für Hibernate, die auf jeden Fall konfiguriert werden muß, ist die
Anbindung an ein Datenbanksystem. Dafür müssen in der Konfiguration etliche Parameter
angegeben werden.

   hibernate.dialect = org.hibernate.dialect.MySQLDialect
   hibernate.connection.driver_class = com.mysql.jdbc.Driver
   hibernate.connection.url = jdbc:mysql://localhost:3306/hibernate
   hibernate.connection.username = hibernate
   hibernate.connection.password = hibernate

   Resource 10: Hibernate Datenbankeinstellungen

Für die gewünschte Datenbank müssen eine JDBC-Treiber-Klasse und der zugehörige Hiber-
nate-Dialekt, der für die Erzeugung der passenden SQL-Anweisungen zuständig ist, verfügbar
sein. Unter

   http://www.hibernate.org/hib_docs/v3/api/org/hibernate/dialect/package-summary.html

kann die Liste der verfügbaren Dialekte und unterstützten Datenbanken abgerufen werden.


4.6.3.3. Weitere Einstellungen

Zwei weitere Einstellungen können für die grundlegende Verwendung von Hibernate wichtig
sein. Zum einen kann man sich mit

   hibernate.show_sql = true

alle SQL-Anweisungen, die Hibernate erzeugt und an die Datenbank schickt, ausgeben lassen,
was während der Entwicklungsphase einer Anwendung von großer Bedeutung sein kann.

Zum anderen kann man festlegen wie Hibernate mit dem Datenbank-Schema für die persi-
stenten Klassen umgeht. Der Parameter

   hibernate.hbm2ddl.auto = ...

hat dafür mögliche vier mögliche Werte, die angeben auf welche Weise Hibernate das Daten-
bank-Schema automatisch verwaltet.

   •   validate    das Schema wird nicht automatisch erzeugt, sondern nur auf seine Gül-
                   tigkeit hin überprüft
   •   update      das Schema wird erzeugt oder aktualisiert, wenn bereits vorhanden
   •   create      das Schema wird automatisch erzeugt, wenn es nicht vorhanden ist
   •   create-drop das Schema wird erzeugt und beim expliziten Schließen von Hibernate
                   wieder gelöscht


Bei allen Einstellungen ist besonders auf die richtige Schreibweise zu achten, da sie sonst von
Hibernate nicht erkannt und die Standardwerte verwendet werden.


                                            - 107 -
                                  Datenhaltung - Hibernate



4.6.4. Zugriff auf Hibernate

Nachdem nun alle Vorbereitungen getroffen wurden, wird Hibernate angewiesen nach diesen
Vorgaben zu arbeiten, wodurch man seine eigene Anwendung auf Hibernate aufsetzen kann.


4.6.4.1. Erzeugen der Konfiguration

Zentraler Punkt zur Verwendung von Hibernate in einer Java-Anwendung ist ein Konfigurati-
onsobjekt. In diesem Fall wird Eines verwendet, das mit Annotations arbeitet. Dem Konfigu-
rationsobjekt werden alle durch Annotations gekennzeichneten persistenten Klassen hinzuge-
fügt. Die Datei hibernate.properties mit den Grundeinstellungen wird automatisch im
aktuellen CLASSPATH gesucht und, wenn vorhanden, eingelesen. Außerdem ist es möglich
weitere XML-Konfigurationsdateien oder in der Anwendung erstellte Property-Objekte mit
weiteren Einstellungen anzugeben.

   AnnotationConfiguration cfg = new AnnotationConfiguration();
   cfg.addAnnotatedClass(AccountingService.class);
   cfg.addAnnotatedClass(Account.class);
   cfg.addAnnotatedClass(AccountingEntry.class);
   sessionFactory = cfg.buildSessionFactory();

Das fertige Konfigurationsobjekt wird schließlich dazu verwendet, eine Sitzungs-Fabrik zu
erstellen, die nach den angegebenen Parametern arbeitet. Die Sitzungs-Fabrik ist threadsicher
und dient von nun an als Ausgangspunkt für alle weiteren Aktionen mit Hibernate.


4.6.4.2. Sitzungen

Die Arbeit mit den persistenten Objekten erfolgt innerhalb einer Sitzung, die man von der
Sitzungs-Fabrik erhält.

   Session session = sessionFactory.openSession();

Ein Sitzungs-Objekt ist nicht threadsicher und darf deshalb nur von einem einzelnen Thread
begrenzt verwendet werden. Für die Verwendung des Objekts gibt es verschiedene Ansätze
bezüglich der Lebensdauer einer Sitzung.

   •   session-per-operation: Eine bequeme Möglichkeit wäre es, eine eigene Sitzung für je-
       de Operation zu öffnen, die Operation auszuführen, und die Sitzung wieder zu schlie-
       ßen. Dies ist jedoch eine schlechte Vorgehensweise, da in einer Anwendung norma-
       lerweise mehrere Operationen logisch zusammengehören, und auch die Performance
       stark darunter leiden könnte.

   •   session-per-request: Besser wäre es, eine Sitzung zu öffnen, die solange geöffnet
       bleibt, bis ein Benutzeraufruf des Systems vollständig verarbeitet ist. Alle Datenbank-
       Operationen die für den Aufruf notwendig sind, können innerhalb einer Sitzung von
       einem Thread ausgeführt werden.

Jede Datenbank-Operation wird von Hibernate auf jeden Fall als Transaktion ausgeführt, so
daß einer Sitzung 1:1 immer eine Transaktion zugeordnet wird.

                                           - 108 -
                                   Datenhaltung - Hibernate


4.6.4.3. Transaktionen

Häufig reicht diese eine implizite Transaktion jedoch nicht aus. Viele Anwendungen führen
während eines Benutzeraufrufs eine längere Konversation (conversation) mit dem Benutzer
(auch genannt Anwendungstransaktion - application transaction) durch, wobei meist längere
Wartezeiten auf Benutzereingaben (think time) eingeschlossen sind. Wird nun nach dem Mu-
ster session-per-request vorgegangen bleibt eine Datenbanktransaktion während der
think time offen, und zugeordnete Datenbank-Ressourcen bleiben gesperrt, was z.B. für die
Skalierung bezüglich der Benutzerzahl sehr schlecht ist.
Deshalb umfaßt eine Anwendungstransaktion meistens mehrere Datenbanktransaktionen, zu-
erst zum Laden von Daten und später zum Speichern von geänderten Daten, die entsprechend
programmiert werden müssen. In verwalteten Umgebungen, wie z.B. einem Applikationsser-
ver (siehe 6), wird die Transaktions-Demarkation über JTA [51] normalerweise automatisch
vorgenommen. In Einzelanwendungen, oder um die Anwendung portabel zu halten, kann die
Transaktions-Demarkation auch einprogrammiert werden.
Session session = sessionFactory.openSession();
Transaction tx = null;
try {
  tx = session.beginTransaction();

  ...    // Datenbank-Operationen

  tx.commit();
} catch (RuntimeException e) {
  if (tx != null)
    tx.rollback();
  }
  throw e;
} finally {
  session.close();
}

Code 71: Manuelle Transaktions-Demarkation (1)

Um dabei jedoch flexibler vorgehen zu können, kann man die Verwaltung der Sitzung auch
Hibernate überlassen, und sich ganz auf die benötigten Transaktionen konzentrieren.

try {
  sessionFactory.getCurrentSession().beginTransaction();

  ...    // datenbankbezogene Operationen

  sessionFactory.getCurrentSession().getTransaction().commit();
} catch (RuntimeException e) {
  sessionFactory.getCurrentSession().getTransaction().rollback();
  throw e;
}

Code 72: Manuelle Transaktions-Demarkation (2)

Ein Problem dabei ist jedoch eben der gleichzeitige Zugriff von mehreren Benutzern auf das
System, wodurch mehrere Entity-Objekte, die sich aber auf den selben Datensatz in der Da-
tenbank beziehen, im Umlauf sein können, wobei der Datensatz eben nicht für einen bestimm-
ten Benutzer exklusiv gesperrt ist.


                                             - 109 -
                                 Datenhaltung - Hibernate


4.6.4.4. Fehlerbehandlung

Wenn beim Aufruf von Hibernate-Methoden eine Ausnahme geworfen wird, sollte die aktuel-
le Transaktion unbedingt zurückgerollt werden (rollback()). Außerdem muß die aktuelle
Sitzung geschlossen werden, da bei bestimmten Hibernate-Methoden die Sitzung nicht in ei-
nem konsistenten Zustand bleibt. Generell sind Ausnahmen bei Hibernate nicht behebbar und
es muß in einer neuen Sitzung von vorne begonnen werden [68].

Hibernate    wirft     normalerweise    eine  HibernateException         (abgeleitet von
RuntimeException) für einen allgemeinen Fehler in der Persistenz-Schicht.
Der häufigere Fall sind aber Datenbank-Ausnahmen (SQLException), die als JBDCException
oder einer Unterklasse davon (siehe auch SQLExceptionConverterFactory), mit einer be-
stimmten Bedeutung, geworfen werden.

   •   JDBCConnectionException              ein allgemeines Problem mit der Datenbankver-
                                            bindung
   •   SQLGrammarException                  ein Grammatik- oder Syntax-Problem bei der
                                            ausgeführten SQL-Anweisung
   •   ConstraintViolationException         ein Datenbank-Einschränkung wäre verletzt
                                            worden
   •   LockAcquisitionException             eine notwendige Sperre für die aktuelle Operati-
                                            on kann nicht erhalten werden
   •   GenericJDBCException                 eine andere Ausnahme, die nicht in eine der ge-
                                            nannten Kategorien fällt



4.6.4.5. Transaktions-Zeitlimit

Neben der Kontrolle der auftretenden Fehler ist es außerdem wichtig, daß Datenbank-
Ressourcen von ungünstig verlaufenden Transaktionen nicht beliebig lange belegt werden
können. Dazu kann für jede Transaktion ein Zeitlimit (Timeout) angegeben werden.

   //Zeitlimit auf 3 Sekunden setzen
   session.getTransaction().setTimeout(3);

Hibernate versucht über die Kontrolle von Datensatz-Sperren und der Größe von Abfrage-
Ergebnissen sicherzustellen, daß die eingestellte Zeit eingehalten wird. In einer verwalteten
Umgebung übergibt Hibernate die Verwaltung des Zeitlimits an JTA, sofern das Zeitlimit
nicht sowieso von der Ablauf-Umgebung vorgegeben wird.




                                           - 110 -
                                     Datenhaltung - Hibernate


4.6.4.6. Versionskontrolle

Durch Versionskontrolle (optimistic concurrency control) der persistenten Objekte kann die
Datenintegrität bei gleichzeitigen Zugriffen sichergestellt werden. Im einfachsten Fall kann
die Versionskontrolle selbst implementiert werden, und bei unterschiedlichen Versionen eine
Fehlermeldung ausgegeben werden.

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

int oldVersion = foo.getVersion();
session.load(foo, foo.getKey());
if (oldVersion != foo.getVersion) {
  throw new StaleObjectStateException();
}
foo.setProperty("bar");

tx.commit();
session.close();

Code 73: Selbst implementierte Versionskontrolle

Dieses Vorgehen ist für Verarbeitung komplexer Objekthierarchien, besonders bei der An-
wendung der Kaskadierung, sehr umständlich, weshalb die Versionskontrolle von Hibernate
automatisch durchgeführt werden sollte.
Dazu sollte eine persistente Klasse ein numerisches Feld (oder Timestamp) definieren, das mit
der Annotation @Version für die automatische Versionskontrolle gekennzeichnet wird.

@Entity
@org.hibernate.annotations.Entity(
  optimisticLock = OptimisticLockType.VERSION)
public class Account {
  @Version
  public Integer getVersion() {
    ...
  }
}

Code 74: Klasse mit automatischer Versionskontrolle durch Hibernate

Bei der @Entity-Annotation von Hibernate kann durch den zusätzlichen Parameter
optimisticLock angegeben werden, wie Hibernate die Versionskontrolle durchführen soll.

    •   OptimisticLockType.NONE               keine automatische Versionskontrolle
    •   OptimisticLockType.VERSION            Verwendung eines vorhandenen Versionsfeldes
    •   OptimisticLockType.DIRTY              Versionskontrolle durch Vergleich aller Felder, die
                                              von Hibernate als geändert registriert wurden
    •   OptimisticLockType.ALL                Versionskontrolle auch ohne spezielles Versions-
                                              feld, durch Vergleich aller persistenten Felder der
                                              Klasse

Wenn Hibernate durch die Versionskontrolle beim Speichern die mehrfache gleichzeitige
Änderung eines Objekts festestellt wird eine Ausnahme geworfen.


                                               - 111 -
                                 Datenhaltung - Hibernate


4.6.4.7. Sperren

Die andere Möglichkeit die Konsistenz der Daten zu gewährleisten sind Datenbank-Sperren
(pessimistic locking), die von Hibernate automatisch bei bestimmten Zugriffen auf die Daten-
bank verwendet oder durch den Entwickler explizit gesetzt werden.

Die Klasse LockMode definiert die möglichen Sperren, die Hibernate beim Zugriff auf den
Datensatz für ein persistentes Objekt erhalten kann.

   •   LockMode.WRITE          wird verwendet beim Aktualisieren oder Einfügen einer Zeile
                               in die Datenbank
   •   LockMode.UPGRADE        wird nur beim expliziter Angabe verwendet
   •   LockMode.READ           wird beim wiederholten Laden von Objekten verwendet
   •   LockMode.NONE           keine Sperren werden verwendet

Bei den folgenden Hibernate-Methoden kann explizit angegeben werden, daß eine bestimmte
Sperre verwendet werden soll.

   •   session.load()
   •   session.get()
   •   session.lock()
   •   Query.setLockMode()

Hibernate verwendet immer die Sperr-Mechanismen der Datenbank und wird niemals Objekte
im Speicher sperren. Wenn die Datenbank die angeforderte Sperre nicht unterstützt, verwen-
det Hibernate eine passende Alternative und gibt keinen Fehler zurück, damit die Anwendung
portabel bleibt [68].


4.6.5. Verwendung der Objekte

Hibernate hält nicht nur die konkreten Details der zugrundeliegenden Datenbank vom Ent-
wickler fern, sondern kümmert sich auch um die Verwaltung des Objekt-Zustands. Das be-
deutet, daß sich ein Entwickler keine Gedanken darüber machen muß, wann welche SQL-
Anweisungen ausgeführt werden, er muß lediglich den aktuellen Zustands eines Objekts ge-
genüber Hibernate im Auge behalten.


4.6.5.1. Zustände

Jedes Objekt, das mit dem new-Operator gerade erst erzeugt worden ist, ist bei Hibernate noch
nicht bekannt und daher im Objektzustand transient. Es hat keinen entsprechenden Datensatz
in der relationalen Datenbank und keinen von Hibernate zugewiesenen Identifikator.

   Account account = new Account();
   account.setNumber(2400);
   account.setType(AccountType.ACTIVE);
   account.setDescription("Hibernate-Konto");




                                           - 112 -
                                  Datenhaltung - Hibernate

Nachdem ein Objekt durch den Aufruf von perist() oder save() an Hibernate übergeben
wurde ist es persistent. Es ist eine geöffnete Hibernate-Sitzung gebunden und hat zugehörige
Daten in der Datenbank, sowie einen erzeugten oder angegebenen Identifikator.

   Long id = (Long)session.save(account);

   session.save(id, account);

Hibernate überwacht dann das Objekt, so daß am Ende einer Transaktion oder der Sitzung bei
geänderten Daten eine Synchronisation mit der Datenbank stattfindet. Durch den Aufruf von

   session.flush();

kann der Abgleich mit der Datenbank auch manuell angestoßen werden.

Wenn nun die Hibernate-Sitzung zu der ein Objekt gehört geschlossen wird, bleibt das Java-
Objekt in der Virtuellen Maschine natürlich bestehen, es hat nur keine Verbindung zu Hiber-
nate mehr, weshalb es in den Zustand detached übergeht. Man kann das Objekt normal wei-
terverwenden und auch seine Daten ändern. Später kann das Objekt dann wieder ein Sitzung
zugeordnet, und so die geänderten Daten in der Datenbank gespeichert werden.

   session2.update(account);

Dazu muß die Methode update() verwendet werden, da der Aufruf von persist() oder
save() nur bei transienten Objekten möglich ist [67].


4.6.5.2. Automatische Erkennung des Objektzustandes

Durch die möglichen Vorgänge, bei denen ein Objekt an Hibernate gebunden und wieder von
der Sitzung getrennt werden kann, kann es kompliziert werden, jeweils die richtige Hibernate-
Funktion zu verwenden, z.B. wenn ein Objekt aus der Datenhaltungsschicht an die Benutzer-
oberfläche gegeben wird, dort verändert wird, und dann zum Speichern wieder an die Daten-
haltung zurückgegeben wird.

Dafür bietet Hibernate die weiteren Funktionen saveOrUpdate() und merge(), die den aktu-
ellen Zustand eines Objekts in Betracht ziehen, und dann entsprechend verfahren.

   session2.saveOrUpdate(account);

   session2.merge(account);

Die Funktion saveOrUpdate() verhält sich dabei folgendermaßen:

   •   wenn das Objekt bereits persistent ist, wird nichts unternommen
   •   wenn bereits ein Objekt mit dem selben Identifikator vorhanden ist, wird eine Aus-
       nahme geworfen
   •   wenn das Objekt noch keinen Identifikator hat, wird es persistiert
   •   wenn das Objekt einen Identifikator hat, der noch nicht vergeben ist, wird es persistiert
   •   wenn das Objekt eine Versionsangabe (@Version) enthält, die auf ein neues Objekt
       hindeutet, wird es durch save() persistiert
   •   in allen anderen Fällen wird update() aufgerufen

                                            - 113 -
                                   Datenhaltung - Hibernate

Die Funktion merge() verhält sich im Gegensatz dazu ganz unterschiedlich:

   •   wenn es bereits ein persistentes Objekt mit dem selben Identifikator gibt, werden nur
       die Daten des neuen Objekts übernommen
   •   wenn es kein passendes persistentes Objekt gibt, wird Eines aus der Datenbank gela-
       den oder neu erzeugt
   •   die persistente Instanz wird zurückgegeben
   •   die übergebene Instanz wird nicht der aktuellen Sitzung zugeordnet und bleibt somit
       detached



4.6.5.3. Laden der Objekte

Durch die load()-Methode wird eine persistente Instanz geladen, die sicher in der Datenbank
vorhanden und deren Identifikator bekannt ist.

   Account account = (Account)session.load(Account.class, id);

Die Methode instanziert ein Objekt der angegebenen Klasse mit den zugehörigen Daten aus
der Datenbank und gibt es im Zustand persistent zurück. Wenn kein Datensatz zu dem ange-
gebenen Identifikator existiert, wird eine Ausnahme geworfen.

Falls von vorne herein nicht sichergestellt ist, daß ein zugehöriger Datensatz vorhanden ist,
kann die get()-Methode verwendet werden.

   Account account = (Account)session.get(Account.class, id);
   if (account == null) {
     account = new Account();
     session.save(account, id);
   }
   return account;

Diese Methode wirft keine Ausnahme, sondern gibt null zurück, wenn der Identifikator un-
gültig ist. Wie bereits erwähnt, gibt es bei dieser Methode eine Variante, mit der explizit eine
Sperre angefordert werden kann, z.B.

   account = (Account)session.get(Account.class, id, LockMode.UPGRADE);

Außerdem können die Daten eines bereits instanzierten persistenten Objekts durch die
refresh()-Methode erneut geladen werden.

   session.save(account);    // Objekt speichern
   session.flush();          // SQL-Insert-Anweisung ausführen lassen
   session.refresh(account); // Daten erneut laden

Dies ist für den Fall nützlich, daß Datenbank-Trigger beim Speichern verwendet werden, um
Felder des persistenten Objekts zu initialisieren, wie z.B. einen Zeitstempel.




                                            - 114 -
                                 Datenhaltung - Hibernate


4.6.5.4. Abfragen

Wenn die Identifikatoren von gewünschten Objekten nicht bekannt sind muß eine Abfrage
ausgeführt werden. Hibernate bietet dafür eine eigene objektorientierte Abfragesprache
(HQL) die ähnlich zu SQL aufgebaut ist, aber die Portabilität der Anwendung gegenüber ver-
schiedenen Datenbanksystemen erhält. Es können aber auch native SQL-Abfragen direkt für
die Datenbank formuliert und ausgeführt werden.

Eine neue Abfrage wird über ein bestehendes Sitzungsobjekt erzeugt.

   Query debits = session.createQuery(
     "FROM accountingentries WHERE debitaccount = ?");
   debits.setNumber(0, 2400);
   Collection result = debits.list();

Die Abfrage unterstützt ? Platzhalter im Stil von JDBC, nur mit dem Unterschied, daß die
Numerierung bei 0 und nicht bei 1 beginnt. Nachdem alle Platzhalter mit einem Wert belegt
worden sind, wird die Abfrage durch den Aufruf von list() ausgeführt, und eine Kollektio-
nen mit den zutreffenden Objekten als Ergebnis zurückgeliefert.
Wenn vorher bereits bekannt ist, daß eine Abfrage nur ein einzelnes Objekt als Ergebnis zu-
rückliefert, kann auch die Methode uniqueResult() an Stelle von list() zur Ausführung
der Abfrage benutzt werden.


4.6.5.5. Modifikationen an Objekten

Persistente Objekte, die mit einer Sitzung verbunden sind, können über ihre Methoden direkt
manipuliert werden. Die Änderungen werden in der Datenbank gespeichert, wenn flush()
explizit aufgerufen oder die gerade laufende Transaktion abgeschlossen wird. Es ist nicht nö-
tig SQL-Anweisungen wie UPDATE oder DELETE extra aufzurufen, und die Hibernate-API bie-
tet deshalb auch keine Methoden dafür.
Falls nötig kann man jedoch durch Session.connection() direkt auf die JDBC-Verbindung
zur Datenbank zugreifen, z.B. um eine größere Anzahl Datensätze direkt zu ändern.

Bei detached Objekten, deren zugehörige Sitzung geschlossen wurde, ist es nötig eine neue
Sitzung zu öffnen, und ihr die Objekte zu übergeben, damit Änderungen gespeichert werden.
Dies erfolgt durch eine der beschriebenen Methoden update(), saveOrUpdate() oder
merge() (siehe 4.6.5.2).

Außerdem ist möglich mit der lock()-Methode ein nicht modifiziertes Objekt einfach nur an
eine neue Session zu binden und dabei ggf. eine Sperre in der Datenbank zu erwirken.


4.6.5.6. Löschen persistenter Objekte

Persistente Objekte werden durch delete() aus der Hibernate-Sitzung und der Datenbank
gelöscht, wobei das Java-Objekt für die Anwendung natürlich bestehen bleibt, solange eine
Referenz darauf vorhanden ist, d.h. das Objekt wird wieder transient.

   session.delete(account);



                                           - 115 -
                                   Datenhaltung - Hibernate



4.6.6. Buchhaltung mit einer Singleton-Service-Klasse

Ein Ansatz für ein persistentes Buchhaltungssystem mit Hibernate wäre die Betrachtung der
Objekthierarchie von den Konten aus, denen dann jeweils die Buchungen zugeordnet sind.




Abbildung 61: ERM-Modell 1 für die Buchhaltung mit Hibernate

Die Service-Klasse ist dann keine Entität und es muß lediglich für die Implementierung der
Methoden der Kontenobjekte und Buchungsobjekte eine Instanz der Service-Klasse als
"Dienstleister" verfügbar sein.


4.6.6.1. Service-Klasse

Dazu bietet sich die Verwendung des Singleton-Musters an. Die Service-Klasse wird daher
mit einigen statischen Methoden ausgestattet, die immer nur die eine statische Instanz der
Service-Klasse zurückgeben.

package de.fhr.vs_iw.hibernate.v1;

public final class AccountingService implements IAccountingService {
  private static AccountingService instance = null;
  private static Session session = null;
  private static SessionFactory sessionFactory = null;

  private static void initInstance() {
    AnnotationConfiguration cfg = new AnnotationConfiguration();
    cfg.addAnnotatedClass(Account.class);
    cfg.addAnnotatedClass(AccountingEntry.class);

      sessionFactory = cfg.buildSessionFactory();
      session = sessionFactory.openSession();
      session.beginTransaction();

      instance = new AccountingService();
      instance.accountSet = new HashSet<Account>(
          session.createQuery("FROM Account").list());
      instance.accountingEntrySet = new HashSet<AccountingEntry>(
          session.createQuery("FROM AccountingEntry").list());
  }

  protected static void closeInstance() {
    if (instance != null) {
      session.getTransaction().commit();
      session.close();
      session = null;
      sessionFactory = null;
      instance = null;
    }
  }




                                             - 116 -
                                     Datenhaltung - Hibernate

    protected static AccountingService getInstance() {
      if (instance == null) {
        initInstance();
      }
      return instance;
    }

    ...

    public void createAccount(Integer number, AccountType type,
        String description) {
      Account account = new Account(number, type, description);
      accountSet.add(account);
      session.save(account);
    }

    public Integer createAccountingEntry(Integer debitAccount,
        Integer creditAccount, Double amount, String text){

        AccountingEntry entry = new AccountingEntry(
            debitAccount, creditAccount, amount, text);

        accountingEntrySet.add(entry);
        Integer id = (Integer)session.save(entry);

        return id;
    }

    ...
}

Code 75: Class AccountingService (Hibernate - Variante 1)

Dabei werden alle Konten und Buchungen innerhalb der Service-Klasse verwaltet. Beim Er-
zeugen der Instanz der Service-Klasse durch initInstance() wird Hibernate konfiguriert,
sowie eine Session und eine Transaktion gestartet. Alle in der Datenbank vorhandenen Kon-
ten und Buchungen werden durch Abfragen geladen. Die Konten- bzw. Buchungsobjekte
verwenden dann zum Zugriff darauf die Singleton-Instanz der Service-Klasse.

     AccountingService.getInstance();

Alle von Hibernate aus der Datenbank geladenen Objekte sind persistent und Änderungen
daran werden gespeichert. Nur beim Erzeugen neuer Objekte durch createAccount() bzw.
createAccountingEntry() müssen diese durch den Aufruf von persist() oder save(),
wie oben im Code gezeigt, bei der Hibernate-Sitzung registriert werden. Beim Speichern ei-
nes neuen Buchungsobjekts wird die zugewiesene ID des neuen Objekts zurückgegeben.




                                               - 117 -
                                     Datenhaltung - Hibernate


4.6.6.2. Konto-Klasse

Ein Konto speichert seine zugeordneten Buchungen nicht selbst, sondern sucht diese über die
Service-Klasse. Konten und Buchungen speichern nur ihre jeweiligen Basisdaten. Da dieser
Ansatz bereits vollständig dem Buchhaltungs-Modell entspricht, und die Entitäten bereits über
die Kontonummer einander zugeordnet werden, muß für Hibernate in dieser Hinsicht keine
Beziehung zwischen den Entitäten konfiguriert werden, da dies von der implementierten Ge-
schäftslogik des Buchhaltungssystems her bereits erfolgen muß.

package de.fhr.vs_iw.hibernate.v1;

@Entity
@Table(name = "Account")
public final class Account implements IAccount {

    private String description = null;
    private Integer number = null;
    private AccountType type = null;

    public Account();
    public Account(Integer number, AccountType type, String description);

    @Transient
    public Set<Integer> getCreditEntries() {
      final Set<Integer> credit = new HashSet<Integer>();
      for (AccountingEntry entry
          : AccountingService.getInstance().accountingEntrySet) {
        if (entry.getCreditAccount().equals(number)) {
          credit.add(entry.getId());
        }
      }
      return credit;
    }

    @Transient
    public Set<Integer> getDebitEntries();

    @Basic
    public String getDescription();

    @Id
    @Column(name = "accountnumber")
    public Integer getNumber();

    @Basic
    public AccountType getType();

    public void setDescription(String description);
    public void setNumber(Integer number);
    public void setType(AccountType type);
}

Code 76: Class Account (Hibernate - Variante 1)

Die Konto-Klasse wird als Entität und die Eigenschaften der Klasse als Basis-Elemente ge-
kennzeichnet, wobei die Methoden zum Ermitteln der zugeordneten Buchungen transient sein
müssen, da Hibernate hier nicht darauf zugreifen soll.


                                                  - 118 -
                                     Datenhaltung - Hibernate


4.6.6.3. Buchungs-Klasse

Auch die Buchungs-Klasse wird einfach als Entität gekennzeichnet.

package de.fhr.vs_iw.hibernate.v1;

@Entity
@Table(name = "AccountingEntry")
public final class AccountingEntry implements IAccountingEntry {
  private Double amount = null;
  private Integer creditAccount = null;
  private Integer debitAccount = null;
  private Integer id = null;
  private String text = null;

    public AccountingEntry();
    public AccountingEntry(Integer debit, Integer credit,
                           Double amount, String text);
    @Basic
    public Double getAmount();

    @Basic
    public Integer getCreditAccount();

    @Basic
    public Integer getDebitAccount();

    @Id
    @GeneratedValue()
    public Integer getId();

    @Basic
    public String getText();

    public   void   setAmount(Double amount);
    public   void   setCreditAccount(Integer number);
    public   void   setDebitAccount(Integer number);
    public   void   setId(Integer id);
    public   void   setText(String text);
}

Code 77: Class AccountingEntry (Hibernate - Variante 1)

Bei der Buchungs-Klasse treten von den Daten her sowieso nur Basis-Elemente auf. Da bei
der Buchungs-ID die Vergabe durch das System vorgesehen ist, kann Hibernate dies durch
die Annotation @GeneratedValue übernehmen.


4.6.6.4. Proxy-Klasse

In der connect()-Methode der Proxy-Klasse wird lediglich die Singleton-Instanz der Ser-
vice-Klasse zurückgegeben (getInstance()). Beim Schließen des Systems durch close()
wird dann noch die Methode closeInstance() der Service-Klasse aufgerufen, um die Ob-
jekte zu speichern und die Hibernate-Sitzung abzuschließen.




                                               - 119 -
                                     Datenhaltung - Hibernate



4.6.7. Buchhaltung mit einer Entity-Service-Klasse

Ein anderer Ansatz wäre es, die Objekthierarchie von der Buchhaltungsanwendung von der
Service-Klasse aus zu betrachten, die ja sowieso schon alle vorhandenen Konten und Bu-
chungen verwaltet.




Abbildung 62: ERM-Modell 2 für die Buchhaltung mit Hibernate

Die Service-Klasse ist dann selbst eine zu speichernde Entität des Systems, und die Konten
und Buchungen werden mit ihr in eine feste Beziehung gesetzt.


4.6.7.1. Service-Klasse

Dazu wird die Service-Klasse als Entität gekennzeichnet und um direkte Zugriffsmethoden
für die Listen der Konten bzw. der Buchungen erweitert. Außerdem muß ein Primär-Schlüssel
definiert werden, der allerdings immer 0 ist.

package de.fhr.vs_iw.hibernate.v2;

@Entity
@Table(name = "AccountingService2")
public final class AccountingService implements IAccountingService {
  private int nextId = 0;
  private Set<AccountingEntry> accountingEntrySet
    = new HashSet<AccountingEntry>();
  private Set<Account> accountSet
    = new HashSet<Account>();

    public AccountingService();

    @OneToMany(mappedBy = "service", cascade = CascadeType.ALL)
    public Set<AccountingEntry> getAccountingEntrySet();

    @OneToMany(mappedBy = "service", cascade = CascadeType.ALL)
    public Set<Account> getAccountSet();

    @Basic
    public int getNextId();

    @Id
    public Integer getServiceId() {

    public void setAccountingEntrySet(
        Set<AccountingEntry> accountingEntriesSet);
    public void setAccountSet(Set<Account> accountsSet);
    public void setNextId(int nextId);
    public void setServiceId(Integer serviceId);

    ...
}

Code 78: Class AccountingService (Hibernate - Variante 2)

                                               - 120 -
                                     Datenhaltung - Hibernate

Der Primärschlüssel wird nicht ein einem Feld der Instanz der Klasse gespeichert, sondern ist
immer 0, weil es nur eine Instanz der Service-Klasse geben soll. Außerdem ist es sinnvoll das
Feld nextId, mit der nächsten zu vergebenden Buchungs-ID, auch mitzuspeichern, damit das
System nach dem Laden aus der Datenbank keine Fehler beim Erfassen neuer Buchungen
produziert. Die Buchungs-ID kann man hier nur schlecht von Hibernate vergeben lassen, da
nicht vorgesehen ist, daß ein Buchungsobjekt nach dem Anlegen sofort von Hibernate persi-
stiert wird, sondern später die vollständige Objekthierarchie auf einmal (siehe 4.6.7.4). Des-
halb ist bei den @OneToMany-Beziehungen auch der Kaskadierungstyp angegeben


4.6.7.2. Konto-Klasse

Die Konto-Klasse stellt ebenfalls wieder eine eigene Entität dar, die mit der Service-Klasse in
einer bidirektionalen Beziehung steht. Dafür erhält die Konto-Klasse ein entsprechendes Feld
mit Getter- und Setter-Methode für die zugehörige Instanz der Service-Klasse.

package de.fhr.vs_iw.hibernate.v2;

@Entity
@Table(name = "Account2")
public final class Account implements IAccount {
  private AccountingService service = null;
  @ManyToOne
  public AccountingService getService();
  public void setService(AccountingService service);

    ...
}

Code 79: Class Account (Hibernate - Variante 2)

Durch die Annotation @ManyToOne wird der Rückbezug zur Service-Klasse hergestellt. An-
sonsten bleiben die Methoden und Annotations im Vergleich zur vorherigen Konto-Klasse
(siehe 4.6.6.2) unverändert.


4.6.7.3. Buchungs-Klasse

Bei der Buchungs-Klasse wird analog zur Konto-Klasse vorgegangen und die Beziehung zur
Service-Klasse hergestellt. Ansonsten entspricht auch diese Klasse der vorigen (siehe 4.6.6.3).

package de.fhr.vs_iw.hibernate.v2;

@Entity
@Table(name = "AccountingEntry2")
public final class AccountingEntry implements IAccountingEntry {
  private AccountingService service = null;

    @ManyToOne
    public AccountingService getService();
    public void setService(AccountingService service);

    ...
}

Code 80: Class AccountingEntry (Hibernate - Variante 2)


                                                  - 121 -
                                     Datenhaltung - Hibernate

4.6.7.4. Proxy-Klasse

Da die Service-Klasse jetzt selbst eine Entität darstellt wird der Aufruf von Hibernate in die
Proxy-Klasse verlegt. Die Konfiguration erfolgt im Konstruktor wie vorher (siehe 4.6.6.1).

package de.fhr.vs_iw.hibernate.v2;

public final class HibernateProxy2 implements IAccountingProxy {
  private AccountingService service = null;
  private Session session = null;
  private SessionFactory sessionFactory = null;

    public HibernateProxy() {
      AnnotationConfiguration cfg = new AnnotationConfiguration();
      cfg.addAnnotatedClass(AccountingService.class);
      cfg.addAnnotatedClass(Account.class);
      cfg.addAnnotatedClass(AccountingEntry.class);
      sessionFactory = cfg.buildSessionFactory();
    }

    public void close() {
      if (service != null) {
        session.getTransaction().commit();
        session.close();
        session = null;
        sessionFactory = null;
        service = null;
      }
    }

    public IAccountingService connect(String[] args) {
      if (service == null) {
        session = sessionFactory.openSession();
        session.beginTransaction();
        service = (AccountingService)session.get(AccountingService.class, 0);
        if (service == null) {
          service = new AccountingService();
          session.save(service);
        }
      }
      return service;
    }
}

Code 81: Class HibernateProxy2 (Hibernate - Variante 2)

In der connect()-Methode wird eine Hibernate-Sitzung geöffnet und eine Transaktion ge-
startet. Dann wird das Service-Objekt, und durch Kaskadierung auch alle zugehörigen Kon-
ten- und Buchungsobjekte, aus der Datenbank geladen. So sind all diese Objekte, und auch
neue Objekte die später den Sets der Service-Klasse hinzugefügt werden, als persistent anzu-
sehen. Wenn in der Datenbank noch kein Service-Objekt vorhanden ist, wird ein neues Objekt
erzeugt und an die Hibernate-Sitzung übergeben.
Die Hibernate-Sitzung und die Transaktion werden bis zum Aufruf der close()-Methode
offen gehalten. Dort wird dann commit() aufgerufen und so die aktuellen Daten der gesamten
Hierarchie persistenter Objekte in die Datenbank gespeichert.




                                               - 122 -
                                 Datenhaltung - Hibernate


4.6.7.5. Fazit

Hibernate ist ein mächtiges und ausgereiftes Framework, um die Persistenz-Schicht einer
Anwendung zur Speicherung der Daten zu realisieren. Die Entwicklung von Hibernate war
mit ausschlaggebend für die JPA-Spezifikation (Java Persistence API) im Rahmen der Enter-
prise JavaBeans 3.0 Spezifikation und stellt damit quasi selbst den Standard für die Daten-
speicherung bei großen Java-Anwendungen dar.
Die Einarbeitung ist im ersten Schritt etwas aufwendiger, da Hibernate sehr flexibel und für
jede mögliche Situation anwendbar ist, dafür zahlt sich die Persistenz durch einfache Java-
Objekte für jeden Anwendungsfall aus.
Der hier beschriebene Umfang der Funktionalität von Hibernate stellt nur einen Bruchteil der
Möglichkeiten zum Einstieg in die Verwendung des Frameworks dar und ist speziell auf das
Buchhaltungsbeispiel bezogen. Die gesamten Möglichkeiten von Hibernate sind noch wesent-
lich umfangreicher.




                                          - 123 -
                                     Transport - Übersicht


5. Transport




5.1. Übersicht
Nachdem nun mehrere Möglichkeiten besprochen wurden, die Daten der Buchhaltungsan-
wendung zu speichern und dabei die Geschäftslogik zu implementieren, soll die Anwendung
jetzt auf mehrere Systeme verteilt werden.


5.1.1. Prinzipieller Ablauf

                                                             Dazu wäre es einfach ein Proto-
                                                             koll zu definieren, und damit Da-
                                                             ten über ein Netzwerk zu trans-
                                                             portieren, da die Anbindung an
                                                             Netzwerk-Sockets unter Java sehr
                                                             einfach möglich ist, sei es durch
                                                             asynchronches Versenden von
                                                             Datenpakten oder durch ein einfa-
                                                             ches     synchrones      Anfrage-
                                                             Antwort-Protokoll.
                              Abbildung 64:
                              Anfrage-Antwort


Abbildung 63: Datenpakte

                                                             Ziel ist aber die Verwendung von
                                                             einem objektorientierten System,
                                                             bei dem Methoden von entfernten
                                                             Objekten aufgerufen werden, so
                                                             als ob das Objekt lokal vorhanden
                                                             wäre. Das System sollte im Ideal-
                                                             fall vollständige Transparenz bie-
                                                             ten, so daß sich ein Entwickler
                                                             nicht darum kümmern muß, wo
                                                             und wie genau der Aufruf einer
                                                             Methode eines Objekts ausgeführt
                                                             wird. Eine Middleware-Schicht
                                                             übernimmt dabei den Transport
                                                             des Aufrufs über das Netzwerk.
Abbildung 65: Entfernter Methodenaufruf

                                            - 124 -
                                         Transport - Übersicht



5.1.2. Erstellung mit Eclipse

Zur Realisierung der Middlewareschicht wird ein neues Modul für die Buchhaltungsanwen-
dung erstellt (siehe 3.9.4). Wie beschrieben wird zuerst in Eclipse ein neues Package dafür
angelegt, z.B. mit dem Namen de.fhr.vs_iw.socket, im Menü unter:

   File -> New -> Package

Dann wird wieder je eine eigene Klasse für jede der Schnittstellen der Buchhaltung erzeugt.
Durch die Implementierung der Schnittstellen kann das Modul nun wieder von der Benutzer-
oberfläche aufgerufen werden. Weiterhin benötigt das Modul aber noch eine Server-Seite, die
in einer eigenen Klasse erstellt wird, über:

   File -> New -> Class



                                                          Zusammen mit einem Modul aus der Da-
                                                          tenhaltung ergibt sich im Package Explo-
                                                          rer von Eclipse z.B. die nebenstehende
                                                          Ansicht.


                                                          Ein Modul aus der Datenhaltung kann von
                                                          der Server-Klasse über die Buchhaltungs-
                                                          schnittstellen direkt verwendet werden,
                                                          damit bereits vorhandene Funktionalitäten
                                                          nicht nochmals implementiert werden
                                                          müssen. Auf diese Weise entsteht der ge-
                                                          wünschte modulare Aufbau des gesamten
                                                          Systems.




Abbildung 66: Eclipse Package Explorer


Dazu wird die Proxy-Klasse des gewünschten Moduls (z.b. de.fhr.vs_iw.local) in der
Server-Klasse entweder direkt instanziert

   IAccountingProxy proxy = new LocalProxy();

oder über die Methode loadProxy() der Klasse Utilities, durch ihren vollständigen Na-
men (über Reflection) geladen.

   proxy = Utilities.loadProxy("de.fhr.vs_iw.local.LocalProxy");

Durch die Verwendung des Names wird das Datenhaltungsmodul leicht austauschbar. Dies ist
deshalb die Methode, die bevorzugt verwendet werden sollte (siehe 2.4.1). Anschließend kann
nun die Datenübertragung zwischen der Proxy-Klasse (hier SocketProxy) und der Server-
Klasse (hier SocketServer) implementiert werden.
                                                - 125 -
                              Transport - Eigener Transport über Sockets


5.2. Eigener Transport über Sockets
5.2.1. Allgemeines

Der Einstieg in die Transportschicht soll mit einer völlig eigenen Implementierung der Daten-
und Objektübertragung erfolgen. Dies soll einen Eindruck vermitteln welche Mechanismen
und Maßnahmen für die Anwendungs-Kommunikation notwendig sind. Deshalb erfolgt der
Ansatz auf einer grundlegenden Ebene, unter direkter Verwendung von Netzwerk-Sockets,
und wird wie eine Middleware-Schicht aufgebaut.


5.2.2. Anbindung an die Datenhaltung

Das Socket-Kommunikations-Modul soll auf ein beliebiges Modul der Datenhaltungsschicht
aufsetzen können, davon die bereits implementierte Geschäftslogik nutzen und lediglich die
Kommunikationsfähigkeit hinzufügen.


5.2.2.1. SocketServer

Dazu verhält sich der SocketServer gegenüber einem Datenhaltungs-Modul wie ein Client.
Er instanziert die auf der Kommandozeile angegebene Proxy-Klasse eines vorhandenen Mo-
duls, und lädt damit die zugehörige Service-Klasse.

package de.fhr.vs_iw.socket;

public final class SocketServer extends Thread {
  public SocketServer(IAccountingService service, Socket socket);
  public void run();
  private String genOid(Object obj);
  private String getOid(Object obj);
  private ServerSideResponse process(ServerSideRequest req);
  private ServerSideResponse remoteObjectResponse(Object o, Class c);

    public static void main(String[] args) {
      try {
        if (args.length <= 1) {
          System.err.println("Port und Proxy-Klasse angegeben");
          return;
        }
        ServerSocket ss = new ServerSocket(Integer.parseInt(args[0]));
        IAccountingProxy localProxy = Utilities.loadProxy(args[1]);
        IAccountingService nextService
          = localProxy.connect(Utilities.shiftArgs(args, 2));
        while (true) {
          try {
            new SocketServer(nextService, ss.accept()).start();
          } catch (IOException e) {
          }
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
}

Code 82: Class SocketServer

                                               - 126 -
                        Transport - Eigener Transport über Sockets

Als weiteres Kommandozeilenargument benötigt der SocketServer noch den Port auf dem er
lauschen soll. Auf diesem Port wird ein ServerSocket eröffnet, der mit accept() Verbin-
dungen von Clients entgegen nimmt.

   java de.fhr.vs_iw.socket.SocketServer <port> <proxy> [proxy-parameters]

Die restlichen Kommandozeilenargumente werden der angegebenen Proxy-Klasse als Para-
meter für die connect()-Methode übergeben. Dazu ist die Utilities-Methode

   shiftArgs(String[] args, int number);

hilfreich, die die angegebene Anzahl Argumente vom Anfang des String-Arrays entfernt.


5.2.2.2. Parallele Server-Instanzen

Der SocketServer selbst ist von Thread abgeleitet. Damit wird für jeden Client, der sich mit
dem Server verbindet, eine eigener SocketServer instanziert und gestartet, der den verbun-
denen Socket und die Instanz der geladenen Service-Klasse erhält. Dies stellt die einfachste
Möglichkeit dar, die Anfragen von Clients parallel und unabhängig zu verarbeiten. Hier wird
dann auch deutlich, warum die Implementierung eines Moduls der Datenhaltungsschicht
threadsicher sein muß.


5.2.3. Aufbau und Nutzung der Verbindung

Aufbau und Verwendung der Verbindung gehen vom Client aus. Der Server verarbeitet die
lediglich die eingehenden Anfragen.


5.2.3.1. Socket-Proxy-Klasse

Um dem Konzept der Schnittstellen und Module der Buchhaltung zu entsprechen, wird eine
Socket-Client-Klasse erstellt, welche die IAccountingProxy-Schnittstelle implementiert. Die
connect()-Methode wird im Sinne ihres Namens tätig und stellt die Socket-Verbindung zum
SocketServer her, die dann in der close()-Methode wieder geschlossen wird.

package de.fhr.vs_iw.socket;

public final class SocketProxy implements IAccountingProxy {
  private BufferedReader reader = null;
  private Socket socket = null;
  private PrintWriter writer = null;

  public void close() throws IOException {
    if (reader != null) {
      reader.close();
    }
    if (writer != null) {
      writer.close();
    }
    if (socket != null) {
      socket.close();
    }
  }

                                          - 127 -
                             Transport - Eigener Transport über Sockets

    public IAccountingService connect(String[] args) throws IOException {
      if (args.length < 2) {
        throw new IllegalArgumentException(
            "Hostname und Port müssen angegeben werden");
      }

        socket = new Socket(args[0], Integer.parseInt(args[1]));
        writer = new PrintWriter(socket.getOutputStream());
        reader = new BufferedReader(
            new InputStreamReader(socket.getInputStream()));

        try {
          return (IAccountingService)requestObject(
              IAccountingService.class, "SocketService", null);
        } catch (AccountingException e) {
          IOException ioe = new IOException(e.getMessage());
          ioe.initCause(e);
          throw ioe;
        }
    }

    private ClientSideResponse request(String oid, String method,
        Object... args);
    protected Object requestObject(Class clas, String oid, String method,
        Object... args);
    protected Object request1(String oid, String method, Object... args)
        throws IOException;
    protected Object request2(String oid, String method, Object... args)
        throws IOException, AccountingException;
}

Code 83: Class SocketProxy

Nach dem Herstellen der Verbindung wird der Ein- bzw. Ausgabestrom des Socket geöffnet.
Die Request-Methoden der Klasse übernehmen dann die eigentliche Kommunikation nach
dem definierten Protokoll (siehe 5.2.4) mit den dafür vorhandenen Klassen.


5.2.3.2. Verarbeitung auf dem Server

Auf dem Server wird nach dem Instanzieren (siehe 5.2.2.2) ebenso verfahren. Sobald die Ser-
ver-Instanz eigenständig läuft wird in der run()-Methode der Ein- bzw. Ausgabestrom des
verbundenen Sockets geöffnet.

public void run() {
  BufferedReader br = new BufferedReader(
        new InputStreamReader(socket.getInputStream()));
  PrintWriter pw = new PrintWriter(socket.getOutputStream());

    while (true) {
      try {
        ServerSideRequest req = new ServerSideRequest(br);
        ServerSideResponse res = process(req);
        res.send();
      } catch (SocketException e) {
        break;
      } catch (IOException e) {
        break;
      }
    }

                                              - 128 -
                           Transport - Eigener Transport über Sockets

    try {
      if (br != null) {
        br.close();
      }
      if (pw != null) {
        pw.close();
      }
      socket.close();
    } catch (IOException e) {
    }
}

Code 84: run()-Methode von SocketServer

Dann werden kontinuierlich die Anfragen eines Clients gelesen, jeweils in der process()-
Methode verarbeitet und eine Antwort an den Client zurückgeschickt.


5.2.4. Definition eines eigenes Protokolls

Zur Kommunikation wird ein selbst definiertes Request-Response-Protokoll verwendet, das
auf einer Socket-Verbindung aufsetzt und alle Daten in String-Form zeilenweise überträgt.

writer = new PrintWriter(socket.getOutputStream())
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()))

Dies erlaubt die unproblematische Verwendung der bei Java mitgelieferten Reader- und Wri-
ter-Klassen.

Das Protokoll orientiert sich im Aufbau an der Objektorientierung von Java und bestehenden
objektorientierten Kommunikationsverfahren (RMI, CORBA) und versucht den Aufruf einer
Methode eines entfernten Objekts nachzubilden.


5.2.4.1. Anfrage

Eine Anfrage besteht aus bei diesem Protokoll aus drei oder mehr Zeilen:

     Objekt-Identifkator
     Methodenname
     [Parameter]
     [Parameter]
     [...]
     Anfrage-Trenner

Resource 11: Socket-Protokoll - Anfrage

Zuerst wird ein Objekt-Identifikator gesendet, der Angibt welches Objekt auf dem Server auf-
gerufen werden soll, sowie der Name der aufzurufenden Methode. Dann folgt eine beliebige
Anzahl Parameter für den Methodenaufruf, die aber nicht notwendigerweise vorhanden sein
müssen. Die Anfrage wird mit einer speziellen Trennzeile abgeschlossen.




                                            - 129 -
                           Transport - Eigener Transport über Sockets


5.2.4.2. Request-Klassen

Als Basis für Anfragen dient die abstrakte Klasse Request, welche die obigen Elemente einer
Anfrage enthält.

package de.fhr.vs_iw.socket;

public abstract class Request {
  protected String method = null;
  protected String oid = null;
  protected ArrayList<Object> params = new ArrayList<Object>();
  protected static final String DELIM = "-#-requestend-#-";
}

Code 85: Class Request

Davon werden die konkreten Klassen zur Verarbeitung einer Anfrage abgeleitet. Durch die
Klasse ClientSideRequest wird das Erstellen und Versenden der Anfrage über den zuvor
geöffneten PrintWriter durchgeführt.

package de.fhr.vs_iw.socket;

public final class ClientSideRequest extends Request {
  protected ClientSideRequest(String oid, String method, Object... args) {
    this.oid = oid;
    this.method = method;
    for (Object o : args) {
      params.add(o);
    }
  }
  protected void send(PrintWriter pw) throws Exception {
    ArrayList<String> strparams = new ArrayList<String>();
    for (Object o : params) {
      strparams.add(StringUnMarshaller.marshall(o));
    }
    pw.println(oid);
    pw.println(method);
    for (String str : strparams) {
      pw.println(str);
    }
    pw.println(DELIM);
    pw.flush();
  }
}

Code 86: Class ClientSideRequest

Der Aufruf von flush() nach jeder Anfrage ist explizit notwendig, da sonst die Daten vom
PrintWriter bzw. vom Socket bis zu einer bestimmten Menge gepuffert würden, die bei
dieser Anwendung praktisch nicht erreicht wird.




                                            - 130 -
                          Transport - Eigener Transport über Sockets

Auf der Server-Seite wird die Anfrage von der zugehörigen Klasse ServerSideRequest ent-
gegengenommen. Mit dem vorhandenen BufferedReader wird solange vom Eingabestrom
des Sockets gelesen bis ein Anfrage-Trenner gefunden wird.
Nach dem Einlesen einer Anfrage stellt die Klasse Methoden zur Verfügung, mit denen die
empfangenen Daten und Werte gezielt abgefragt werden können.

package de.fhr.vs_iw.socket;

public final class ServerSideRequest extends Request {
  protected ServerSideRequest(BufferedReader br) throws Exception {
    oid = readLine(br);
    method = readLine(br);
    ArrayList<String> strparams = new ArrayList<String>();

     while (true) {
       String line = readLine(br);
       if (line.equals(DELIM)) {
         break;
       }
       strparams.add(line);
     }

     for (String str : strparams) {
       params.add(StringUnMarshaller.unmarshall(str));
     }
    }
    private String readLine(BufferedReader br) throws IOException {
      String line = br.readLine();
      if (line == null) {
        throw new IOException("end of input stream");
      }
      return line;
    }
    protected String method() {
      return method;
    }
    protected String oid() {
      return oid;
    }
    protected Object[] params() {
      return params.toArray();
    }
    protected Class[] types() {
      Class[] types = new Class[params.size()];
      for (int i = 0; i < params.size(); i++) {
        types[i] = params.get(i).getClass();
      }
      return types;
    }
}

Code 87: Class ServerSideResponse

Zusätzlich zur Rückgabe-Methode für die empfangenen Parameter params() gibt es die Me-
thode types(), die die Typen der Parameter zurückgibt, was später für den Methodenaufruf
über Reflection (siehe 5.2.6.1) benötigt wird.




                                           - 131 -
                          Transport - Eigener Transport über Sockets


5.2.4.3. Antwort

Die Antwort ist ebenfalls zeilenweise Aufgebaut und hat immer drei Zeilen:

   Objekt-Identifikator
   Rückgabewert
   Antwort-Trennzeile

   Resource 12: Socket-Protokoll - Antwort

Bei einer empfangenen Antwort müssen drei verschiedene mögliche Typen unterschieden
werden:

   •   Entferntes Objekt:
       Wenn die Antwort mit einem Objekt-Identifikator beginnt, hat der Server eine Refe-
       renz zu einem entfernten Objekt zurückgegeben, für das mit diesem Identifikator ein
       lokaler Stellvertreter erzeugt werden muß. Der Rückgabewert stellt den Typ (Class)
       des entfernten Objekts dar. (siehe 5.2.6)

   •   Ergebnis eines Methodenaufrufs:
       Wenn kein Objekt-Identifikator sondern eine Leerzeile am Beginn der Antwort steht
       handelt es sich um den normalen Rückgabewert eines Methodenaufrufs.

   •   Ausnahme:
       Eine Ausnahme wird wie der Rückgabewert eines Methodenaufrufs (ohne Objekt-
       Identifikator) übertragen, und dann clientseitig erkannt und in einer ServerException
       (siehe 5.2.7) verpackt und geworfen.

Die Trennzeile würde hier im Prinzip nicht unbedingt benötigt werden, da die Länge der An-
twort festgelegt ist, wird aber trotzdem für einen "sauberen" Abschluß verwendet.


5.2.4.4. Response-Klassen

Wie auch bei der Anfrage teilt sich die Verarbeitung der Antworten auf Server und Client auf,
ausgehend von der abstrakten Klasse Response.

package de.fhr.vs_iw.socket;

public abstract class Response {
  protected String oid = null;
  protected Object value = null;
  protected static final String DELIM = "-#-responseend-#-";
}

Code 88: Class Response




                                             - 132 -
                          Transport - Eigener Transport über Sockets

Auf der Serverseite wird dann die Antwort als ServerSideResponse analog zu einer Anfrage
auf der Clientseite erzeugt und verschickt.

package de.fhr.vs_iw.socket;

public final class ServerSideResponse extends Response {
  protected ServerSideResponse(Object value) {
    this.oid = null;
    this.value = value;
  }
  protected ServerSideResponse(String oid, Class clas) {
    this.oid = oid;
    this.value = clas;
  }
  protected void send(PrintWriter pw) throws Exception {
    String strvalue = StringUnMarshaller.marshall(value);
    pw.println(oid);
    pw.println(strvalue);
    pw.println(DELIM);
    pw.flush();
  }
}

Code 89: Class ServerSideResponse

Der Client liest die Antwort über die Klasse ClientSideResponse ein. Hier wird dann ge-
prüft ob die Antwort eine Ausnahme ist, und falls ja, diese entsprechend als
ServerException geworfen (siehe 5.2.4.3).

package de.fhr.vs_iw.socket;

public final class ClientSideResponse extends Response {
  protected ClientSideResponse(BufferedReader br) throws Exception {
    oid = readLine(br);
    String strvalue = readLine(br);
    if (!readLine(br).equals(DELIM)) {
      throw new IOException("delimiter expected");
    }
    value = StringUnMarshaller.unmarshall(strvalue);
    if (value instanceof Throwable) {
      throw new ServerException((Throwable)value);
    }
  }
  private String readLine(BufferedReader br) throws IOException {
    String line = br.readLine();
    if (line == null) {
      throw new IOException("end of input stream");
    }
    return line;
  }
  protected String getOid() {
    return oid;
  }
  protected Object getValue() {
    return value;
  }
}

Code 90: Class ClientSideResponse



                                           - 133 -
                           Transport - Eigener Transport über Sockets



5.2.5. Un-/Marshalling der Objekte

Ein wesentlicher Punkt ist nun die Umformung von Java-Objekten bzw. deren datenmäßigem
Inhalt in ein Format, das zu dem String-basierten Protokoll paßt, und auch wieder zurück, das
so genannte Marshalling und Unmarshalling. Dazu wird ein Objekt in folgender grundlegen-
der Form dargestellt

     Java-Type (Klasse) : spezifische Werte

Durch einen Doppelpunkt getrennt wird die Klasse eines Objekt und die für ein Objekt dieser
Klasse spezifischen Werte als eine Zeile im Protokoll übertragen.


5.2.5.1. Die Klasse StringUnMarshaller

Diese Umwandlungen übernimmt die statische Hilfs-Klasse StringUnMarshaller in beiden
Richtungen über die entsprechenden Methoden marshall() und unmarshall().

package de.fhr.vs_iw.socket;

public final class StringUnMarshaller {
  public static synchronized String marshall(Object o) throws Exception {
  public static synchronized Object unmarshall(String line)

    ...
}

Code 91: Class StringUnMarshaller (Teil 1)

Diese beiden Methoden werden beim Verarbeiten von Anfragen und Antworten aufgerufen.


5.2.5.2. Verarbeitung definierter Superklassen

Da nicht wie z.B. bei der Java-Serialisierung annähernd alle Objekte verarbeitet werden müs-
sen, wird der Vorgang über einen vorgegebenen Satz von Klassen gesteuert, deren Objekte
bzw. Objekte von Unterklassen spezifisch umgewandelt werden.

package de.fhr.vs_iw.socket;

public final class StringUnMarshaller {
  private static final HashSet<Class> CLASSES = new HashSet<Class>();
  static {
    CLASSES.add(Class.class);
    CLASSES.add(Set.class);
    CLASSES.add(Integer.class);
    CLASSES.add(Throwable.class);
    CLASSES.add(String.class);
    CLASSES.add(AccountType.class);
    CLASSES.add(Double.class);
  }

    ...
}

Code 92: Class StringUnMarshaller (Teil 2)

                                             - 134 -
                           Transport - Eigener Transport über Sockets


5.2.5.3. Aufruf der un-/marshal-Methoden

Durch Prüfung eines Objekts mit isAssignableFrom() gegenüber allen in der obigen Aufli-
stung festgelegten Klassen wird die seine Zulässigkeit festgestellt. Die entsprechende Metho-
de zur Verarbeitung des Objekts wird aus dem Namen der festgelegten Klasse mit vorange-
stelltem "marshal" bzw. "unmarshal" ermittelt, und über Reflection aufgerufen.

package de.fhr.vs_iw.socket;

public final class StringUnMarshaller {

    ...

    private   static   String   marshalAccountType(Object o) {
    private   static   String   marshalClass(Object o) {
    private   static   String   marshalDouble(Object o) {
    private   static   String   marshalInteger(Object o) {
    private   static   String   marshalSet(Object o)
    private   static   String   marshalString(Object o) {
    private   static   String   marshalThrowable(Object o) {
    private   static   Object   unmarshalAccountType(String s) {
    private   static   Object   unmarshalClass(String s) {
    private   static   Object   unmarshalDouble(String s) {
    private   static   Object   unmarshalInteger(String s) {
    private   static   Object   unmarshalSet(String s)
    private   static   Object   unmarshalString(String s) {
    private   static   Object   unmarshalThrowable(String s)
}

Code 93: Class StringUnMarshaller (Teil 3)


5.2.5.4. Einfache Objekte

Bei einfachen Objekten wird beim Marshalling von den Methoden der Datenwert als String
dargestellt und zurückgegeben.

    •     Integer.toString(((AccountType)object).getId());
    •     ((Class)object).getName();
    •     ((Double)object).toString();
    •     ((Integer)object).toString();
    •     (String)object;

Dem zurückgelieferten Wert wird wie oben angegeben (siehe 5.2.5) die Information über die
Klasse des Objekts vorangestellt.

Im umgekehrten Fall, beim unmarshalling, wird genauso verfahren und mit der Information
aus einem String ein neues Objekt erzeugt. Die vorangestellte Information über die Klasse des
Objekts wird zuvor benutzt um die richtige Methode aufzurufen.

    •     AccountType.getAccountTypeById(Integer.parseInt(string));
    •     Class.forName(string);
    •     Double.valueOf(string);
    •     Integer.valueOf(string);
    •     string


                                             - 135 -
                          Transport - Eigener Transport über Sockets


5.2.5.5. Kollektionen

Die Verarbeitung einer Kollektion (Set) erfordert noch einen weiteren Schritt, da jedes ent-
haltene Objekt selbst auch verarbeitet werden muß.

private static String marshallSet(Object object) throws Exception {
  StringBuffer sb = new StringBuffer();
  for (Object obj : (Set)object) {
    sb.append(marshall(obj));
    sb.append(",");
  }
  return sb.toString();
}

Code 94: Method mashalSet()

Für jedes enthaltene Objekt wird nochmals die marshall()-Methode aufgerufen. Die erhalte-
nen Teil-Strings werden durch Komma getrennt als Gesamt-String-Darstellung für die Kol-
lektion zurückgegeben.

private static Object unmarshallSet(String string) throws Exception {
  HashSet set = new HashSet();
  for (String str : string.split(",")) {
    if (str != null && !str.equals("")) {
      set.add(unmarshall(str));
    }
  }
  return set;
}

Code 95: Method unmarshalSet()

Beim unmarshalling wird der Gesamt-String der Kollektion bei den Kommas in Teil-Strings
getrennt und für jeden Teil-String die unmarshall()-Methode aufgerufen. Die daraus resul-
tierenden Objekte werden in einem neuen Set zurückgegeben.


5.2.5.6. Ausnahmen

Bei Ausnahmen werden neben dem allgemeinen Typen Throwable, auch der spezifische Typ
der Ausnahme und die Nachricht, durch Komma getrennt, übertragen.

private static String marshallThrowable(Object object) {
  Throwable throwable = (Throwable)object;
  return throwable.getClass().getName() + "," + t.getMessage();
}

private static Object unmarshallThrowable(String string) throws Exception {
  int p = string.indexOf(",");
  String type = string.substring(0, p);
  String value = string.substring(p + 1);
  Class c = Class.forName(type);
  Constructor co = c.getDeclaredConstructor(new Class[] { String.class });
  return co.newInstance(new Object[] { value });
}

Code 96: Methoden marshalThrowable() und unmarshalThrowable()

                                           - 136 -
                         Transport - Eigener Transport über Sockets



5.2.6. Entfernte Objekte

Wie angesprochen (siehe 5.2.4) arbeitet das Protokoll mit der Idee von eindeutig identifizier-
baren entfernten Objekten deren Methoden aufgerufen werden.


5.2.6.1. Aufruf der Methoden über Reflection

Der SocketServer erhält vom Client in jeder Anfrage einen Objekt-Identifikator (OID) durch
den er das gewünschte Objekt in einer HashMap auffinden kann.

   private final HashMap<String, Object> objects
     = new HashMap<String, Object>();

   Object object = objects.get(request.oid());

Sofern unter der angegebenen OID ein Objekt vorhanden ist, wird die in der Anfrage angege-
bene Methode davon über Reflection aufgerufen.

   Class clas = object.getClass();
   Method m = clas.getMethod(request.method(), request.types());
   Object result = m.invoke(object, params);

Das Ergebnis (result) des Methodenaufrufs wird zurückgegeben und in der Antwort an den
Client geschickt (siehe 5.2.4.3).

   response = new ServerSideResponse(result);

Durch diese Vorgehensweise wird eine Anfrage in der process()-Methode durch den
SocketServer verarbeitet. Durch das von den Anfrage- und Antwort-Klassen übernommene
Un-/Marshalling kann der Server die Aufrufe der Methoden der "entfernten" Objekte völlig
unproblematisch ausführen und das Ergebnis zurückschicken.


5.2.6.2. Übertragung der entfernten Objekte

Lediglich wenn der Rückgabewert eines Methodenaufrufs (getAccountByNumber(),
getAccountingEntryById()) ein Objekt liefert, das keinen Datenwert für sich, sondern
selbst wieder ein entferntes Objekt (IAccount, IAccountingEntry) darstellt wird etwas von
der normalen Verarbeitung abgewichen und das Objekt speziell behandelt.

   if (result instanceof IAccount) {
     response = remoteObjectResponse(result, IAccount.class);
   } else if (result instanceof IAccountingEntry) {
     response = remoteObjectResponse(result, IAccountingEntry.class);
   } else {
     response = new ServerSideResponse(result);
   }

Durch die Methode remoteObjectResponse() wird eine passende Antwort erzeugt und der
Client erhält nicht das Objekt selbst, sondern nur die OID, um es später entfernt aufrufen zu
können.


                                           - 137 -
                          Transport - Eigener Transport über Sockets

private ServerSideResponse remoteObjectResponse(Object o, Class c) {
  String oid = getOid(o);
  if (oid == null) {
    oid = genOid(o);
    objects.put(oid, o);
  }
  return new ServerSideResponse(oid, c);
}

Code 97: Methode remoteObjectResponse()

Sofern für das Objekt noch keine OID vergeben wurde (getOid()), wird eine neue erzeugt
(genOid()), und damit das Objekt in der HashMap zur Verwaltung der entfernten Objekte
abgelegt und in der Antwort zusammen mit dem Objekt-Typ (Class) an den Client geschickt.

Um festzustellen ob für ein Objekt bereits eine OID vergeben wurde, müssen die Schlüssel
der HashMap durchsucht werden.

private String getOid(Object object) {
  for (Entry<String, Object> ent : objects.entrySet()) {
    if (ent.getValue() == object) {
      return ent.getKey();
    }
  }
  return null;
}

private String genOid(Object object) {
  return Integer.toHexString(object.hashCode());
}

Code 98: Methoden getOid() und genOid()

Als OID für ein Objekt wird die hexadezimale Darstellung seines hashCode() in der Virtuel-
len Maschine verwendet. Aber auch jede andere Methode, die innerhalb einer SocketServer-
Instanz eine eindeutige OID liefert (z.B. einfach eine Nummer hochzählen), ist anwendbar, da
ein Client bei diesem verteilten System immer nur mit genau einem Server verbunden ist und
dessen Objekte aufruft.

Da ein Client als Ausgangspunkt aller Aufrufe Zugriff auf eine Instanz der entfernten Service-
Klasse benötigt, wird eine spezielle OID für den Einstieg definiert.

   if (req.oid().equals("SocketService")) {
     return new ServerSideResponse(serviceOid, IAccountingService.class);
   }

Weil die Erstellung eines eigenen Namensdienstes, der normalerweise zu einem verteilten
System gehört, doch ein gewissen Aufwand darstellt, wird einfach ein festgelegter String
"SocketService" wie eine OID verwendet und extra abgefragt.

   requestObject(IAccountingService.class, "SocketService", null);

Bei einer Anfrage mit dieser speziellen OID wird als Antwort die eigentliche OID
(serviceOid) eines entfernten Service-Objekts zurückgegeben mit der ein lokaler Stellvertre-
ter dafür erzeugt werden kann. Das Objekt und seine OID werden zusammen mit einer Ser-
ver-Instanz erzeugt und in der HashMap der entfernten Objekte abgelegt.

                                           - 138 -
                          Transport - Eigener Transport über Sockets


5.2.6.3. Lokale Stellvertreter

Da ein entferntes Objekt auch entfernt bleibt, auf der Clientseite jedoch mit den Java-
Objekten der Buchhaltung gearbeitet werden soll, ist für jedes entfernte Objekt ein zugehöri-
ges lokales Stellvertreter-Objekt (Stub) (AccountingService, Account, AccountingEntry)
notwendig, das entsprechend die jeweilige Buchhaltungsschnittstelle implementiert.

Für jede OID, die vom entfernten Server zurückgeschickt wird, wird ein Stellvertreter-Objekt
erzeugt. Jede Methode eines solchen lokalen Objekts ruft lediglich mit der OID die zugehöri-
ge Methode des entfernten Objekts auf, unter Verwendung der in der Klasse SocketProxy
definierten Methoden request1(), request2() und requestObject().

Die Buchhaltungsmethoden sind dann vom Prinzip her wie die folgenden Beispiele aus der
Klasse AccountingService implementiert.

public Set<Integer> getAccounts() throws IOException {
  return (Set<Integer>)proxy.request1(oid, "getAccounts");
}

private HashMap<Integer, Account> accounts
  = new HashMap<Integer, Account>();

public IAccount getAccountByNumber(Integer number)
    throws IOException, AccountingException {
  Account account = accounts.get(number);
  if (account == null) {
    account = (Account)proxy.requestObject(IAccount.class, oid,
        "getAccountByNumber", number);
    accounts.put(number, account);
  }
  return account;
}

public void removeAccount(Integer number)
    throws IOException, AccountingException {
  proxy.request2(oid, "removeAccount", number);
}

Code 99: Implementierung einiger Buchhaltungsmethoden (Socket - AccountingService)

Jede Methode einer Buchhaltungsschnittstelle ruft die passende Request-Methode mit allen
übergebenen Parametern auf und gibt das Ergebnis direkt zurück. Lediglich die Methoden
getAccountByNumber() und getAccountingEntryById() haben noch eine weitere Funkti-
on. Hier werden die entfernten Objekte für Konten bzw. Buchungen angefragt und die erhal-
tenen lokalen Stellvertreter-Objekte werden in HashMaps gespeichert, damit nicht ständig
neue Stellvertreter-Objekte erzeugt werden.


5.2.6.4. Anfrage-Methoden

Die Anfrage-Methoden request1() und request2() geben jeweils einfache Werte zurück,
unterscheiden sich in ihrer Signatur aber in den geprüften Ausnahmen, die geworfen werden,
damit diese Anfrage-Methoden aus den jeweiligen Buchhaltungs-Methoden einfach aufgeru-
fen werden können.


                                             - 139 -
                          Transport - Eigener Transport über Sockets

protected Object request1(String oid, String method, Object... args)
    throws IOException {
  try {
    return request2(oid, method, args);
  } catch (AccountingException e) {
    // Eine aufgetretene Buchhaltungsausnahme wird in eine
    // E/A-Ausnahme umgewandelt
    IOException ioe = new IOException();
    ioe.initCause(e);
    throw ioe;
  }
}

Code 100: Method request1() (SocketProxy)

Die Methode request1() wirft eine lediglich eine IOException bei Kommunikationsfehlern,
und wird von den Methoden verwendet, die nach der Definition in der Schnittstelle auch nur
eine IOException werfen dürfen. Intern wird wiederum die Methode request2() aufgerufen
und lediglich die zusätzliche AccountingException als IOException verpackt.

protected Object request2(String oid, String method, Object... args)
    throws IOException, AccountingException {
  try {
    return request(oid, method, args).getValue();
  } catch (IOException e) {
    // Eine E/A-Ausnahme wird direkt geworfen
    throw e;
  } catch (ServerException e) {
    // Eine Buchhaltungsausnahme auf dem Server wird direkt geworfen
    if (e.getCause() instanceof AccountingException) {
      throw (AccountingException)e.getCause();
    }
    // Jede andere Server-Ausnahme wird als E/A-Ausnahme geworfen
    IOException ioe = new IOException(e.getMessage());
    ioe.initCause(e);
    throw ioe;
  } catch (final Exception e) {
    // Jede andere Ausnahme wird als E/A-Ausnahme geworfen
    IOException ioe = new IOException();
    ioe.initCause(e);
    throw ioe;
  }
}

Code 101: Method request2() (SocketProxy)

Auch die Methode request2() verpackt nur mögliche Ausnahmen auf die passende Weise,
so daß letztlich nur IOExceptions und AccountingExceptions geworfen werden. Die ei-
gentliche Funktionalität zur Datenübertragung durch die Request-Klasse wird in der privaten
Methode request() aufgerufen.

private ClientSideResponse request(String oid, String method,
    Object... args) throws Exception {
  ClientSideRequest req = new ClientSideRequest(oid, method, args);
  req.send(writer);
  return new ClientSideResponse(reader);
}

Code 102: Method request() (SocketProxy)


                                            - 140 -
                          Transport - Eigener Transport über Sockets

Die Methode requestObject() schließlich gibt nicht nur einen einfachen Wert, sondern ein
Objekt der angegebenen Klasse als lokalen Stellvertreter für ein entferntes Objekt zurück.
Damit das entfernte Objekt durch ein Stellvertreter-Objekt austauschbar ist, muß mit Schnitt-
stellen gearbeitet werden.

Die Verarbeitung von Ausnahmen erfolgt auf die gleiche Weise wie in der Methode
request2().

protected Object requestObject(Class clas, String oid, String method,
    Object... args) throws IOException, AccountingException {

    if (!clas.isInterface()) {
      throw new IOException("requested type is not an interface");
    }

    ClientSideResponse res;

    try {
      res = request(oid, method, args);
    } catch (...) {
      ...
    }

    if (res.getOid() == null) {
      throw new IOException("no remote object received");
    }
    if (!res.getValue().equals(clas)) {
      throw new IOException("unexpected remote object type");
    }
    if (clas.equals(IAccount.class)) {
      return new Account(this, res.getOid());
    } else if (clas.equals(IAccountingEntry.class)) {
      return new AccountingEntry(this, res.getOid());
    } else if (clas.equals(IAccountingService.class)) {
      return new AccountingService(this, res.getOid());
    }

    throw new IOException("no proxy for remote object available");
}

Code 103: Method requestObject() (SocketProxy)

Je nachdem, ob eine OID von Server empfangen wurde, wird damit ein Objekt instanziert
oder eine Ausnahme geworfen. Im Beispiel erfolgt die Instanzierung direkt, durch Unter-
scheidung der gewünschten Schnittstelle über if-Anweisungen, wobei es sicher elegantere
Möglichkeiten gäbe ein Stellvertreter-Objekt für eine angegebene Schnittstelle zu erhalten
(Zuordnung in einer Map, dynamische Stub-Generierung).


5.2.7. Fazit

Dieses Beispiel zeigt, daß es einen nicht unerheblichen Aufwand darstellt, ein umfassendes
objektorientiertes verteiltes System zu entwickeln, wobei hier etliche Punkte noch gar nicht
betrachtet wurden (z.B. verteilte Garbage-Collection, Namensdienst). Glücklicherweise gibt
es einige ausgereifte Technologien und Bibliotheken, die unter Java verwendbar sind, und alle
notwendigen Funktionalitäten für den Aufbau von verteilten Systemen zur Verfügung stellen.


                                             - 141 -
                        Transport - Remote Method Invocation (RMI)


5.3. Remote Method Invocation (RMI)
5.3.1. Allgemeines

Remote Method Invocation (RMI) [33] ist die Java-eigene Art, die Methoden von entfernten
Objekten aufzurufen. Das bedeutet, daß sich das Objekt auf jeden Fall in einer separaten vir-
tuellen Maschine, die auch auf einem anderen Rechner laufen kann, befindet [34].




Abbildung 67: Schematischer Ablauf von RMI

Als Basis dient ein Namensdienst, bei dem sich Objekte, die einen entfernten Dienst anbieten,
unter einem festgelegten Namen registrieren (1), und dann über den Namen abgerufen werden
können (2). Man erhält dann einen lokalen Stellvertreter für dieses Objekt, von dem die Me-
thoden aufgerufen (3) und auf dem entfernten System ausgeführt (4) werden. Wegen dem
Austausch durch lokale Stellvertreter werden die Methoden in einer Schnittstelle definiert.
Die Komplexität der Netzwerk-Kommunikation wird vor der Anwendung verborgen [36].




Abbildung 68: RMI-Kommunikations-Architektur

Auf die genaue Funktionsweise von RMI soll hier nicht eingegangen, sondern hauptsächlich
die Anbindung an das Buchhaltungssystem beschrieben werden (siehe auch [35]).

                                             - 142 -
                       Transport - Remote Method Invocation (RMI)



5.3.2. Entfernte Objekte

Ein entferntes Objekt basiert auf einer Schnittstelle und einer implementierenden Klasse, die
nach den folgenden Vorgaben aufgebaut wird.


5.3.2.1. Remote-Interface

Wie bereits angesprochen wird eine Schnittstelle für die entfernten Methoden definiert, die
lediglich einige Kriterien erfüllen muß:

   •   Vom Interface java.rmi.Remote abgeleitet, damit die Schnittstelle als Remo-
       te-Interface anerkannt wird
   •   Alle definierten Methoden müssen eine RemoteException oder eine Superklasse da-
       von werfen
   •   Die verwendeten Parameter und Rückgabetypen müssen serialisierbar
       (Interface java.io.Serializable) sein

Die im Buchhaltungsmodell definierten Schnittstellen (IAccountingServce, IAccount,
IAccountingEntry, siehe 2.2) erfüllen diese Anforderungen bereits, und können direkt als
Remote-Interface verwendet werden.


5.3.2.2. Remote-Object

Wie in den vorigen Kapiteln muß nun eine Service-Klasse, Konto-Klasse und Buchungs-
Klasse erstellt werden, die wieder jeweils eine der Schnittstellen implementieren. Durch die
Ableitung von java.rmi.Remote erkennt RMI Objekte als Remote-Object an.
Zugriff auf die Service-Klasse selbst erhält ein Client über den Namensdienst. Aber wenn nun
beim Aufruf von getAccountByNumber() oder getAccountEntryById() der Service-Klasse
ein Konten- oder Buchungsobjekt zurückgegeben wird, erhält ein Client automatisch nur den
lokalen Stellvertreter für das zurückgegebene Objekts.
Es reicht aber nicht, nur das Remote-Interface zu implementieren. Die Klassen von entfernten
Objekten müssen zusätzlich noch um einige Fähigkeiten bzgl. der Kommunikation über TCP-
Verbindungen erweitert werden. Dies geschieht durch das Ableiten von der Klasse
java.rmi.server.UnicastRemoteObject und den Aufruf des Konstruktors der Superklasse.
Dies gilt für alle drei Klassen unseres Systems (Service, Konto, Buchung).


5.3.3. Anbindung an die Datenhaltung

Auch die RMI-Implementierung zur Datenübertragung wird an eines der bestehenden Module
zur Datenhaltung angebunden.


5.3.3.1. Proxy-Instanzierung

Wie bei jedem Client eines Buchhaltungssystems wird eine Instanz der angegebenen Pro-
xy-Klasse gebildet und davon die connect()-Methode aufgerufen. Damit kann die RMI-
Implementierung auf die Objekte des Datenhaltungmoduls zugreifen.

                                           - 143 -
                         Transport - Remote Method Invocation (RMI)


5.3.3.2. Weitergabe der Aufrufe

Alle Aufrufe der entfernten Methoden werden direkt an die Datenhaltung weitergegeben. Das
RMI-System "stülpt" sich praktisch nur über die Datenhaltung und übernimmt die Datenüber-
tragung, was allerdings manuell zu implementieren ist.

public Double calculateBalance(Integer number)
    throws IOException, AccountingException {
  return nextService.calculateBalance(number);
}

Code 104: Method calculateBalance() bei RMI

Dies hat aber nichts mit RMI an sich zu tun, sondern mit dem Aufbau des Buchhahltungssy-
stems, und muß deshalb für jede einzelne Methode manuell ausprogrammiert werden.


5.3.4. Server - RMI-Registry

Im Prinzip kann man im ersten Ansatz nicht von einem RMI-Server sprechen, da zwei Dien-
ste (siehe 5.3.1) benötigt werden, zum Einen der Namensdienst und zum anderen das entfern-
te Objekt selbst.


5.3.4.1. Registry starten

Bei größeren Anwendungen läuft der Namensdienst, die RMI-Registry, meistens irgendwo als
eigenständiger Dienst, bei dem sich Objekte, die auf verschiedenen Systemen zur Verfügung
stehen zentral registrieren können. Beim JDK ist eine RMI-Registry enthalten, die durch

   rmiregistry [Optionen] [Port]

auf dem angegebenen Port (standardmäßig 1099) gestartet werden kann.


5.3.4.2. Registry einbinden

Über die Schnittstelle java.rmi.Registry wird in einer Anwendung auf den Namensdienst
zugegriffen. Eine Instanz dieser Klasse, und damit eine Verbindung zu einer Registry erhält
man durch die Klasse java.rmi.LocateRegistry.

package java.rmi.registry;

public class LocateRegistry {
  static Registry createRegistry(int port);
  static Registry getRegistry();
  static Registry getRegistry(int port);
  static Registry getRegistry(String host);
  static Registry getRegistry(String host, int port);
}

Code 105: Class LocateRegistry



                                              - 144 -
                          Transport - Remote Method Invocation (RMI)

Mit den verschiedenen Methoden kann auf eine Registry, die auf dem angegebenen Host und
dem angegebenen Port läuft zugegriffen werden. Interessant ist aber auch die Methode
createRegistry(), wodurch eine Anwendung ihre eigene Registry erzeugen, und damit Ob-
jekte zur Verfügung stellen kann.


5.3.4.3. Objekt registrieren

Nachdem man sich zu einer Registry verbunden, oder selbst eine erzeugt hat, können mit den
Methoden einer Registry-Instanz die entfernten Objekte registriert oder geholt werden.

package java.rmi.registry;

public interface Registry extends Remote {
  void bind(String name, Remote obj);
  String[] list();
  Remote lookup(String name);
  void rebind(String name, Remote obj);
  void unbind(String name);
}

Code 106: Interface Registry

Durch den Aufruf von bind() bzw. rebind() wird ein Objekt unter dem angegebenen Na-
men registriert. Mit list() kann eine Liste aller registrierten Namen ausgegeben werden.


5.3.4.4. Der vollständige Server

Der RMIServer stellt beide nötigen Dienste, Registry und entferntes Objekt, zur Verfügung.

package de.fhr.vs_iw.rmi;

public final class RMIServer {
  public static void main(String[] args) {
    try {
      if (args.length <= 0) {
        System.err.println("Eine Proxy-Klasse muß angegeben werden");
        return;
      }

         IAccountingProxy localProxy = Utilities.loadProxy(args[0]);
         IAccountingService nextService
           = localProxy.connect(Utilities.shiftArgs(args, 1));
         IAccountingService service = new AccountingService(nextService);

          Registry registry
            = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
          registry.rebind("RMIAccountingService", service);
        } catch (Exception e) {
          e.printStackTrace();
        }
    }
}

Code 107: Class RMIServer




                                           - 145 -
                       Transport - Remote Method Invocation (RMI)

Zuerst wird von der als Kommandozeilenargument angegebenen Proxy-Klasse (von einem
Modul der Datenhaltung) eine Instanz gebildet, um damit ein Objekt der zugehörigen Service-
Klasse zu erhalten. Alle weiteren Kommandozeilenargumente werden der connect()-
Methode zum Laden des Service-Objekts übergeben.
Mit diesem Service-Objekt wird ein RMI-Service-Object erzeugt, das wie beschrieben
(siehe 5.3.3.2) die RMI-Methodenaufrufe weitergibt. Anschließend wird eine eigene
RMI-Registry auf dem Standardport 1099 erzeugt, und das RMI-Service-Object unter dem
Namen "RMIAccountingService" dort registriert.


5.3.5. Entfernte Buchhaltungsobjekte

Die Funktionalität der RMI-Buchhaltung basiert schließlich auf entfernten Objekten und de-
ren lokalen Stellvertretern.


5.3.5.1. Service-Klasse

Die RMI-Service-Klasse kapselt die Service-Klasse eines anderen Buchhaltungsmoduls
(IAccountingService nextService) für die Verwendung mit RMI.

package de.fhr.vs_iw.rmi;

public final class AccountingService extends UnicastRemoteObject
    implements IAccountingService {

  private HashMap<Integer, Account> accountMap
    = new HashMap<Integer, Account>();

  private HashMap<Integer, AccountingEntry> entryMap
    = new HashMap<Integer, AccountingEntry>();

  private final IAccountingService nextService;

  protected AccountingService(IAccountingService next) throws IOException {
    super();
    nextService = next;
  }

  public IAccount getAccountByNumber(Integer number)
      throws IOException, AccountingException {
      Account account = accountMap.get(number);
    if (account == null) {
      account = new Account(nextService.getAccountByNumber(number));
      accountMap.put(number, account);
    }
    return account;
  }

  public IAccountingEntry getAccountingEntryById(Integer id)
      throws IOException, AccountingException {
    AccountingEntry entry = entryMap.get(id);
    if (entry == null) {
      entry = new AccountingEntry(nextService.getAccountingEntryById(id));
      entryMap.put(id, entry);
    }
    return entry;
  }

                                          - 146 -
                        Transport - Remote Method Invocation (RMI)

    public Double calculateBalance(Integer number)
        throws IOException, AccountingException {
      return nextService.calculateBalance(number);
    }

    ...

    <More Service-Methods>
}

Code 108: Class AccountingService (RMI)

Die Klasse ist von UnicastRemoteObject abgeleitet, und ruft diese (super()) im Konstruk-
tor auf. Jede Methode der RMI-Service-Klasse ruft lediglich die korrespondierende Methode
des gekapselten Service-Objekts auf. Als einzige Funktionalität werden die Objekte für Kon-
ten und Buchungen in HashMaps verwaltet.


5.3.5.2. Konto-Klasse

Die RMI-Konto-Klasse unterscheidet sich vom Prinzip her nicht von der Service-Klasse.
Auch hier werden nur Methodenaufrufe an ein anderes gekapseltes Kontenobjekt weitergege-
ben.

package de.fhr.vs_iw.rmi;

public final class Account extends UnicastRemoteObject
    implements IAccount {

    private final IAccount nextAccount;

    protected Account(IAccount next) throws IOException {
      super();
      nextAccount = next;
    }
    public Set<Integer> getCreditEntries() throws IOException {
      return nextAccount.getCreditEntries();
    }
    public Set<Integer> getDebitEntries() throws IOException {
      return nextAccount.getDebitEntries();
    }
    public String getDescription() throws IOException {
      return nextAccount.getDescription();
    }
    public Integer getNumber() throws IOException {
      return nextAccount.getNumber();
    }
    public AccountType getType() throws IOException {
      return nextAccount.getType();
    }
    public void setDescription(String description) throws IOException {
      nextAccount.setDescription(description);
    }
    public void setType(AccountType type) throws IOException {
      nextAccount.setType(type);
    }
}

Code 109: Class Account (RMI)


                                          - 147 -
                        Transport - Remote Method Invocation (RMI)


5.3.5.3. Buchungs-Klasse

Die RMI-Buchungs-Klasse weicht auch nicht von Aufbau und Funktionalität der
RMI-Service-Klasse bzw. der RMI-Konto-Klasse ab.

package de.fhr.vs_iw.rmi;

public final class AccountingEntry extends UnicastRemoteObject
    implements IAccountingEntry {

    private IAccountingEntry nextEntry;

    protected AccountingEntry(IAccountingEntry next) throws IOException {
      super();
      nextEntry = next;
    }

    public Double getAmount() throws IOException {
      return nextEntry.getAmount();
    }
    public Integer getCreditAccount() throws IOException {
      return nextEntry.getCreditAccount();
    }
    public Integer getDebitAccount() throws IOException {
      return nextEntry.getDebitAccount();
    }
    public Integer getId() throws IOException {
      return nextEntry.getId();
    }
    public String getText() throws IOException {
      return nextEntry.getText();
    }
    public void setAmount(Double amount) throws IOException {
      nextEntry.setAmount(amount);
    }
    public void setCreditAccount(Integer number)
        throws IOException, AccountingException {
      nextEntry.setCreditAccount(number);
    }
    public void setDebitAccount(Integer number)
        throws IOException, AccountingException {
      nextEntry.setDebitAccount(number);
    }
    public void setText(String text) throws IOException {
      nextEntry.setText(text);
    }
}

Code 110: Class AccountingEntry (RMI)



5.3.6. Aufruf der entfernten Buchhaltung

Zum Aufruf der entfernten Buchhaltung muß die beim Start des Servers registrierte Instanz
des entfernten Service-Objekts abgerufen werden.




                                         - 148 -
                        Transport - Remote Method Invocation (RMI)


5.3.6.1. Anfrage an den Namensdienst

Dazu wird auf der Clientseite die lookup()-Methode der Klasse java.rmi.Naming verwen-
det, die ein entferntes Objekt direkt, durch Angabe ein Art URL in der Form

     //host:port/name

abruft. Bei diesem Vorgang wird automatisch (seit Java 5) ein lokales Stellvertreter-Objekt
(Stub) erzeugt, das man direkt zum definierten Remote-Interface "casten" und verwenden
kann (Strichwort: dynamische Proxy-Generierung).


5.3.6.2. RMI-Proxy-Klasse

Die Proxy-Klasse muß dann auf der Clientseite nur noch diesen lookup()-Aufruf durchfüh-
ren, wobei nur die Ausnahme NotBoundException abgefangen werden muß, die auftritt, falls
das gewünschte Objekt mit dem angegebenen Namen nicht registriert ist.

package de.fhr.vs_iw.rmi;

public final class RMIProxy implements IAccountingProxy {
  private IAccountingService remoteService = null;

    public RMIProxy() {}

    public void close() {
      remoteService = null;
    }

    public IAccountingService connect(String[] args) throws IOException {
      if (args.length <= 0) {
        throw new IllegalArgumentException(
            "Als Argument muß der Host[:Port]"
          + " der RMI-Registry angegeben werden");
      }
      try {
        remoteService = (IAccountingService)
          Naming.lookup("//" + args[0] + "/RMIAccountingService");
      } catch (NotBoundException e) {
        throw new IOException(
            "Entferntes IAccountingService-Objekt nicht gebunden");
      }
      return remoteService;
    }
}

Code 111: Class RMIProxy

Da die Service-Schnittstelle der Buchhaltung als Remote-Interface verwendet wurde, kann das
Objekt, das man durch den Aufruf der lookup()-Methode erhält, direkt von der connect()-
Methode zurückgegeben werden, ohne daß dafür ein Adapter erforderlich wäre. Und da auch
die Konto- und die Buchungs-Schnittstelle von java.rmi.Remote abgeleitet wurden, werden
deren Methoden entfernt aufgerufen, wobei diese Objekte nicht unter einem Namen registriert
werden müssen, weil RMI automatisch dafür sorgt, daß ein Client nur den Stub eines entfern-
ten Objekts erhält, wenn es als Rückgabewert einer Methode auftritt.


                                          - 149 -
                        Transport - Remote Method Invocation (RMI)



5.3.7. Codebase

Eine der Möglichkeiten von Java besteht darin, daß Java-Klassen zur Laufzeit dynamisch von
jeder beliebigen URL (sofern nicht durch Sicherheitseinstellungen eingeschränkt) in eine Vir-
tuelle Maschine nachgeladen werden können. Dadurch können Java-Programme ausgeführt
werden, die auf einem System nicht im Vorfeld installiert wurden [77].
Für RMI bedeutet das konkret, daß die class-Dateien der entfernten Objekte und Schnittstel-
len nicht lokal auf einem Rechner vorhanden sein müssen, sondern bei Aufruf der lookup()-
Methode automatisch nachgeladen werden können.




Abbildung 69: Nachladen von Java-Klassen

Damit das RMI-System fehlende Klassen auffinden kann, muß eine Codebase eingerichtet
werden. Dies ist normalerweise ein HTTP- oder FTP-Server, der die notwendigen class-
Dateien zur Verfügung stellt. Es können aber auch "file:///" URLs verwendet werden,
wenn sich Client und Server auf demselben System befinden, oder aber die Dateien auf ande-
re Weise (NFS, CIFS, SMB, etc.) zugänglich sind.
Bei der RMI-Registry wird dann durch die Eigenschaft java.rmi.server.codebase ange-
geben unter welcher URL die Codebase eingerichtet wurde. Beim Start der Virtuellen Ma-
schine für den RMI-Server bzw. die RMI-Registry muß die Einstellung als Kommandozeilen-
parameter angegeben werden.

   -Djava.rmi.server.codebase=http://webserver/classdir/

Diese Einstellung wird dann jedem Client, der nach einem entfernten Objekt anfragt, automa-
tisch mitgeteilt.


5.3.8. Fazit

Durch die Verwendung von RMI können mit Java sehr einfach verteilte Systeme realisiert
werden. Ein Problem besteht erst dann, wenn ein verteiltes System plattform- und systemu-
nabhängig sein soll, weil RMI auf die ausschließliche Verwendung unter Java ausgelegt und
im Kern der Sprache integriert ist.

                                           - 150 -
                                     Transport - CORBA


5.4. CORBA
5.4.1. Allgemeines

CORBA (Common Object Request Broker Architecture) ist ein plattform- und systemunab-
hängiger Standard der Object Management Group (OMG) [79] zur Entwicklung von verteil-
ten Systemen mit dem IIOP-Protokoll, für den es entsprechende Anbindungen an verschiede-
ne Programmierumgebungen gibt [80].


5.4.1.1. Object Request Broker (ORB)




5.4.1.2. Interoperable Object Reference (IOR)




5.4.1.3. Portable Object Adapter




5.4.1.4. Tie Mechanismus




5.4.1.5. CORBA-Dienste

Zusätzlich zum Protokoll für den Aufruf entfernter Objekte definiert CORBA noch einige
Dienste mit einer definierten IDL-Schnittstelle (siehe 5.4.2) die bei der Verwaltung der Ob-
jekte und dem Aufbau eines verteilten Systems hilfreich sind [81].

   •   Naming Service
   •   Trading Service
   •   Event Service
   •   Lifecycle Service
   •   Relationship Service
   •   Externalization Service
   •   Persistent Object Service
   •   Concurrency Control Service
   •   Transaction Service
   •   Property Service
   •   Licensing Service
   •   Object Collection Service
   •   Query Service
   •   Time Service
   •   Security Service

                                          - 151 -
                             Transport - CORBA




5.4.1.6. Implementierungen




                                  - 152 -
                                    Transport - CORBA



5.4.2. Interface Definition Language

CORBA basiert auf einer eigenen Beschreibungssprache, die von Plattformen und Program-
miersprachen unabhängig ist, um einen Dienst, der entfernt aufrufbar sein soll, zu beschrei-
ben. In dieser Beschreibung werden im wesentlichen Schnittstellen definiert, deren Metho-
denaufrufe an ein entferntes System übertragen werden. Zu den Schnittstellen werden weitere
notwendige Bestandteile des Systems definiert (Ausnahmen, Enumerationen, Arrays), die
dann zusammen mit den Basisdatentypen von CORBA verwendet werden können.


5.4.2.1. Buchhaltungs-IDL

Für die Buchhaltungsanwendung bedeutet dies, daß alle Funktionalitäten der Buchhaltungs-
schnittstellen nochmals als CORBA-IDL definiert werden müssen.

module corba {

  typedef sequence<long> CorbaIntegerSet;

  exception CorbaAccountingException {
    string message;
  };

  exception CorbaIOException {
    string message;
  };

  enum CorbaAccountType {
    ACTIVE, AMOUNT, EXPENSE, PASSIVE, PROCEEDS
  };

  interface CorbaAccount {
    CorbaIntegerSet getCreditEntries()
      raises (CorbaIOException);
    CorbaIntegerSet getDebitEntries()
      raises (CorbaIOException);
    string getDescription()
      raises (CorbaIOException);
    long getNumber()
      raises (CorbaIOException);
    CorbaAccountType getType()
      raises (CorbaIOException);
    void setDescription(in string description)
      raises (CorbaIOException);
    void setType(in CorbaAccountType type)
      raises (CorbaIOException);
  };




                                          - 153 -
                                    Transport - CORBA

  interface CorbaAccountingEntry {
    double getAmount()
      raises (CorbaIOException);
    long getCreditAccount()
      raises (CorbaIOException);
    long getDebitAccount()
      raises (CorbaIOException);
    long getId()
      raises (CorbaIOException);
    string getText()
      raises (CorbaIOException);
    void setAmount(in double amount)
      raises (CorbaIOException);
    void setCreditAccount(in long creditAccount)
      raises (CorbaAccountingException, CorbaIOException);
    void setDebitAccount(in long debitAccount)
      raises (CorbaAccountingException, CorbaIOException);
    void setText(in string text)
      raises (CorbaIOException);
  };

  interface CorbaAccountingService {
    double calculateBalance(in long number)
      raises (CorbaAccountingException, CorbaIOException);
    void createAccount(in long number, in CorbaAccountType type,
      in string description)
      raises (CorbaAccountingException, CorbaIOException);
    long createAccountingEntry(in long debitAccount, in long creditAccount,
      in double amount, in string text)
      raises (CorbaAccountingException, CorbaIOException);
    CorbaAccount getAccountByNumber(in long number)
      raises (CorbaAccountingException, CorbaIOException);
    CorbaIntegerSet getAccountingEntries()
      raises (CorbaIOException);
    CorbaAccountingEntry getAccountingEntryById(in long id)
      raises (CorbaAccountingException, CorbaIOException);
    CorbaIntegerSet getAccounts()
      raises (CorbaIOException);
    void removeAccount(in long number)
      raises (CorbaIOException, CorbaAccountingException);
    void removeAccountingEntry(in long id)
      raises (CorbaIOException, CorbaAccountingException);
  };
};

Resource 13: CORBA Accounting.idl

Von der Logik her entspricht der Aufbau der IDL vollkommen den Schnittstellen und Klassen
aus dem Buchhaltungsmodel. Die Kontotypen werden als Enumeration definiert und die bei-
den bekannten Typen für Ausnahmen eingeführt.
Anstatt eines Set zur Auflistung der Kontonummern bzw. Buchungs-IDs wird in der IDL ein
benutzerdefinierter Datentyp als sequence angegeben. Die sequence definiert hier als In-
haltstyp den Basisdatentyp long, könnte aber auch entfernte Objekte zurückgeben.
Alle Definitionen werden von der globalen Angabe module umschlossen, die zur Abgrenzung
von Namensräumen dient. Für den Aufbau von hierarchischen Namensräumen können die
module Angaben geschachtelt werden.




                                         - 154 -
                                      Transport - CORBA


5.4.2.2. Zuordnung der IDL-Elemente zu Java-Typen

Für die meisten Programmiersprachen gibt es eine Zuordnung von Datentypen zu den Ele-
menten der IDL. Hier werden die definierten Schnittstellen und die CORBA-Basis-Typen
entsprechend auf Java-Klassen und Java-Typen abgebildet (IDL-to-Java-Mapping).

Einfache Datentypen
boolean             Boolean                       long                    Integer
char                Char                          unsigned long           Integer
wchar               Char                          long long               Long
octet               Byte                          unsigned long long      Long
string              java.lang.String              float                   Float
wstring             java.lang.String              double                  Double
short               Short
unsigned short      Short
                                                  long double             *unmapped*

Tabelle 13: Zuordnung der Basisdatentypen

Die einfachen Datentypen der IDL werden in Java durch die Wrapper-Klassen der primitiven
Java-Datentypen behandelt. Für andere Angaben in der IDL sind entsprechend komplexere
Java-Konstrukte notwendig.

Komplexe IDL-Angaben
module     Java-Package
interface
           Schnittstelle (org.omg.CORBA.Object, org.omg.CORBA.portable.IDLEntity)
           Stub-Klasse (org.omg.CORBA.portable.ObjectImpl)
enum       Klasse (org.omg.CORBA.portable.IDLEntity)
exception Klasse, Ausnahme (org.omg.CORBA.UserException)
sequence   Array vom entsprechenden Inhaltstyp

Tabelle 14: Zuordnung der komplexen IDL-Angaben

Die Java-Klassen für eine CORBA-basierte Anwendung werden je nach Typ von den entspre-
chenden CORBA-Klassen abgeleitet bzw. müssen bestimmte CORBA-spezifische Schnittstel-
len implementieren.


5.4.2.3. Java-Artefakte generieren

Dieses Java-to-IDL-Mapping muß natürlich nicht manuell ausgeführt werden. Dafür gibt es
den IDL-Compiler (idlj.exe), der beim J2SE-SDK enthalten ist und aus einer IDL-Datei
alle korrespondierenden Java-Artefakte erzeugt.

Für den IDL-Compiler werden einige Kommandozeilenargumente angegeben, die den Vor-
gang steuern.

   •   -pkgPrefix      zusätzliches Präfix für eine module Angabe, zur Einordnung der gene-
                       rierten Klassen in die gewünschte Java-Package-Hierarchie
   •   -td             Target Directory: das Zielverzeichnis für die erzeugten Klassen
   •   -fallTie        gibt an, daß alle möglichen Artefakte erzeugt werden sollen, auch die
                       für die TIE-Anbindung


                                            - 155 -
                                        Transport - CORBA

<?xml version="1.0" standalone="yes"?>
<project basedir=".." default="idl-generator">
  <property file="${basedir}/ant/build.properties" />
  <target name="idl-generator">
    <exec executable="${jdk.home}/bin/idlj.exe">
      <arg line="-pkgPrefix corba de.fhr.vs_iw" />
      <arg line="-td ${basedir}/generated" />
      <arg line="-fallTie" />
      <arg value="${basedir}/resources/corba/Accounting.idl" />
    </exec>
  </target>
</project>

Resource 14: Ant-Build-Datei corba-idlj.xml

Der IDL-Compiler kann aus Eclipse heraus durch diese Ant-Build-Datei aufgerufen werden,
sofern das bin-Verzeichnis des installierten JDK im aktuellen PATH aufgenommen wurde.


5.4.2.4. Erzeugte Klassen und Schnittstellen

Durch den IDL-Compiler wird für die Buchhaltung eine größere Anzahl an Java-Klassen und
Schnittstellen erzeugt.

Klasse                                        Java-Typ        Beschreibung
_CorbaAccountingEntryStub                     Klasse          Lokaler Stellvertreter
_CorbaAccountingServiceStub                   Klasse          Lokaler Stellvertreter
_CorbaAccountStub                             Klasse          Lokaler Stellvertreter
CorbaAccount                                  Schnittstelle   Schnittstelle mit Mehrfachvererbung
CorbaAccountHelper                            Klasse          Werkzeug-Klasse
CorbaAccountHolder                            Klasse          Behälter für Objekte des Typs
CorbaAccountingEntry                          Schnittstelle   Schnittstelle mit Mehrfachvererbung
CorbaAccountingEntryHelper                    Klasse          Werkzeug-Klasse
CorbaAccountingEntryHolder                    Klasse          Behälter für Objekte des Typs
CorbaAccountingEntryOperations                Schnittstelle   Schnittstelle mit Geschäftsmethoden
CorbaAccountingEntryPOA                       Klasse          Superklasse für normale Anbindung
CorbaAccountingEntryPOATie                    Klasse          Superklasse für TIE-Anbindung
CorbaAccountingException                      Klasse          Klasse für definierte Ausnahme
CorbaAccountingExceptionHelper                Klasse          Werkzeug-Klasse
CorbaAccountingExceptionHolder                Klasse          Behälter für Objekte des Typs
CorbaAccountingService                        Schnittstelle   Schnittstelle mit Mehrfachvererbung
CorbaAccountingServiceHelper                  Klasse          Werkzeug-Klasse
CorbaAccountingServiceHolder                  Klasse          Behälter für Objekte
CorbaAccountingServiceOperations              Schnittstelle   Schnittstelle mit Geschäftsmethoden
CorbaAccountingServicePOA                     Klasse          Superklasse für normale Anbindung
CorbaAccountingServicePOATie                  Klasse          Superklasse für TIE-Anbindung
CorbaAccountOperations                        Schnittstelle   Schnittstelle mit Geschäftsmethoden
CorbaAccountPOA                               Klasse          Superklasse für normale Anbindung
CorbaAccountPOATie                            Klasse          Superklasse für TIE-Anbindung
CorbaAccountType                              Klasse          Enumeration
CorbaAccountTypeHelper                        Klasse          Werkzeug-Klasse
CorbaAccountTypeHolder                        Klasse          Behälter für Objekte des Typs
CorbaIntegerSetHelper                         Klasse          Werkzeug-Klasse
                                               - 156 -
                                       Transport - CORBA

CorbaIntegerSetHolder                      Klasse      Behälter für Objekte des Typs
CorbaIOException                           Klasse      Klasse für definierte Ausnahme
CorbaIOExceptionHelper                     Klasse      Werkzeug-Klasse
CorbaIOExceptionHolder                     Klasse      Behälter für Objekte des Typs

Tabelle 15: Erzeugte CORBA-Artefakte

Für jedes in der IDL definierte Artefakt werden mehrere Java-Dateien erzeugt, die jeweils
verschiedene Funktionalitäten übernehmen und deren Name mit einem entsprechenden Suffix
versehen wird.

Suffix     Beschreibung
Stub
           lokale Stellvertreterklasse für entfernte Objekte, die den notwendigen Code für
           CORBA-Kommunikation der Anwendung implementiert
Helper
           Hilfsklasse mit den notwendigen Methoden, um aus einer Referenz auf ein
           entferntes Objekt ein lokales Stellvertreterobjekt zu erstellen
           Behälter-Klasse für ein Objekt des entsprechenden Typs, wird benötigt, da Java
Holder     keine out-Parameter bei Methoden unterstützt, CORBA und andere Program-
           miersprachen aber schon
Operations
           Schnittstelle, die nur die definierten Geschäftsmethoden aus der IDL-Datei
           enthält
           Portable Object Adapter: Basisklasse zur Ableitung einer eigenen Klasse, die
POA        die Funktionalität von entfernten Objekten im Sinne der Geschäftslogik im-
           plementiert, und auf einem Server als Servant instanziert wird
           Adapter für die TIE-Anbindung einer Klasse, bei der die eigene Klasse nicht
POATie     davon abgeleitet wird, sondern nur die Schnittstelle mit den Methoden imple-
           mentiert, damit die Vererbungsstruktur der eigenen Anwendung erhalten bleibt.
           Schnittstellen mit Mehrfachvererbung fassen die Operations-Schnittstelle mit
           den CORBA-Kommunikationsschnittstellen (Object, IDLEntity) zusammen,
           und werden dann von einer Anwendung für die konkreten Aufrufe verwendet

Resource 15: Bedeutung der CORBA-Suffixe

Durch diese Kombination aus anwendungsspezifisch erzeugten Dateien und den im JDK mit-
gelieferten CORBA-Klassen wird die Kommunikationsschicht der Anwendung realisiert. Was
fehlt ist nun die Implementierung der Geschäftslogik auf Client- und Server-Seite.


5.4.3. Verwendung der erzeugten Klassen auf der Serverseite

Auf der Serverseite werden die generierten POA- bzw. POATie-Klassen verwendet, um die
Geschäftslogik der Buchhaltung zu Implementieren, bzw. um wieder auf die Klassen eines
Moduls aus der Datenhaltung zuzugreifen, welches die Geschäftslogik bereits implementiert.


5.4.3.1. CORBA-Server

Die Klasse CorbaServer enthält zum einen die main()-Methode zum Start des Server und ist
zum anderen von CorbaAccountingServicePOA abgleitet, damit eine Instanz der Klasse als
Servant für den entfernten Aufruf der Methoden der Service-Schnittstelle dienen kann.



                                            - 157 -
                                     Transport - CORBA

package de.fhr.vs_iw.corba;

public final class CorbaServer extends CorbaAccountingServicePOA {

    private   Map<Integer, CorbaAccountingEntry> accountingEntries
      = new   HashMap<Integer, CorbaAccountingEntry>();
    private   Map<Integer, CorbaAccount> accounts
      = new   HashMap<Integer, CorbaAccount>();
    private   final IAccountingService next;

    private CorbaServer(final IAccountingService next) {
      this.next = next;
    }

    public void createAccount(int number, CorbaAccountType type,
        String description);
    public int createAccountingEntry(int debitAccount, int creditAccount,
        double amount, String text);

    public CorbaAccount getAccountByNumber(int number)
        throws CorbaAccountingException, CorbaIOException {
      try {
        CorbaAccount account = accounts.get(number);
        if (account == null) {
          IAccount nextaccount = next.getAccountByNumber(number);
          Servant servant = new CorbaServerAccount(nextaccount);
          rootPoa.activate_object(servant);
          org.omg.CORBA.Object ref = rootPoa.servant_to_reference(servant);
          account = CorbaAccountHelper.narrow(ref);
          accounts.put(number, account);
        }
        return account;
      } catch (IOException e) {
        throw new CorbaIOException(e.getMessage());
      } catch (AccountingException e) {
        throw new CorbaAccountingException(e.getMessage());
      } catch (ServantAlreadyActive e) {
        throw new CorbaIOException(e.getMessage());
      } catch (WrongPolicy e) {
        throw new CorbaIOException(e.getMessage());
      } catch (ServantNotActive e) {
        throw new CorbaIOException(e.getMessage());
      }
    }

    public   int[] getAccountingEntries();
    public   double calculateBalance(int number);
    public   CorbaAccountingEntry getAccountingEntryById(int id);
    public   int[] getAccounts();
    public   void removeAccount(int number);
    public   void removeAccountingEntry(int id);

    ...
}

Code 112: Class CorbaServer (Methoden)

Die Klasse fungiert als Adapter von CORBA zu einer Service-Klasse der Buchhaltung. Die
Methodenaufrufe werden direkt umgesetzt, und in erster Linie müssen nur die Ausnahmen aus
dem Buchhaltungsmodell in ihre korrespondierenden CORBA-Ausnahmen umgeformt wer-
den. Lediglich bei der Rückgabe der Konten bzw. Buchungsobjekte (CorbaAccount bzw.

                                          - 158 -
                                     Transport - CORBA

CorbaAccountingEntry) ist mehr Aufwand erforderlich. Das Objekt muß als Servant in-
stanziert mit activate_object() zuerst aktiviert werden. Anschließend kann man sich durch
die Methode servant_to_reference() die Objekt-Referenz des gerade aktivierten Objekts
holen. Aus dieser Referenz wird durch die Helfer-Klasse mit narrow() ein Stub-Objekt er-
zeugt, das dann an den Client geschickt wird. Damit es nicht zu Fehlern kommt, weil jedes
Objekt nur einmal aktiviert werden darf, werden alle Servant-Objekte in HashMaps verwaltet.
In der main-Methode wird der CorbaServer dann einmal instanziert und stellt die Funktiona-
litäten als entferntes Objekt zur Verfügung.

package de.fhr.vs_iw.corba;

public final class CorbaServer extends CorbaAccountingServicePOA {

    ...

    private static final String CORBA_NAME = "CorbaAccountingService";
    private static POA rootPoa = null;

    public static void main(String[] args) {
      if (args.length < 1) {
        System.err.println(
            "Host, Port und Proxy-Klasse müssen angegeben werden");
        return;
      }
      try {
        // process command line args, first is host, second is port
        String[] params = { "-ORBInitialHost", args[0],
            "-ORBInitialPort", args[1] };
        IAccountingProxy proxy = Utilities.loadProxy(args[2]);
        IAccountingService next
          = proxy.connect(Utilities.shiftArgs(args, 2));

          // initialize ORB and object adapter
          ORB orb = ORB.init(params, null);
          rootPoa
            = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));
          rootPoa.the_POAManager().activate();

          // initantiate the server implementation
          Servant servant = new CorbaServer(next);
          org.omg.CORBA.Object ref = rootPoa.servant_to_reference(servant);

          // get root naming context
          org.omg.CORBA.Object namingref
            = orb.resolve_initial_references("NameService");
          NamingContextExt ncref = NamingContextExtHelper.narrow(namingref);

          // bind service object to name
          NameComponent[] path = ncref.to_name(CORBA_NAME);
          ncref.rebind(path, ref);

          // let corba do its job
          orb.run();
        } catch (final Exception e) {
          e.printStackTrace();
        }
    }
}

Code 113: Class CorbaServer (main)

                                          - 159 -
                                     Transport - CORBA

Doch vor der Instanzierung eines Servant-Objekts werden die CORBA-Klassen initialisiert.
Eine ORB-Instanz (Object Request Broker) übernimmt bei CORBA die Kommunikation mit
anderen Systemen, bzw. mit anderen ORBs auf diesen Systemen, und wird mit den Parame-
tern -ORBInitialHost und -ORInitialPort für den Zugriff auf den ORB-Daemon instan-
ziert, die in einem String-Array angegeben werden.
Zusätzlich ist noch eine Instanz des RootPOA notwendig der die Verwaltung und Aktivierung
aller über CORBA angebotenen Objekte übernimmt, und selbst wie ein entferntes Objekt ab-
gerufen wird. Der CorbaServer wird als Servant instanziert und über die Methode narrow()
erhält man eine Referenz darauf, die für entfernte Aufrufe verwendet werden kann.
Diese Referenz wird dann an den Namensdienst gebunden (rebind()), damit sie über den
angegebenen Namen (NameComponent[]) leicht abrufbar ist.


5.4.3.2. Konto-Klasse

Die Klasse CorbaServerAccount dient nur als Adapter für IAccount-Objekte der Datenhal-
tungsschicht, gibt die Methodenaufrufe weiter und formt auftretende Ausnahmen um.

package de.fhr.vs_iw.corba;

public final class CorbaServerAccount extends CorbaAccountPOA {

    private final IAccount next;

    protected CorbaServerAccount(IAccount next) {
      this.next = next;
    }

    public int[] getDebitEntries() throws CorbaIOException {
      try {
        Set<Integer> set = next.getDebitEntries();
        int[] ar = new int[set.size()];
        int i = 0;
        for (Integer id : set) {
          ar[i++] = id;
        }
        return ar;
      } catch (IOException e) {
        throw new CorbaIOException(e.getMessage());
      }
    }

    public void setType(CorbaAccountType type) throws CorbaIOException {
      try {
        next.setType(AccountType.getAccountTypeById(type.value()));
      } catch (IOException e) {
        throw new CorbaIOException(e.getMessage());
      }
    }

    ...
}

Code 114: Class CorbaServerAccount

Wie auch in der Service-Klasse müssen hier die Sets zu Arrays umgewandelt werden.


                                          - 160 -
                                    Transport - CORBA


5.4.3.3. Buchungs-Klasse

Auch die Buchungs-Klasse dient nur als Adapter für die Datenhaltungsschicht und jede In-
stanz kapselt ein IAccountingEntry-Objekt.

package de.fhr.vs_iw.corba;

public final class CorbaServerEntry extends CorbaAccountingEntryPOA {

    private final IAccountingEntry next;

    protected CorbaServerEntry(IAccountingEntry next) {
      this.next = next;
    }

    public double getAmount() throws CorbaIOException {
      try {
        return next.getAmount();
      } catch (IOException e) {
        throw new CorbaIOException(e.getMessage());
      }
    }


    public void setCreditAccount(int creditAccount)
        throws CorbaAccountingException, CorbaIOException {
      try {
        next.setCreditAccount(creditAccount);
      } catch (IOException e) {
        throw new CorbaIOException(e.getMessage());
      } catch (AccountingException e) {
        throw new CorbaAccountingException(e.getMessage());
      }
    }

    ...
}

Code 115: Class CorbaServerEntry

Die Methodenaufrufe werden direkt weitergegeben und die Ausnahmen entsprechend zu den
in der IDL definierten CORBA-Ausnahmen adaptiert.


5.4.3.4. Der ORB-Daemon

Die Kommunikation und alle entfernten Aufrufe werden zwar von ORB-Instanzen erledigt,
damit sich diese aber grundsätzlich untereinander auffinden können ist eine zentrale Anlauf-
stelle notwendig, der ORB-Daemon. Im Java Development Kit ist ein ORB-Daemon vorhan-
den der direkt verwendet werden kann.

     bin/orbd.exe -port 9050

Beim Start sollte der Port angegeben werden, auf dem der ORB-Daemon Anfragen entgegen-
nehmen soll, sonst wird der Standardport 3895 verwendet.



                                          - 161 -
                                    Transport - CORBA



5.4.4. Verwendung der erzeugten Klassen auf der Clientseite

Auf der Clientseite wird im Prinzip dieselbe Vorgehensweise verfolgt, wie auf der Serversei-
te. Die vom IDL-Compiler generierten CORBA-Client-Schnittstellen (siehe 5.4.2.4,
CorbaAccountingService, CorbaAccount, CorbaAccountingEntry) werden mit den zuge-
hörigen Stub-Klassen für die CORBA-Kommunikation verwendet und durch Adapter-Klassen
wieder an die Schnittstellen des Buchhaltungsmodells angebunden.


5.4.4.1. Service-Klasse

Die Service-Klasse implementiert die Service-Schnittstelle aus dem Buchhaltungsmodell
(IAccountingService) und kapselt ein CorbaAccountingService-Objekt.

package de.fhr.vs_iw.corba;

public final class AccountingService implements IAccountingService {
  private final CorbaAccountingService corba;

  protected AccountingService(CorbaAccountingService corba) {
    this.corba = corba;
  }

  private   Map<Integer, AccountingEntry> accountingEntries
    = new   HashMap<Integer, AccountingEntry>();
  private   Map<Integer, Account> accounts
    = new   HashMap<Integer, Account>();

  public Double calculateBalance(Integer number)
      throws IOException, AccountingException {
    try {
      return corba.calculateBalance(number);
    } catch (CorbaAccountingException e) {
      throw new AccountingException(e.message, e);
    } catch (CorbaIOException e) {
      IOException ioe = new IOException(e.message);
      ioe.initCause(e);
      throw ioe;
    }
  }

  public IAccount getAccountByNumber(Integer number)
      throws IOException, AccountingException {
    try {
      Account account = accounts.get(number);
      if (account == null) {
        account = new Account(corba.getAccountByNumber(number));
        accounts.put(number, account);
      }
      return account;
    } catch (CorbaAccountingException e) {
      throw new AccountingException(e.message, e);
    } catch (CorbaIOException e) {
      IOException ioe = new IOException(e.message);
      ioe.initCause(e);
      throw ioe;
    }
  }


                                          - 162 -
                                     Transport - CORBA

    public Set<Integer> getAccounts() throws IOException {
      try {
        int[] ar = corba.getAccounts();
        HashSet<Integer> set = new HashSet<Integer>();
        for (int i : ar) {
          set.add(i);
        }
        return set;
      } catch (CorbaIOException e) {
        IOException ioe = new IOException(e.message);
        ioe.initCause(e);
        throw ioe;
      }
    }

    ...
}

Code 116: Class AccountingService (CORBA)

In den Methoden getAccountByNumber() bzw. getAccountingEntryById() werden ent-
sprechende Adapter-Objekte für Konten bzw. Buchungen zurückgegeben, die über CORBA
als entfernte Objekte aufgerufen werden. Diese Adapter-Objekte werden wie auf dem Server
in HashMaps verwaltet.


5.4.4.2. Konto-Klasse

Die Konto-Klasse implementiert entsprechend die IAccount-Schnittstelle und gibt alle Me-
thodenaufrufe an ein CorbaAccount-Objekt weiter. Die CORBA-Ausnahmen werden ent-
sprechend zu Ausnahmen aus dem Buchhaltungsmodell zurückgewandelt, die Elemente der
übertragenen Arrays wieder in Sets aufgenommen und auch der Kontotyp entsprechend dem
generierten IDL-Kontotyp (CorbaAccountType) übertragen.

package de.fhr.vs_iw.corba;

public final class Account implements IAccount {

    private final CorbaAccount corba;

    protected Account(CorbaAccount corba) {
      this.corba = corba;
    }

    public Set<Integer> getCreditEntries() throws IOException {
      try {
        int[] ar = corba.getCreditEntries();
        HashSet<Integer> set = new HashSet<Integer>();
        for (int i : ar) {
          set.add(i);
        }
        return set;
      } catch (CorbaIOException e) {
        IOException ioe = new IOException(e.message);
        ioe.initCause(e);
        throw ioe;
      }
    }


                                            - 163 -
                                    Transport - CORBA

    public void setType(AccountType type) throws IOException {
      try {
        corba.setType(CorbaAccountType.from_int(type.getId()));
      } catch (CorbaIOException e) {
        IOException ioe = new IOException(e.message);
        ioe.initCause(e);
        throw ioe;
      }
    }

    ...
}

Code 117: Class Account (CORBA)


5.4.4.3. Buchungs-Klasse

Wie auch die Konto-Klasse dient auch die Buchungs-Klasse als Adapter auf die entsprechen-
de Buchhaltungsschnittstelle (IAccountingEntry), und zwar für ein entferntes
CorbaAccountingEntry-Objekt.

package de.fhr.vs_iw.corba;

public final class AccountingEntry implements IAccountingEntry {

    private final CorbaAccountingEntry corba;

    protected AccountingEntry(CorbaAccountingEntry corba) {
      this.corba = corba;
    }

    public Double getAmount() throws IOException {
      try {
        return corba.getAmount();
      } catch (CorbaIOException e) {
        IOException ioe = new IOException(e.message);
        ioe.initCause(e);
        throw ioe;
      }
    }

    public void setDebitAccount(Integer debitAccount)
        throws IOException, AccountingException {
      try {
        corba.setDebitAccount(debitAccount);
      } catch (CorbaAccountingException e) {
        throw new AccountingException(e.message, e);
      } catch (CorbaIOException e) {
        IOException ioe = new IOException(e.message);
        ioe.initCause(e);
        throw ioe;
      }
    }

    ...
}

Code 118: Class AccountingEntry (CORBA)



                                          - 164 -
                                   Transport - CORBA


5.4.4.4. Proxy-Klasse

Die Proxy-Klasse übernimmt den Aufruf der entfernten CORBA-Service-Servant. Als Para-
meter wird der Host und der Port des ORB-Daemon benötigt, um die Kommunikation zu in-
itialisieren.

package de.fhr.vs_iw.corba;

public final class CorbaProxy implements IAccountingProxy {

    public CorbaProxy() {}

    public void close() {}

    public IAccountingService connect(String[] args) throws IOException {
      if (args.length < 2) {
        throw new IllegalArgumentException(
            "Hostname und Port müssen angegeben werden");
      }
      try {
        // process command line args, first is host, second is port
        String[] params = { "-ORBInitialHost", args[0],
            "-ORBInitialPort", args[1] };

         // initialize the ORB
         ORB orb = ORB.init(params, null);

         // get root naming context
         org.omg.CORBA.Object namingref
           = orb.resolve_initial_references("NameService");
         NamingContextExt ncref = NamingContextExtHelper.narrow(namingref);

         // get the corba bank object by its name
         NameComponent[] path = ncref.to_name(CORBA_NAME);
         org.omg.CORBA.Object ref = ncref.resolve(path);
         CorbaAccountingService service
           = CorbaAccountingServiceHelper.narrow(ref);

          return new AccountingService(service);
        } catch (Exception e) {
          IOException ioe = new IOException(e.getMessage());
          ioe.initCause(e);
          throw ioe;
        }
    }

    private static final String CORBA_NAME = "CorbaAccountingService";
}

Code 119: Class CorbaProxy

Zuerst wird wieder ein ORB instanziert, der eine Verbindung zum ORB-Daemon und dann
auch zum ORB auf dem CORBA-Server herstellt. Damit wird eine Referenz zum Namens-
dienst geholt, wodurch wiederum die Referenz auf den im Server instanzierten Servant durch
den registrierten Namen abgerufen kann. Durch den Aufruf von narrow() der Helfer-Klasse
erhält man ein Stub-Objekt für die Referenz auf das entfernte Objekt, mit dem man die ent-
fernten Methoden aufrufen kann. Das CorbaAccountingService-Objekt wird dann von ei-
nem AccountingService-Objekt gekapselt und von der connect()-Methode zurückgegeben.

                                         - 165 -
                                   Transport - CORBA



5.4.5. JACORB

Der JacORB [78] ist eine freie Java-
Implementierung des CORBA-Standards als
Alternative zur CORBA-Implementierung von SUN. Er arbeitet erwiesenermaßen gut mit den
CORBA-Implementierungen für andere Programmiersprachen (z.B. C++) zusammen, was die
Interoperabilität, die für CORBA eigentlich grundsätzlich gefordert wird, garantiert.


5.4.5.1. Features

Die Implementierung von JacORB umfaßt im wesentlichen folgende Elemente:

   •   multithreaded ORB mit hoher Performance
   •   100% Java, kompatibel mit JDK 1.1 bis JDK 1.5
   •   IDL-Compiler für IDL-Mapping 2.3
   •   natives IIOP, GIOP 1.2 und bidirektionales GIOP
   •   POA (Portable Object Adapter) mit POA-Monitor-GUI
   •   AMI (Asynchronous Method Invocation)
   •   Unterstützung für Quality of Service (QoS)
   •   ETF (Extensible Transport Framework)
   •   Dynamic Invocation Interface (DII)
   •   Dynamic Skeleton Interface (DSI)
   •   Portable Interceptors
   •   OMG Interoperable Naming Service mit GUI
   •   verbessertes IIOP über SSL mit KeyStoreManager
   •   OMG Notification-Service und Event-Service
   •   Transaktions-Service
   •   Trading-Service
   •   CORBA 2.3 Code-Unterstützung
   •   OMG Data Distribution Service (DDS)
   •   Interface Repository mit GUI
   •   Implementaion Repository mit GUI


5.4.5.2. Installation

Zur Verwendung des JacORB muß das entsprechende Paket für Java heruntergeladen und
entpackt werden.

   http://www.jacorb.org/download.html

Dem Paket liegen Installationsanweisungen bei, um die Quellen mit Ant vollständig neu zu
kompilieren, und dabei die Pfade passend zum Installationsort einzurichten. Dies ist jedoch
nicht unbedingt nötig, da man die Pfade zu den nötigen Bibliotheken auch manuell bei Start
einer JVM angeben kann. Die Bibliotheken müssen hier auch nicht zum CLASSPATH von Ec-
lipse hinzugefügt werden.



                                          - 166 -
                                  Transport - CORBA


5.4.5.3. Verwendung

Die JacORB-Klassen werden nicht direkt zur Programmierung verwendet. Das oben angege-
bene CORBA-Programm der Buchhaltung wird exakt so verwendet und durch Kommando-
zeilenparameter wird lediglich die Verwendung der ORB-Klasse von JacORB eingestellt.
Wegen der Angabe der Pfade und Parameter ist es günstig diese einmalig für eine JacORB-
Installation in eine Batch-Datei (jacorbsettings.cmd) zu schreiben und diese beim Start
von JacORB-basierten Programmen zu verwenden.

@echo off
set LIB=D:\classpath\JacORB_2.3.0_beta2\lib
set RT=c:\Program Files\Java\jdk1.5.0_06\jre\lib\rt.jar
set BOOTCLASSPATH=%LIB%\jacorb.jar;%LIB%\logkit-1.2.jar;
    %LIB%\avalon-framework-4.1.5.jar;%RT%;%CLASSPATH%
set JACOPTIONS=-Djacorb.home=D:\classpath\JacORB_2.3.0_beta2
    -Dorg.omg.CORBA.ORBClass=org.jacorb.orb.ORB
    -Dorg.omg.CORBA.ORBSingletonClass=org.jacorb.orb.ORBSingleton

Resource 16: jacorbsettings.cmd

Hier werden die Bibliotheken von JacORB und die darin enthaltenen Klassen zur Verwen-
dung bei CORBA-Anwendungen Ausgehend von diesen Einstellungen wird dann jeweils eine
JVM für die entsprechenden Java-Programme gestartet.

Im Gegensatz zur mitgelieferten CORBA-Implementierung muß bei JacORB nicht der orbd,
sondern der NameServer separat gestartet werden, und zwar durch folgende Batch-Datei.

@echo off
call jacorbsettings.cmd
java -Xbootclasspath/p:"%BOOTCLASSPATH%" %JACOPTIONS%
     org.jacorb.naming.NameServer -Djacorb.naming.ior_filename=d:/NS_Ref

Resource 17: jacorbnaming.cmd

Die zuvor definierten Einstellungen werden beim Start einer JVM angegeben und damit die
Klasse des NameServers aufgerufen. Als zusätzlicher Parameter wird eine Datei angegeben,
in der der NameServer seine IOR zur späteren Verwendung hinterlegt.

Die Klasse CorbaServer wird auf dieselbe Weise mit den JacORB-Einstellungen aus einer
Batch-Datei (jacorbserver.cmd) heraus gestartet.

@echo off
call jacorbsettings.cmd
java -Xbootclasspath/p:"%BOOTCLASSPATH%" %JACOPTIONS%
     de.fhr.vs_iw.corba.CorbaServer localhost 9050
     de.fhr.vs_iw.local.LocalProxy

Resource 18: jacorbserver.cmd

Die von der Implementierung der CorbaServer-Klasse her muß zwar die geforderte Angabe
von Host und Port erfolgen, diese wird aber von JacORB nicht verwendet. Der JacORB lädt
über seine Konfiguration die IOR des NameServer für alle weiteren Operationen.



                                        - 167 -
                                    Transport - CORBA

Für den Client gilt natürlich selbiges, und es muß nicht die Implementierung angepaßt, son-
dern nur die Startparameter verwendet werden.

@echo off
call jacorbsettings.cmd
java -Xbootclasspath/p:"%BOOTCLASSPATH%" %JACOPTIONS%
     de.fhr.vs_iw.Client de.fhr.vs_iw.corba.CorbaProxy localhost 9050

Resource 19: jacorbclient.cmd

Auch hier ist die Angabe von Host und Port nur wegen der originalen CORBA-
Implementierung notwendig, da sonst eine Ausnahme wegen fehlender Parameter geworfen
wird.


5.4.5.4. Einstellungen

Jede JacORB-Instanz lädt die globale Konfigurationsdatei orb.properties aus dem
CLASSPATH der Anwendung. Darin werden alle relevanten Einstellungen vorgenommen. Eine
Beispieldatei, in der alle möglichen Einstellungen erklärt werden, ist im JacORB-Paket ent-
halten. Die wichtigsten Einstellungen die darin aber angepaßt werden sollten sind die folgen-
den:

    jacorb.config.dir=D:/eclipse/workspace/VS_IW_Complete/resources/jacorb
    ORBInitRef.NameService=file:/d:/NS_Ref

Das ist zum einen ein Verzeichnis, in dem weitere spezifische Konfigurationsdateien für ein-
zelne ORB-Instanzen abgelegt werden können. Zum anderen ist es die Angabe der Datei mit
der IOR des NameServers, womit eine ORB-Instanz erst in der Lage ist, ihren Dienst zu ver-
richten. Hier kann eine beliebige URL zu der Datei angegeben werden, falls der NameServer
auf einem anderen Host gestartet wurde und die Datei, z.B. über HTTP, bereitgestellt wird.


5.4.6. Fazit

Wenn in der Praxis ein verteiltes System realisiert werden soll, muß man sich auf jeden Fall
überlegen ob die Verwendung von CORBA den Vorgang nicht erheblich vereinfachen würde.
In diesem Beispiel hier ist die Angelegenheit allerdings relativ umständlich, weil auf beiden
Seiten der CORBA-Datenübertragung die CORBA-Klassen durch Adapter an die vorgegebe-
nen Buchhaltungsschnittstellen angepaßt werden müssen.
Wenn aber eine Anwendung keine eigenen Kommunikationsschnittstellen vorgibt und direkt
auf die generierten CORBA-Schnittstellen aufsetzen kann, ist die Verwendung von CORBA
auf jeden Fall ein guter Ansatz.




                                           - 168 -
                                 Java-Enterprise - Übersicht


6. Java-Enterprise




6.1. Übersicht
In der Spezifikation der J2EE (Java 2 Enterprise Edition) [45] werden Softwarekomponenten
und Dienste definiert, die primär in der Programmiersprache Java erstellt werden. Die Spezi-
fikation dient dazu, einen allgemein akzeptierten Rahmen zur Verfügung zu stellen, um auf
dessen Basis aus modularen Komponenten verteilte, mehrschichtige Anwendungen entwik-
keln zu können. Klar definierte Schnittstellen zwischen den Komponenten und Schichten sol-
len dafür sorgen, daß Softwarekomponenten unterschiedlicher Hersteller interoperabel sind,
wenn sie sich an die Spezifikation halten, und daß die verteilte Anwendung gut skalierbar ist
[08].




Abbildung 70: Java 2 Enterprise Komponenten-Model




                                            - 169 -
                               Java-Enterprise - Übersicht

Anwendungen, die sich auf dieses Model stützen und die angegebenen Komponenten und
Dienste verwenden, sind theoretisch auf jedem zertifizierten J2EE-Applikations-Server ab-
lauffähig.


6.1.1. Komponenten

Im Groben gliedert sich der Aufbau einer Anwendung und damit des Applikations-Servers in
verschiedenen logische Komponenten, die als Container bezeichnet werden und prinzipiell in
jedem Applikations-Server vorhanden sein müssen [08].


6.1.1.1. EJB-Container

Der Enterprise-JavaBeans-Container stellt die Laufzeitumgebung für Enterprise JavaBeans
(siehe 6.3) bereit, die die Geschäftslogik einer Anwendung implementieren und den Zugriff
auf die Daten der Anwendung gestatten. Er arbeitet als Vermittlungsschicht (Middleware)
zwischen den Clients einer Anwendung und den Daten in einer Datenbank [46].


6.1.1.2. Application-Client-Container

Als Application-Client-Container (AAC) wird ein Satz notwendiger Bibliotheken bezeichnet,
die eine Java-Anwendung benötigt, um aus einer separat laufenden Virtuellen Maschine her-
aus auf die EJBs eines Applikations-Servers, der sich auch auf einem anderen Host befinden
kann, zuzugreifen [47].


6.1.1.3. Web-Container

Der Web-Container stellt eine Laufzeitumgebung und Frameworks für web-basierte Client-
Anwendungen, die durch Servlets und Java-Server-Pages (JSPs) realisiert werden (siehe 7)
zur Verfügung.


6.1.1.4. Applet-Container

Als Teil einer web-basierten Client-Anwendung können auch Applets zum Einsatz kommen.
Von dem her ist der Applet-Container aber nur eine Ergänzung zum Web-Container, der sich
im Zusammenspiel mit einem Applikations-Server nicht anders verhält als bei eigenständigen
Applets.


6.1.1.5. Datenbank

Als weitere Infrastrukturkomponente kommt zur persistenten Speicherung der Daten ein Da-
tenbankmanagementsystem (DBMS) zum Einsatz, das nicht zwingend Teil des eigentlichen
Applikations-Servers, sondern in der Regel unabhängig ist [08]. Der Applikations-Server
stellt lediglich einen einheitlichen Zugriffsweg (JDBC, Persistenz-Manager) dafür bereit.




                                         - 170 -
                                Java-Enterprise - Übersicht



6.1.2. Dienste

Die Komponenten eines Applikations-Servers müssen verschiedene spezifizierte Dienste und
technische Funktionalitäten zur Verfügung stellen, die von Anwendungen genutzt werden
können. Außerdem kapselt der Server dadurch den Zugriff auf die Ressourcen des zugrunde-
liegenden Betriebssystems [08].


6.1.2.1. JMS

Der Java Messaging Service (JMS) stellt eine API für den asynchronen Austausch von Nach-
richten zwischen den Komponenten zur Verfügung [15]. Als Empfänger der Nachrichten fun-
gieren z.B. Message Driven Beans (siehe 6.3.4).


6.1.2.2. JMX

Die Java Management Extension (JMX) [16] ist eine Spezifikation zur Überwachung und
Steuerung von Java-Anwendungen. Auch die Kommunikation zwischen Anwendungen inner-
halb einer VM wird dadurch vereinfacht. Durch ein Model von Adaptern und Connectoren
läßt sich sehr leicht eine externe Steuerung der Objekte der Anwendung realisieren, z.B. mit
einem HTTP-Adapter über einen Web-Browser oder mit einem SNMP-Adapter durch andere
Verwaltungs-Tools [48].


6.1.2.3. JAAS

Der Java Authentication und Authorization Service (JAAS) [07] ist eine API, um Dienste zur
Authentifizierung und Zugriffsberechtigung in Java-Programmen bereitzustellen. JAAS orien-
tiert sich dabei an den Pluggable Authentication Modules (PAM) und unterstützt eine benut-
zerbasierte Autorisierung [50].


6.1.2.4. JTA

Die Java Transaction API (JTA) [51] ist eine Art Protokoll, das der Anwendung die Steuerung
der Transaktionsverwaltung erlaubt, die wiederum vom Java Transaction Service (JTS) [52]
nach der OMG Object Transaction Spezifikation implementiert wird [08].


6.1.2.5. JavaMail

Die JavaMail-API [53] erlaubt den Zugriff auf verbreitete Mail-Dienste, wie z.B. SMTP,
POP3, IMAP oder NNTP [08].


6.1.2.6. JAF

Das Java Beans Activation Framework (JAF) [49] bietet die Möglichkeit, verschiedene Daten
anhand des MIME-Headers zu erkennen und dementsprechend bestimmte Funktionalitäten
oder Enterprise Java Beans zu aktivieren.

                                          - 171 -
                                 Java-Enterprise - Übersicht


6.1.2.7. JCA

Die Java Connector Architecture (JCA) [56] ist eine Schnittstellen-Spezifikation zur Integra-
tion von heterogenen Anwendungen in die J2EE-Plattform, um den Austausch von Daten und
die Verbindung von Geschäftsprozessen zu vereinfachen [57].


6.1.2.8. JAXR

Die Java-API for XML-Registries (JAXR) [54] stellt einen transparenten Zugriff auf so ge-
nannte Business-Registries, wie z.B. ebXML oder UDDI, zur Verfügung [08].


6.1.2.9. JAX-RPC

Die Java-API for XML-Based Remote Procedure Calls (JAX-RPC) [55] dient zum Zugriff
auf entfernte RPC-Dienste, die als Web-Service angeboten werden, über das SOAP-Protokoll.


6.1.3. Verfügbare Implementierungen

Die angegeben Dienste stellen in erster Linie Spezifikation und Definition dar, wobei es von
SUN jeweils eine Referenz-Implementierung gibt. Verbreitet sind aber etliche kommerzielle
und freie Implementierungen der Standards. Applikations-Server und Web-Container sind
meist separate Produkte die aber oft in Kombination vertrieben werden [08].


6.1.3.1. Open Source Server

   •   Apache Geronimo (benutzt Jetty)
   •   JBoss              (benutzt Apache Tomcat)
   •   JOnAS              (benutzt Apache Tomcat)
   •   GlassFish          (benutzt Apache Tomcat)
   •   Enhydra Enterprise (benutzt Apache Tomcat)
   •   Sun Java System Application Server (Referenz-Implementierung)

6.1.3.2. Proprietäre Server

   •   ATG Dynamo Application Server (DAS)
   •   BEA WebLogic
   •   SAP Web Application Server
   •   Oracle Application Server
   •   IBM WebSphere

6.1.3.3. Web-Container

   •   Apache Tomcat                Open Source
   •   Caucho Technology Resin      proprietär
   •   Enhydra Server               Open Source
   •   Jetty                        Open Source

                                           - 172 -
                      Java-Enterprise - Der JBoss-Application-Server


6.2. Der JBoss-Application-Server
6.2.1. Allgemeines

Für dieses Tutorial wird der JBoss-Application-Server [02] [03] verwendet, eine zertifizierte
Anwendungs-Plattform nach dem J2EE (Java 2 Enterprise Edition) Standard. Vor allem die
vereinfachte Entwicklung von Anwendungen mit Enterprise Java Beans (EJB) 3.0, bei der
Annotations statt Deployment-Descriptoren benutzt werden, wird unterstützt. Der Applikati-
ons-Server bietet verschiedene frei konfigurierbare Dienste an, die jeweils in einem eigenen
"Service Archive" verpackt und auf dem Server "deployed" werden [04]. Er enthält als Serv-
let-Container für Web-Anwendungen den "Tomcat" [06] von der Apache-Foundation [05].


6.2.2. Installation

Vorraussetzung für die Verwendung von JBoss ist ein installierter Java-Development-Kit
(JDK) [01], das Java-Runtime-Environment (JRE) alleine reicht nicht aus. Eine Umgebungs-
variable JAVA_HOME muß auf den Pfad des JDK gesetzt werden, damit der JBoss die Pro-
gramme und Tools des JDK finden und verwenden kann.




Abbildung 71: Windows Umgebungsvariablen

Alternativ kann auch die Batch-Datei (siehe 6.2.4.1), die zum Starten des Servers verwendet
wird, abgeändert werden und der Pfad zum JDK dort direkt als Variable eingetragen werden.




                                           - 173 -
                         Java-Enterprise - Der JBoss-Application-Server


6.2.2.1. Aus dem beiliegenden Paket

Der JBoss-Application-Server kann direkt aus dem an der FH vorbereiteten Archiv
    http://java.fh-regensburg.de/download/jboss-4.0.5.GA.zip

entpackt, gestartet (siehe 6.2.4.1) und verwendet werden. Das Paket enthält den JBoss in der
Version 4.0.5.GA mit der Unterstützung von EJB 3.0, sowie den vorkonfigurierten Daten-
quellen MySqlDS (siehe 6.2.3.2), OracleDS (siehe 6.2.3.4), PostgresDS (siehe 6.2.3.3), und
den dafür nötigen JDBC-Treibern.


6.2.2.2. Originalinstallation von jboss.org

Der JBoss kann auch direkt von der Download-Seite des Herstellers

    http://labs.jboss.com/portal/jbossas/download

heruntergeladen werden. Wie dort angegeben muß zur Unterstützung von EJB 3.0 der Instal-
ler verwendet werden, das Herunterladen und Entpacken eines Archivs ist dafür nicht ausrei-
chend. Die Installation über Java-Web-Start direkt gestartet werden. Alternativ kann der In-
staller als Paket von sourceforge.net heruntergeladen und gestartet werden:

    http://prdownloads.sourceforge.net/jboss/jems-installer-1.2.0.BETA3.jar

    java -jar jems-installer-1.2.0.BETA3.jar




Abbildung 72: Paket-Auswahl bei der JBoss-Installation

                                               - 174 -
                        Java-Enterprise - Der JBoss-Application-Server

Der dialogbasierte Installer (erstellt mit IzPack [09]) führt durch die Installation von JBoss.
Wichtig dabei ist die korrekte Auswahl des Typs der Installation. Nur bei der Auswahl von

   ejb3      oder      ejb3-clustered

ist später die Unterstützung für EJB 3.0 verfügbar. Da bei allen anderen Installations-Typen
die nötigen Bibliotheken für EJB 3.0 nicht installiert werden, sind diese eher uninteressant.


6.2.2.3. Verzeichnisstruktur

                              bin Startskripte (siehe 6.2.4.1)
                              client JAR-Dateien für externe Client-Anwendungen
                              docs Beiliegende Dokumente
                                dtd Document Type Definitions (DTDs) zum Validieren der
                                      XML-Konfigurationsdateien
                                examples Vorlagen für verschiedene Dienste
                                  binding-manager Konfiguration der Port-Nummern
                                  jca Vorlagen für verschiedene Datenquellen / Datenbanken
                                  jms Vorlagen für die Persistenz des Java-Messaging
                                  jmx EJB-Management-Beans
                                  netboot Das Netboot-Archiv, mit dem ein JBoss ohne In-
                                            stallation gestartet werden kann
                                  varia Diverse Beispiele
                                licenses Lizenzdokumente der verwendeten Komponenten
                                schema XML-Schemas der Deployment-Descriptoren
                                tests Testergebnisse von JBoss.org
                              lib Bibliotheken für den Start des Servers
                              scripts Skripte für zusätzliche Installationsvorgänge
                              server Hauptverzeichnis mit verschiedenen möglichen Server-
                                      konfigurationen
                                default Standardkonfiguration
                                  conf Konfigurationsdateien für den Server
                                  data Datenverzeichnis
                                    hypersonic Datendateien der Hypersonic-Datenbank
                                    wsdl Schemata der verfügbaren Web-Services
                                  deploy Die Applikationen und Dienste auf dem Server
                                  lib Bibliotheken die von Anwendungen benötigt werden
Abbildung 73:                     log Ablage der Log-Dateien des Servers
JBoss-Verzeichnisstruktur
                                  tmp Ablage von ausgelagerten Stateful-Session-Beans und
                                        des Inhalts ausgepackter Archive
                                  work Hier werden JSP-Seiten von Tomcat zu Servlets co-
                                          mpiliert

6.2.3. Datenbankanbindung

Zum sinnvollen Betrieb von JBoss ist eine Datenbankanbindung notwendig. Alle Anwendun-
gen, die auf dem Server laufen, können die konfigurierten Datenbankanbindungen zum Spei-
chern ihrer Daten verwenden. Auch interne Verwaltungsdaten der Servers, wie z.B. zur Be-
nutzerauthentifizierung mit JAAS (Java Authentication and Authorization Service) [07] wer-
den in einer Datenbank gespeichert.

                                            - 175 -
                        Java-Enterprise - Der JBoss-Application-Server

Die Anbindungen werden in Dateien mit der Endung ...-ds.xml definiert. Diese Dateien
werden im Deploy-Verzeichnis des Servers

   server/default/deploy/

abgelegt, wo der Server die Datei automatisch findet und verarbeitet. In einer JBoss-
Installation ist eine Reihe von Beispieldateien für die Konfiguration verschiedener Datenban-
ken im Verzeichnis

   docs/examples/jca/

enthalten. Dort müssen nur die passenden Parameter eingetragen und die Dateien im Deploy-
Verzeichnis abgelegt werden. Im folgende ist jede Datenquelle in einer eigenen Datei defi-
niert, die Angaben könnten aber jederzeit zusammengefaßt werden.


6.2.3.1. Hypersonic

Die Hypersonic-Datenbank-Engine [14] ist eine integrierte relationale Java-Datenbank-
Engine und wird beim JBoss mitgeliefert. Sie ist als Standard-Datenquelle im Deploy-
Verzeichnis in der Datei hsqldb-ds.xml unter dem Namen DefaultDS voreingestellt und
muß nicht erst konfiguriert werden.

<?xml version="1.0" encoding="UTF-8" ?>
<datasources>
  <local-tx-datasource>
    <jndi-name>DefaultDS</jndi-name>
    <connection-url>
      jdbc:hsqldb:${jboss.server.data.dir}${/}hypersonic${/}localDB
    </connection-url>
    <driver-class>org.hsqldb.jdbcDriver</driver-class>
    <user-name>sa</user-name>
    <password></password>
    <min-pool-size>5</min-pool-size>
    <max-pool-size>20</max-pool-size>
    <idle-timeout-minutes>0</idle-timeout-minutes>
    <track-statements />
    <metadata><type-mapping>Hypersonic SQL</type-mapping></metadata>
    <depends>jboss:service=Hypersonic,database=localDB</depends>
  </local-tx-datasource>
  <mbean code="org.jboss.jdbc.HypersonicDatabase"
         name="jboss:service=Hypersonic,database=localDB">
    <attribute name="Database">localDB</attribute>
    <attribute name="InProcessMode">true</attribute>
  </mbean>
</datasources>

Resource 20: Hypersonic-Datenquelle hsqldb-ds.xml

Die Daten und Einstellungen der Hypersonic-Datenbank-Engine werden im Unterverzeichnis

   server/default/data/hypersonic

in der voreingestellten Datenbank-Datei localDB abgelegt. Diese Datenbank muß nicht expli-
zit angelegt werden, da bei Hypersonic nichtvorhandene Datenbanken automatisch erzeugt
werden.

                                              - 176 -
                       Java-Enterprise - Der JBoss-Application-Server


6.2.3.2. MySQL

MySQL ist ein freies Open-Source SQL-Datenbankverwaltungssystem [10], das momentan in
der Version 5.0 vorliegt und von der Homepage des Herstellers MySQL AB [11] herunterge-
laden werden kann.

   http://dev.mysql.com/downloads/mysql/5.0.html

Auf die Installation und Einrichtung eines MySQL-Servers soll in diesem Zusammenhang
nicht weiter eingegangen werden. Lediglich der Zugriff auf einen vorhandenen Server ist hier
von Interesse. Dazu wird der ein JDBC-Treiber für MySQL benötigt, der auch vom Hersteller
heruntergeladen werden kann.

   http://dev.mysql.com/downloads/connector/j/5.0.html

Die heruntergeladene Datei mysql-connector-java-5.0.4.zip muß entpackt und das darin
enthalte Jar-Archiv mit den Treiber-Klassen mysql-connector-java-5.0.4-bin.jar im
Lib-Verzeichnis des Servers (unter der entsprechenden Konfiguration, hier default)

   server/default/lib

abgelegt werden. Falls der JBoss-Server gerade läuft, muß er neu gestartet werden, damit die
Klassen aus dem Jar-Archiv verfügbar sind.

Nun muß der JBoss zu Verwendung der MySQL-Datenbank konfiguriert werden. Dazu wird
die Vorlage

   docs/examples/jca/mysql-ds.xml aus

entsprechend angepaßt und im Deploy-Verzeichnis abgelegt.

<?xml version="1.0" encoding="UTF-8" ?>
<datasources>
  <local-tx-datasource>
    <jndi-name>MySqlDS</jndi-name>
    <connection-url>jdbc:mysql://localhost:3306/jboss</connection-url>
    <driver-class>com.mysql.jdbc.Driver</driver-class>
    <user-name>jboss</user-name>
    <password>jboss</password>
    <exception-sorter-class-name>
      org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter
    </exception-sorter-class-name>
    <metadata>
      <type-mapping>mySQL</type-mapping>
    </metadata>
  </local-tx-datasource>
</datasources>

Resource 21: MySQL-Datenquelle mysql-ds.xml

In der XML-Datei wird die Datenquelle unter dem Namen „MySqlDS“ eingerichtet. Die darin
angegebene Datenbank muß auf dem MySQL-Server vorhanden sein, und der angegebene
Benutzer eingerichtet sein, und darauf Zugriff haben.


                                              - 177 -
                        Java-Enterprise - Der JBoss-Application-Server


6.2.3.3. PostgreSQL

Ein anderes verbreitetes Open-Source Datenbank-Verwaltungs-System ist PostgreSQL [19].
Auch dafür ist der passende JDBC-Treiber erforderlich, der unter

   http://jdbc.postgresql.org/download.html

heruntergeladen werden kann und im Lib-Verzeichnis abgelegt werden muß. Im Vorlagen-
Verzeichnis existiert auch für eine Datei für PostgreSQL, die nach dem Anpassen der Verbin-
dungsparameter im Deploy-Verzeichnis des Servers gespeichert wird.

<?xml version="1.0" encoding="UTF-8" ?>
<datasources>
  <local-tx-datasource>
    <jndi-name>PostgresDS</jndi-name>
    <connection-url>
      jdbc:postgresql://[servername]:[port]/[database name]
    </connection-url>
    <driver-class>org.postgresql.Driver</driver-class>
    <user-name>[benutzername]</user-name>
    <password>[passwort]</password>
    <metadata>
      <type-mapping>PostgreSQL 7.2</type-mapping>
    </metadata>
  </local-tx-datasource>
</datasources>

Resource 22: PostegreSQL-Datenquelle postgres-ds.xml

Die Datenquelle würde hier mit dem Namen PostgresDS eingerichtet und die Angaben müs-
sen für den entsprechenden PostgreSQL-Datenbank-Server erfolgen.


6.2.3.4. Oracle

Auch die Anbindung an die Oracle-Datenbank der Fachhochschule ist von Interesse. Dafür ist
der entsprechende JDBC-Treiber für Oracle

   ojdbc14.jar

aus einer Oracle-Installation oder von der Oracle-Downloadseite

   http://www.oracle.com/technology/software/tech/java/sqlj_jdbc/index.html

erforderlich, der im Lib-Verzeichnis des JBoss-Servers vorhanden sein muß. Danach wird die
Datenquellen-Konfigurations-Datei

   /docs/examples/jca/oracle-ds.xml

editiert und im Deploy-Verzeichnis von JBoss gespeichert, um die Datenquelle mit dem Na-
men OracleDS zu definieren.




                                             - 178 -
                         Java-Enterprise - Der JBoss-Application-Server

<?xml version="1.0" encoding="UTF-8" ?>
<datasources>
  <local-tx-datasource>
    <jndi-name>OracleDS</jndi-name>
    <connection-url>
      jdbc:oracle:thin:@fbim.fh-regensburg.de:1523:ora10g
    </connection-url>
    <driver-class>oracle.jdbc.driver.OracleDriver</driver-class>
    <user-name>luf33607</user-name>
    <password>luf33607</password>
    <exception-sorter-class-name>
      org.jboss.resource.adapter.jdbc.vendor.OracleExceptionSorter
    </exception-sorter-class-name>
    <metadata>
      <type-mapping>Oracle10g</type-mapping>
    </metadata>
  </local-tx-datasource>
</datasources>

Resource 23: Oracle-Datenquelle oracle-ds.xml

In der Datei muß lediglich der Benutzername und das Paßwort angepaßt werden, damit jeder
Benutzer Zugriff auf seine eigenen Daten hat.
Bei der Übertragung der Daten zu Oracle muß aber zum Beispiel auf reservierte Wörter ge-
achtet werden. So kann die Kontonummer in einer Oracle-Datenbank nicht als "number" ge-
speichert werden, da "number" bei Oracle ein reserviertes Wort ist. Dies muß durch eine ent-
sprechende Annotation berücksichtigt werden.


6.2.4. Verwendung

Nach der erfolgreichen Installation kann der JBoss gestartet und bestimmte Dienste über die
voreingestellten TCP-Ports angesprochen werden.

    •   1098 RMI-Registry
    •   1099 Naming-Service
    •   4444 RMI-Object-Port (JRMP)
    •   4445 RMI-Object-Port, PooledInvoker
    •   8009 Tomcat-AJP-Port
    •   8080 Tomcat-HTTP-Connector
    •   8083 Web-Service-Port
    •   8093 UIL2-Port für JMS

Von dieser Liste sind vor allem der Port 8080, von Tomcat für Aufrufe von Web-
Anwendungen mit einem Browser, und der Port 1099, um über den Namensdienst auf Enter-
prise JavaBeans zuzugreifen, wichtig.




                                                - 179 -
                        Java-Enterprise - Der JBoss-Application-Server


6.2.4.1. Einfacher Start

Gestartet werden kann der JBoss grundsätzlich immer mit den mitgelieferten Skripten. Wie
o.g. wird unter Windows die Batch-Datei

    bin/run.bat

aufgerufen. Diese setzt noch einige Umgebungsvariablen und Einstellungen für Java und star-
tet dann die JVM mit der JBoss-Main-Klasse aus dem Archiv run.jar. Die JVM mit dem
JBoss-Server läuft dann in einem Konsolenfenster ab, wo Statusmeldungen und etwaige Feh-
ler (Ausnahmen) sofort zu sehen sind.




Abbildung 74: Konsolenfenster eines laufenden JBoss

Beendet wird der Server dann entweder durch das Drücken von Strg+C im Konsolenfenster
oder durch den Aufruf des Shutdown-Skripts

    bin/shutdown.bat

das dem laufenden Server über JMS das Herunterfahren signalisiert.




                                               - 180 -
                      Java-Enterprise - Der JBoss-Application-Server


6.2.4.2. JBoss als Windows-Systemdienst

Eine Java-Anwendung kann nicht einfach beliebig als Window-Systemdienst gestartet wer-
den, da die Windows-API einige Anforderungen stellt, wie sich ein Dienst zu verhalten hat.
Zu diesem Zweck gibt es unter

   http://javaservice.objectweb.org/

das Tool JavaService, das auf der einen Seite den Aufruf der JVM mit einer beliebigen
Klasse für den gewünschten Dienst kapselt, und sich auf der anderen Seite konform zur API
für Windows-Dienste verhält. Das Archiv mit der derzeit aktuellen JavaService-Version

   JavaService-2.0.10.zip

enthält neben dem Service-Tool auch einige vorgefertigte Installationsskripten, unter anderem
für den JBoss. Die Vorraussetzung dafür sind lediglich die beiden Umgebungsvariablen

   JAVA_HOME        (Absoluter Pfad zum installierten JDK) und

   JBOSS_HOME       (Absoluter Pfad zur Verzeichnis der JBoss-Installation).

die von den Skripten ausgewertet und entsprechend bei einem solchen Java-Dienst eingetra-
gen werden. Somit kann der JBoss relativ einfach durch Aufruf von

   InstallJBoss.bat [depends_on] [-auto / -manual]

installiert werden. Die optionale Angabe [depends_on] gibt einen bereits vorhandenen
Dienst an, der für den Start des neuen JBoss-Dienstes erforderlich ist, z.B. MySQL. Außer-
dem kann mit [-auto / -manual] angegeben werden, ob der Dienst beim Systemstart auto-
matisch mitgestartet werden soll. Standardmäßig wird ein neu installierter Java-Service so
eingestellt, daß er beim Systemstart mitgestartet wird.

Anschließend ist der JBoss-Server als Windows-Systemdienst eingerichtet und wie alle ande-
ren Dienste unter

   Systemsteuerung -> Verwaltung -> Dienste -> JBoss

verfügbar, und kann dort gestartet und beendet werden. Direkt nach der Installation muß der
Dienst einmal manuell gestartet oder aber der Rechner neu gebootet werden.

Über den Status und die Operationen des Servers kann man sich bei dieser Betriebsvariante
über die Log-Datei informieren.

   server/default/log/server.log

Die darin enthaltenen Informationen sind in der Standardeinstellung noch wesentlicher detail-
lierter als die Ausgaben im Konsolenfenster.




                                           - 181 -
                        Java-Enterprise - Der JBoss-Application-Server


6.2.4.3. Deployment von Anwendungen

Um eine eigene Anwendung auf dem Server ablaufen zu lassen, muß diese, einschließlich
irgendwelcher nötigen Zusatzdateien und Konfigurationen, auf dem Server deployed werden.
Dabei sind die verwendeten Datei- und Archiv-Typen und -endungen von Bedeutung. Alle
Archive sind ZIP-Archive, nur mit entsprechend angepaßter Endung [18].

.sar Service-Archive für spezielle Dienste, besonders für MBeans
.rar Resource Archive, z.B. für Konnektoren zu Datenbanken
.har Hibernate-Archive, um eine oder mehrere Hibernate-Instanzen zu deployen
.jar Java-Archive (ganz allgemein)
.war Web-Archive für Webanwendungen, Servlets, JSP- und HTML-Seiten
.ear Enterprise Application Archive für komplette J2EE-Applikationen
.aop Archiv für die aspektorientierte Programmierung
.xml Konfigurationsdatei im XML-Format:
     *aop.xml         Definition von Aspekten bei der aspektorientierten Programmierung
     *deployer.xml Konfiguration eines Deployers für bestimmte Dateitypen
     *ds.xml          Definition von Resource-Adaptern und Datenquellen
     *service.xml Beschreibung von Server-Diensten
.wsr Web-Service-Repository des zukünftigen JBoss.Net-Subsystems
.bsh BeanShell-Skript. BeanShell ist ein in Java geschriebener Interpreter für Java

Auch die Startreihenfolge hängt grundsätzlich von der Endung ab. Über den standardmäßig
eingestellten DeploymentSorter werden die Dateien und Archive in folgender Reihenfolge
verarbeitet bzw. gestartet:

   *deployer.xml, .sar, .rar, *ds.xml, *service.xml, .har, .jar, .war, .wsr, .ear, .zip, .bsh

Damit wird z.B. sichergestellt, daß für eine Web-Applikation die auf EJBs zugreift, diese
auch verfügbar sind, da EJBs vor Web-Applikationen gestartet werden. Ein in
*deployer.xml definierter zusätzlicher Deployer kann in diese Reihenfolge allerdings weite-
re Typen einfügen.

Um den Deploy-Vorgang anzustoßen, werden alle nötigen Dateien der Anwendung im De-
ploy-Verzeichnis

   server/default/deploy/

des laufenden JBoss-Servers abgelegt. Dieses Verzeichnis und alle Dateien darin werden
ständig auf Änderungen hin überwacht, und sobald dort neue Dateien hinzukommen werden
diese von JBoss verarbeitet und die darin spezifizierten Anwendungen gestartet.


6.2.5. Verwaltung über die JMX-Konsole

Die JMX-Konsole [16] dient zur webbasierten Statusabfrage und Verwaltung des JBoss-
Servers. In der Ausgangskonfiguration ist sie in einem Web-Browser unter der URL

   http://localhost:8080/jmx-console/

abrufbar. Dort werden alle konfigurierten Dienste und MBeans nach Typ getrennt aufgelistet.

                                              - 182 -
                       Java-Enterprise - Der JBoss-Application-Server


6.2.5.1. System-Eigenschaften

Im Abschnitt "jboss" wird unter dem Eintrag "name=SystemProperties,type=Service"
das MBean für die Anzeige der System-Eigenschaften aufgerufen. Die Methode showAll()
zeigt eine Auflistung aller konfigurierten Einstellungen des laufenden JBoss bzw. der Be-
triebssystem-Umgebung an. Dies kann zum einen bei Problemen hilfreich sein, zum anderen
können diese Eigenschaften auch in eigenen Anwendungen oder Konfigurationsdateien ver-
wendet werden. Anhang A zeigt eine Auflistung der Eigenschaften.


6.2.5.2. JNDI-Übersicht

Ebenfalls im Abschnitt "jboss" befindet sich der Eintrag "service=JNDIView", der zur An-
zeige des JNDI-MBean führt. Hier gibt es die Methode list(), die eine Übersicht über alle
registrierten Namensräume von Anwendungen und deren Einträge im JNDI-Verzeichnis des
Servers anzeigt. Der allgemeine Java-Namensraum listet z.B. die vorher konfigurierten Da-
tenquellen MySqlDS, OracleDS und DefaultDS auf.

java: Namespace

+-   XAConnectionFactory (class: org.jboss.mq.SpyXAConnectionFactory)
+-   DefaultDS (class: javax.sql.DataSource)
+-   SecurityProxyFactory (class:
|      org.jboss.security.SubjectSecurityProxyFactory)
+-   OracleDS (class: javax.sql.DataSource)
+-   DefaultJMSProvider (class: org.jboss.jms.jndi.JNDIProviderAdapter)
+-   MySqlDS (class: javax.sql.DataSource)
+-   comp (class: javax.naming.Context)
+-   JmsXA (class: org.jboss.resource.adapter.jms.JmsConnectionFactoryImpl)
+-   ConnectionFactory (class: org.jboss.mq.SpyConnectionFactory)
+-   jaas (class: javax.naming.Context)
|     +- HsqlDbRealm (class:
|     |    org.jboss.security.plugins.SecurityDomainContext)
|     +- jbossmq (class: org.jboss.security.plugins.SecurityDomainContext)
|     +- JmsXARealm (class: org.jboss.security.plugins.SecurityDomainContext)
+-   timedCacheFactory (class: javax.naming.Context)
+-   TransactionPropagationContextExporter (class:
|      org.jboss.tm.TransactionPropagationContextFactory)
+-   StdJMSPool (class: org.jboss.jms.asf.StdServerSessionPoolFactory)
+-   Mail (class: javax.mail.Session)
+-   comp.ejb3 (class: javax.naming.Context)
|      NonContext: null
+-   TransactionPropagationContextImporter (class:
|      org.jboss.tm.TransactionPropagationContextImporter)
+-   TransactionManager (class: org.jboss.tm.TxManager)

Resource 24: JNDI-Ansicht des allgemeinen Java-Namensraumes

Diese Ansicht ist eine mögliche Anlaufstelle, um festzustellen ob eine eigene Anwendung
richtig auf dem Server deployed wurde und verfügbar ist, bzw. um den genauen Namen unter
dem die Anwendung erreichbar ist, zu erhalten.




                                            - 183 -
                      Java-Enterprise - Der JBoss-Application-Server


6.2.5.3. Hypersonic-Manager

Der Datenbank-Manager ist eine einfache Benutzeroberfläche zur Verwaltung der integrierten
Hypersonic-Datenbank. Aufgerufen wird der Manager aus der JMX-Konsole über das MBean
"database=localDB,service=Hypersonic" im Abschnitt "jboss". Davon muß die Metho-
de startDatabaseManager() aufgerufen werden.




Abbildung 75: Hypersonic Datenbank-Manager

Links wird das Schema der vorhandenen Datenbanken und Tabellen als Baumstruktur ange-
zeigt. Über das Menü "Command" erzeugt man SQL-Befehle die manuell vervollständigt wer-
den müssen und dann mit "Execute SQL" an die Datenbank geschickt werden. Die Oberflä-
che ist allerdings sehr spartanisch, weil alle Aktionen nur auf diese Weise über SQL erfolgen
können, lediglich das Laden und Speichern von SQL-Skripten ist möglich.
Außerdem kann sich der Datenbank-Manager noch mit Anderen HSQL-Datenbanken als der
integrierten Datenbank über das Netzwerk verbinden.




                                             - 184 -
                      Java-Enterprise - Der JBoss-Application-Server



6.2.6. Produktiver Betrieb

Auf die Möglichkeit JBoss als Applikations-Server im Produktiven Betrieb einzusetzen soll
hier nicht näher eingegangen werden. Jedoch ein wichtigster Schritt dafür wäre die Sicherung
der Administrations-Schnittstellen gegen unbefugten Zugriff. Ein Einstieg zu diesem Thema
kann unter folgenden URL gefunden werden:

   http://sourceforge.net/docman/display_doc.php?docid=20143&group_id=22866




6.2.7. Integration in die IDE

Für den JBoss-Server gibt es die auf Eclipse 3.2.1 basierende Entwicklungsumgebung
"JBoss Eclipse IDE" [38] auf der entsprechenden Webseite bei jboss.com zum download:

   http://labs.jboss.com/portal/jbosside/download/index.html

Das angebotene ZIP-Archive JBossIDE-2.0.0.Beta2-ALL.zip enthält die vollständige Ec-
lipse-Entwicklungsumgebung mit den JBoss-Plugins, und muß nur heruntergeladen und ent-
packt werden. Der Einfachheit halber gibt es das Paket auch an der Fachhochschule unter

   http://java.fh-regensburg.de/download/jboss-eclipse-ide-2.0.zip

Es muß nach dem Herunterladen lediglich in ein Verzeichnis entpackt werden.


6.2.7.1. Eclipse-Projekt anlegen

Wenn man im Eclipse unter File -> New -> Project... ein neues Projekt anlegen will,
erscheinen im Auswahlfenster für einen Projektassistenten etliche JBoss-spezifische Einträge:

   •   EJB 3.0 Project         Enterprise Java Beans 3.0 Projekt [45]

   •   JBoss AOP Project       Projekt zur aspektorientierten Programmierung mit JBoss [40]

   •   JBoss Cache             Projekt das einen Cache zum Zwischenspeichern von Daten-
                               bankzugriffen einbindet [39]

   •   J2EE Projects           Projekt mit älteren Enterprise Java Beans [45]

   •   JBoss jBPM              Projekt mit der JBoss-Ablauf-Verwaltung [41]

   •   JBoss Rules             Projekt zur Entwicklung von Geschäftsrichtlinien mit JBoss
                               [42]

Für unsere Zwecke wird ein "EJB 3.0 Project" benötigt. Optional kann auch ein "norma-
les" Java-Projekt erzeugt, und bei diesem dann die EJB 3.0 Bibliotheken hinzugefügt werden.




                                           - 185 -
                        Java-Enterprise - Der JBoss-Application-Server




Abbildung 76: Eclipse-Dialog "New Project"

Für das neue Projekt muß wie gewohnt zuerst ein Name vergeben werden. Als nächstes fragt
der Assistent dann nach einem JBoss-Server den man konkret verwenden will, und dessen
Bibliotheken eingebunden werden sollen.

Neben den Standard-Bibliotheken des verwendeten JDK, sind in dem Projekt dann auch so-
fort die "JBoss EJB3 Libraries", also alle Archive aus dem JBoss-Verzeichnis, die für die
Entwicklung von Enterprise Java Beans 3 notwendig sind, verfügbar und bereits in den "Java
Build Path" eingebunden.




                                             - 186 -
                         Java-Enterprise - Der JBoss-Application-Server




Abbildung 77: Eclipse-Dialog "Select a JBoss configuration"


6.2.7.2. JBoss-Konfiguration anlegen

Falls noch keine JBoss-Konfiguration vorhanden ist, muß mit "Create a JBoss Server"
eine angelegt werden. Dafür wird zuerst die Version des verwendeten Servers, in unserem
Fall "JBoss AS 4.0", ausgewählt.




Abbildung 78: Eclipse-Dialog "New Server" - Define


                                                - 187 -
                         Java-Enterprise - Der JBoss-Application-Server

Im nächsten Dialogfenster wird ein eindeutiger Name für den Server vergeben, und das Basis-
Verzeichnis, in das der Server zuvor entpackt bzw. installiert wurde, angegeben. Daraufhin
werden die vorhandenen Konfigurationen des Servers erkannt und aufgelistet. Mit "Finish"
wird der Server dann endgültig im Eclipse angelegt.




Abbildung 79: Eclipse-Dialog "New Server" - Create

Der neu angelegte JBoss-Server erscheint dann in der Liste der verfügbaren Server und kann
nun bei Projekten ausgewählt werden.


6.2.7.3. Den Server steuern

                                              Nach dem Anlegen eines JBoss-Servers ist im Pa-
                                              ckage-Explorer neben den eigentlichen Java-
                                              Projekten ein neues Projekt Servers vorhanden,
                                              das alle angelegten Server enthält.

                                              Dadurch kann ein Server direkt aus Eclipse heraus
                                              gesteuert werden, sofern er nicht, wie oben be-
                                              schrieben (siehe 6.2.4.2), als Systemdienst einge-
                                              richtet wurde. In diesem Fall kommt es zu Kon-
                                              flikten, wenn der Systemdienst nicht zuvor manu-
                                              ell beendet wird.


Abbildung 80: Eclipse Package Explorer - Servers




                                               - 188 -
                         Java-Enterprise - Der JBoss-Application-Server

In der Servers-Ansicht werden der Server und sein aktueller Status aufgelistet. Über die
Schaltflächen oder das Kontext-Menü kann er gestartet und gestoppt werden.




Abbildung 81: Eclipse Servers-Ansicht

Sollte Eclipse die Ansicht nach dem Anlegen eines Servers nicht selbständig einblenden, kann
sie im Menü unter

    Window -> Show View -> Other... -> Server -> Servers

aufgerufen werden. Die Ausgaben des Servers erfolgen, wie auch bei anderen Programmen, in
einem Konsolenfenster von Eclipse.




Abbildung 82: Eclipse-Console mit laufendem JBoss




6.2.7.4. Deployment eigener Anwendungen

Grundsätzlich sollte das Deployment von selbst erstellten Anwendungen direkt aus der Ober-
fläche heraus auf einem eingerichteten Server erfolgen können. Da sich jedoch die
"JBoss Eclipse IDE" in der Version 2.0 momentan noch in einem Beta-Stadium befindet, ist
dies nicht möglich. Das Deployment muß daher manuell (siehe 6.2.4.3) oder durch
Ant-Skripte erfolgen, die alle nötigen Dateien in ein Archiv verpacken und dieses in das De-
ploy-Verzeichnis des JBoss-Servers kopieren.




                                              - 189 -
                           Java-Enterprise - Enterprise JavaBeans


6.3. Enterprise JavaBeans
6.3.1. Allgemeines

Enterprise JavaBeans sind die Komponenten innerhalb eines J2EE-Servers, mit denen eine
Anwendung implementiert wird. Sie sind aus einfachen Java-Klassen (POJO) aufgebaut und
nutzen die Dienste, die ein Applikations-Server zur Verfügung stellt. Durch zusätzliche XML-
Konfigurationsdateien (Deployment-Descriptor) werden Eigenschaften von EJBs definiert,
die nicht hart codiert sind [73].


6.3.1.1. Annotations

In der Version 3 der Enterprise Java (JSR-220) [66] wird die Konfiguration und Beschreibung
der Klassen durch Annotations vorgenommen und die Klassen müssen keine Verwaltungs-
bzw. Typ-Schnittstellen (Component-Interface) implementieren, was ihre Entwicklung im
Vergleich zu den vorherigen Versionen 1 und 2 stark vereinfacht, da wesentlich weniger
komplexe Deployment-Descriptoren zusätzlich zu den Klassen der Anwendung geschrieben
werden müssen und keine technischen Vorgaben bestehen.
Durch die Annotations werden wie bei JAXB oder Hibernate die Meta-Informationen zu der
Aufgabe, die eine Bean-Klasse innerhalb der Anwendung übernehmen soll, und ihrem Ver-
halten direkt im Quellcode beschrieben und vom Applikations-Server ausgewertet.


6.3.1.2. Home-Interface

Das Home-Interface einer Bean dient zum Erzeugen, Laden oder Löschen von Instanzen der
Bean-Klasse. Weil eine Client-Anwendung nicht direkt auf die Komponenten eines Applika-
tions-Servers zugreifen kann, muß eine Schnittstelle für diese Funktionalitäten definiert wer-
den. Bei EJB3 ist es allerdings nicht mehr notwendig, daß eine Bean-Klasse ein Home-
Interface besitzt, weil man über die lookup()-Methode des EJBContext von außerhalb auf
Bean-Objekte und andere Ressourcen zugreifen kann, die dafür vorgesehen sind.


6.3.1.3. Business-Interface

Das Business-Interface wird vom Entwickler frei definiert (POJI) und gibt die Methoden für
die Geschäftslogik einer Anwendung an. Es ist für Session-Beans und Message-Driven-Beans
immer erforderlich. Wenn eine Klasse nur eine einzige Schnittstelle implementiert, wird diese
Schnittstelle als lokales Business-Interface verwendet. Bei mehreren Schnittstellen müssen
das/die Business-Interface(s) durch Annotations explizit angegeben werden.

   •   @Local  definiert ein lokales Business-Interface innerhalb des Applikations-Servers
   •   @Remote definiert ein Business-Interface das über RMI aufgerufen werden kann

Eine Schnittstelle kann nicht gleichzeitig beide Funktionalitäten erfüllen. Folgende Schnitt-
stellen werden bei der Suche nach einem gültigen Business-Interface grundsätzlich ignoriert:

   •   java.io.Serializable
   •   java.io.Externalizable
   •   alle Schnittstellen aus dem Paket javax.ejb

                                           - 190 -
                            Java-Enterprise - Enterprise JavaBeans


6.3.1.4. Life-Cycle und Callback-Methoden

Jedes Objekt einer Bean-Klasse durchläuft innerhalb eines Applikations-Servers mehrere Zu-
stände, wodurch sich ein eigener Lebenszyklus (Life-Cycle) für jedes Objekt ergibt, der aber
je nach Art der Bean-Klasse unterschiedlich ist. Damit auf diesen Zyklus der Bean-Objekte,
unabhängig von der Geschäftslogik der Anwendung eingegangen werden kann, ist die Ver-
wendung von Callback-Methoden vorgesehen. Die Methoden müssen bei den alten EJB Ver-
sionen, je nach Bean-Typ, über eine bestimmte Schnittstelle (javax.ejb.SessionBean,
javax.ejb.EntityBean, java.ejb.SessionBean) implementiert werden.
Bei EJB3 gibt es die Annotations, um eine beliebige Methode als Callback-Methode für ein
bestimmtes Ereignis des Lebenszyklus anzugeben [74].

EJB2-Methode                      EJB3-Annotation         Bean-Typ    Beschreibung
                                                                      wird vor den Aufruf
                                                          Session
ejbActivate()                     @PostActivate                       einer Bean-Methode
                                                          Entity
                                                                      aufgerufen
                                                                      wird nach dem Auf-
                                                          Session
ejbPassivate()                    @PrePassivate                       ruf einer Bean-
                                                          Entity
                                                                      Methode aufgerufen
                                                          Session     wird vor dem Initiali-
ejbCreate()                       @PostConstruct          Entity      sieren einer neuen
                                                          Message     Instanz aufgerufen
                                  @PreDestroy             Session     wird vor dem entfer-
ejbRemove()                       @PreRemove              Entity      nen eines Objekts
                                  @PostRemove             Message     aufgerufen
                                                          Session     wird nach dem Initia-
ejbPostCreate()                   @PostConstruct          Entity      lisieren einer neuen
                                                          Message     Instanz aufgerufen
                                                                      wird beim Laden aus
ejbLoad()                         @PostLoad               Entity      der Datenbank aufge-
                                                                      rufen
                                  @PrePersist                         wird beim Speichern
                                  @PostPersist                        der Daten einer Enti-
ejbStore()                                                Entity
                                  @PreUpdate                          ty-Bean in der Da-
                                  @PostUpdate                         tenbank aufgerufen
setSessionContext()                                   Session
setEntityContext()                @Resource           Entity          gibt den Kontext
unsetEntityContext()              @PersistenceContext Entity          einer Bean an
setMessageDrivenContext()                             Message

Tabelle 16: Bean-Callback-Methoden und Annotations

Die Zuordnung der EJB3-Annotations zu den alten Callback-Methoden darf in dieser Tabelle
nicht als absolut festgelegt aufgefaßt werden. Die Annotations haben zum Teil eine leicht
abweichende Semantik und erlauben eine feiner abgestufte Kontrolle der Beans.

Innerhalb des Lebenszyklus eines Bean-Objekts werden auch bestimmte andere Vorgänge
definiert, für die nicht unbedingt eine Callback-Methode aufgerufen wird (z.B. Dependency-
Injection), aber unter Umständen über andere Mechanismen beeinflußt werden können.


                                             - 191 -
                             Java-Enterprise - Enterprise JavaBeans



6.3.2. Session-Beans

Session-Beans bilden die Vorgänge der Geschäftslogik ab, bei denen ein Nutzer auf das Sy-
stem zugreift. Sie bedienen sich dabei in der Regel mehrerer Entity-Beans um die Auswir-
kungen eines Vorgangs darzustellen. Man unterscheidet zwei Arten von Session-Beans [73].


6.3.2.1. Zustandslose Session-Beans

Zustandslose Session-Beans speichert keine Informationen aus vorherigen Aufrufen und un-
terscheiden auch nicht von welchem Client aus der Aufruf erfolgt ist. Bei jedem Methoden-
aufruf müssen alle notwendigen Parameter übergeben werden. Session-Bean-Objekte der glei-
chen Klasse haben daher keine eigene Identität und sind untereinander nicht unterscheidbar.
Vom Applikations-Server wird in der Regel ein bestimmte Anzahl Beans instanziert
(ejbCreate) und in einem Pool abgelegt. Bei einem Aufruf der Bean-Klasse wird dann ein
beliebiges Bean-Objekt aus dem Pool verwendet. Der Container kann die Anzahl der Beans
jederzeit reduzieren (ejbRemove). Der Lebenszyklus einer zustandslosen Session-Bean sieht
daher folgendermaßen aus.




Abbildung 83: Lebenszyklus einer zustandslosen Session-Bean

Eine Klasse für zustandslose Session-Beans wird bei EJB3 durch die Annotation @Stateless
gekennzeichnet.


6.3.2.2. Zustandsbehaftete Session-Beans

Zustandsbehaftete Session-Beans können in jeder Instanz Informationen speichern, die bei
aufeinanderfolgenden Aufrufen immer wieder zur Verfügung stehen. Durch die Vergabe einer
eindeutigen ID durch den Applikations-Server sind die Bean-Instanzen unterscheidbar, und
die richtige Zuordnung zu den Aufrufen des Systems ist möglich. Durch diese Zuordnung
können die Bean-Objekte nicht beliebig aus einem Pool verwendet und bei zu großer Anzahl
einfach verworfen werden. Dafür hat der Container die Möglichkeit die Beans in einen passi-
ven Zustand zu versetzen und sie dabei aus dem Arbeitsspeicher zu entfernen und anderweitig
zu speichern, bis ein Bean-Objekt wieder bei einem Aufruf benötigt wird.




Abbildung 84: Lebenszyklus einer zustandsbehafteten Session-Bean

Eine Klasse für zustandsbehaftete Session-Beans wird durch die Annotation @Stateful ge-
kennzeichnet. Zur Initialisierung bzw. Löschung einer Bean-Instanz können Methoden mit
@Init bzw. @Remove annotiert werden.


                                              - 192 -
                             Java-Enterprise - Enterprise JavaBeans


6.3.2.3. Dependency-Injection

Damit der Aufbau eines Bean-Objekts einfach bleibt, muß es sich nicht selbst um benötigte
Ressourcen kümmern. Es bekommt die gewünschten Ressourcen durch Dependency-Injection
vom Container zur Verfügung gestellt. Dazu müssen lediglich die nötigen Instanzvariablen in
der Klasse definiert und durch Annotations gekennzeichnet werden. Dies entspricht dem Prin-
zip "Inversion of Control" (IoC) oder "don't call us, we call you", und drückt aus, daß die
Verantwortlichkeit der Programmausführung stärker beim Applikations-Server liegt.

Annotation          Beschreibung
@Resource
                    gibt eine Instanzvariable als Ressource an, die vom Container inji-
                    ziert werden soll
@Resources          faßt mehrere @Resource Annotation
@PersistenceContext injiziert einen EntityManager zur Verwaltung von Entity-Beans

Tabelle 17: Annotation für Dependency-Injection

Durchgeführt wird die Dependency-Injection durch den Container bevor Methoden aus einem
Business-Interface oder Callback-Methoden für den Lebenszyklus aufgerufen werden. Im
Konstruktor einer Bean-Klasse muß darauf geachtet werden, daß diese Ressourcen dort nicht
verfügbar sind.
Die Art der Resource wird durch den Typ der Instanzvariablen bestimmt.

    @Resource(mappedName = "java:/DefaultDS")
    private DataSource ds;

    @Resource(mappedName = "java:/ConnectionFactory")
    private ConnectionFactory factory;

Der zusätzliche Parameter mappedName gibt den JNDI-Namen einer vorhandenen Ressource
an, für die der Container eine Referenz injizieren soll.


6.3.2.4. Interceptoren (AOP)

                                                            Durch Interceptoren wird der An-
                                                            satz der aspektorientierten Pro-
                                                            grammierung (AOP) realisiert.
                                                            Eine Bean-Methode enthält dabei
                                                            nur die Funktionalitäten, die von der
                                                            Geschäftslogik der Anwendung
                                                            vorgesehen sind.
                                                            Allgemeine Funktionalitäten, wie
                                                            z.B. Logging, Timing oder Authen-
                                                            tifizierung, werden von Intercepto-
                                                            ren übernommen, die den Aufruf
                                                            von beliebigen Methoden "um-
                                                            schließen" und die zusätzliche
                                                            Funktionalität davor bzw. danach
                                                            einfügen.
Abbildung 85: Interceptor-Aufruf


                                                  - 193 -
                            Java-Enterprise - Enterprise JavaBeans



Durch die Annotation @AroundInvoke wird eine Methode als Interceptor definiert. Jede Klas-
se darf nur eine solche Methode besitzen, die aber die folgende Signatur haben muß.

@AroundInvoke
public Object intercept (InvocationContext ctx) throws Exception {
  // Funktionalitäten vor dem Methodenaufruf

    ...

    Object result = ctx.proceed();

    ...

    // Funktionalitäten nach dem Methodenaufruf

    return result;
}

Code 120: Interceptor-Methode

Über den InvocationContext kann die Interceptor-Methode auf die Parameter des ursprüng-
lichen Methodenaufrufs zugreifen. Durch die Methode proceed() wird der Aufruf der ur-
sprünglichen Methode fortgesetzt, sofern die Implementierung der Interceptor-Methode den
Aufruf nicht verhindern will. Schließlich kann auch der Rückgabewert des Methodenaufrufs
ausgewertet und verändert werden.

Soll die Methode in einer separaten Interceptor-Klasse implementiert werden, muß dies bei
der Bean-Klasse durch die zusätzliche Annotation @Interceptor(MyInteceptor.class)
angegeben werden.


6.3.2.5. EntityManager

Der im folgenden Beispiel verwendete EntityManager ist ein weiterer Teil der Java-
Persistence-API und damit des im JBoss verwendeten Hibernate-Frameworks, der in
Kapitel 4.6 noch nicht angesprochen wurde. Im Prinzip besitzt ein EntityManager dieselben
Methoden zum Arbeiten mit persistenten Objekten, wie eine Hibernate-Session. Er ist aber für
die Verwendung in einem Applikations-Server zusammen mit einem PersistenceContext
vorgesehen. Man muß sich nicht selbst um die Konfiguration der Datenbankanbindung, der
persistenten Klassen und das Öffnen von Hibernate-Sitzungen kümmern, sondern kann die
Instanz des EnitityManagers, die man vom Container erhält, ohne weiteres sofort verwen-
den.


6.3.2.6. Service-Klasse

Die Service-Klasse wird als zustandslose Session-Bean (@Stateless) aufgebaut. Die Schnitt-
stelle IExtendedAccountingService wird implementiert kann durch die entsprechende An-
notation (@Remote) direkt als Business-Interface für entfernte Aufrufe angegeben werden.
Durch Dependency-Injection erhält die Klasse eine Instanz des Hibernate-EntityManagers
(@PersistenceContext), zur Verwaltung von Entity-Beans. Der angegebene unitName wird
später in einer separaten Konfigurationsdatei (persistence.xml) genau definiert.


                                           - 194 -
                      Java-Enterprise - Enterprise JavaBeans

package de.fhr.vs_iw.ejb;

@Stateless
@Remote(IExtendedAccountingService.class)
public class EJBAccountingService implements IExtendedAccountingService {

  @PersistenceContext(unitName = "EJBAccountingServiceDS")
  private EntityManager manager;

 public EJBAccountingService() {}

 public IAccount accountChange(IAccount account) {
   return manager.merge(account);
 }

 public IAccountingEntry accountingEntryChange(IAccountingEntry entry) {
   return manager.merge(entry);
 }

 public Double calculateBalance(Integer number)
     throws IOException, AccountingException {
   double saldo = 0.0d;
   Account account = (Account)getAccountByNumber(number);
   for (IAccountingEntry entry : account.getDebits()) {
     saldo += entry.getAmount();
   }
   for (IAccountingEntry entry : account.getCredits()) {
     saldo -= entry.getAmount();
   }
   return saldo;
 }

 public void createAccount(Integer number, AccountType type,
     String description) throws AccountingException {
   if (number <= 0) {
     throw new AccountingException("Die Nummer darf nicht negativ sein");
   }
   checkAccountNumber(number, false);
   Account acc = new Account();
   acc.setNumber(number);
   acc.setType(type);
   acc.setDescription(description);
   manager.persist(acc);
 }

 public Integer createAccountingEntry(Integer debit, Integer credit,
     Double amount, String text) throws AccountingException {
   checkAccountNumber(debit, true);
   checkAccountNumber(credit, true);
   if (debit.equals(credit)) {
     AccountingEntry.exceptionIdenticalAccounts();
   }
   AccountingEntry entry = new AccountingEntry();
   entry.setText(text);
   entry.setAmount(amount);
   entry.setDebit(manager.find(Account.class, debit));
   entry.setCredit(manager.find(Account.class, credit));
   manager.persist(entry);
   return entry.getId();
 }




                                     - 195 -
                            Java-Enterprise - Enterprise JavaBeans

    public IAccount getAccountByNumber(Integer number)
        throws AccountingException {
      checkAccountNumber(number, true);
      return manager.find(Account.class, number);
    }

    public Set<Integer> getAccountingEntries() {
      ArrayList list = (ArrayList)manager.createQuery(
          "SELECT id FROM accountingentry").getResultList();
      return new HashSet<Integer>(list);
    }

    public IAccountingEntry getAccountingEntryById(Integer id)
        throws AccountingException {
      checkAccountingEntryId(id);
      return manager.find(AccountingEntry.class, id);
    }

    public Set<Integer> getAccounts() {
      ArrayList list = (ArrayList)manager.createQuery(
          "SELECT number FROM account").getResultList();
      return new HashSet<Integer>(list);
    }

    public void removeAccount(Integer number)
        throws AccountingException {
      try {
        final Account account = checkAccountNumber(number, true);
        manager.remove(account);
      } catch (final RuntimeException e) {
        throw new AccountingException(e);
      }
    }

    public void removeAccountingEntry(Integer id)
        throws AccountingException {
      try {
        final AccountingEntry entry = checkAccountingEntryId(id);
        entry.getDebit().getDebits().remove(entry);
        entry.getCredit().getCredits().remove(entry);
        manager.remove(entry);
      } catch (final RuntimeException e) {
        throw new AccountingException(e);
      }
    }

    ...
}

Code 121: Class EJBAccountingService

Diese Session-Bean verhält sich im Prinzip wie eine Kombination aus der RMI-Service-
Klasse (siehe 5.3.5.1) und der Hibernate-Service-Klasse (siehe 4.6.6.1). Über den Applikati-
ons-Server können die Methoden der Klasse von einem Client entfernt aufgerufen werden.
Die Implementierung der Methoden benutzt dann die Funktionalitäten des Hibernate-
EntityMangers um mit den Konten- und Buchungsobjekten (Entity-Beans) zu Arbeiten, und
so die Buchhaltungsdaten zu verwalten.
Beim Löschen von Buchungsobjekten ist darauf zu achten, daß die Objekte auch aus den zu-
gehörigen Sets bei den zugeordneten Konten entfernt werden, da sonst eine Referenz darauf
bestehen bleibt und so der EntityManager in einen inkonsistenten Zustand kommt.

                                           - 196 -
                             Java-Enterprise - Enterprise JavaBeans



6.3.3. Entity-Beans

Durch Entity-Beans werden die dauerhaften (persistenten) Daten einer Anwendung model-
liert, und zur Laufzeit zur Verfügung gestellt. In der Regel stellt eine Entity-Bean-Klasse ein
real existierendes Objekt (Person, Rechnung, Konto, Buchung) dar. Die Daten einer Bean-
Instanz stammen z.B. aus einem Datensatz einer Datenbank [73].
Die Instanzen von Entity-Beans dienen auf dem Applikations-Server als Datenzugriffsobjekte
(Data Access Object - DAO) und werden vom Container verwaltet, wodurch sich auch wieder
ein bestimmter Lebenszyklus ergibt.




Abbildung 86: Lebenszyklus einer Entity-Bean

Durch diesen Lebenszyklus sind Entity-Beans an den Applikations-Server gebunden. Um die
Daten an einen Client zu übertragen wird das Entwicklungs-Muster "Datentransferobjekt"
(Data Transfer Object - DTO) verwendet und ein separates Objekte verwendet.

Bei der Java Enterprise Edition 5 und dem Enterprise JavaBeans 3.0 Standard gibt es diese
Unterscheidung zwischen DAO und DTO mittlerweile nicht mehr. Die Entity-Beans werden
im Sinne der Java-Persistence-API verwaltet und besitzen auch keinen vorgegebenen Lebens-
zyklus mehr. Ein persistentes Objekt kann von der Persistenzschicht der Anwendung abge-
trennt, in anderen Anwendungsteilen verwendet, und später wieder angefügt werden
(siehe 4.6.5.1). Eine Entity-Bean-Klasse wird durch die Annotation @Entity gekennzeichnet.


6.3.3.1. Bean-Managed-Persistence

Die Persistenz der Daten kann auf verschiedene Weise realisiert werden. Man kann den
Zugriff auf die persistenten Daten in den Methoden der Entity-Beans selbst implementieren,
was als Bean-Managed-Persistence (BMP) bezeichnet wird. Dies würde z.B. dem Ansatz wie
in Kapitel 4.5 entsprechen, eine direkte Datenbankverbindung zu öffnen, und über eigene
SQL-Anweisungen manuell auf die Daten in der Datenbank zuzugreifen.


6.3.3.2. Container-Managed-Persistence

Der bessere Ansatz ist aber die vom Prinzip her bereits angesprochene Variante der Contai-
ner-Managed-Persistence (CMP), wobei sich der Container um die Persistenz der Daten
kümmert. Konkret wird dies eben durch die Verwendung des EntityMangers erreicht, der die
persistenten Objekte verwaltet. Derartige persistente Entity-Bean-Klassen werden dazu mit
den Annotations der Java-Persistence-API bzw. Hibernate-Annotations, wie sie in Kapitel 4.6
beschrieben wurden, ausgezeichnet. Mit einigen Änderungen entsprechen die Klassen aus
jenem Kapitel bereits den Notwendigkeiten für Java-Enterprise Entity-Beans.




                                               - 197 -
                             Java-Enterprise - Enterprise JavaBeans


6.3.3.3. FetchType

Der Parameter fetch für den FetchType bei der Annotation @OneToMany gibt an, wie die in
einer Beziehung referenzierten Objekte aus der Datenbank geladen werden sollen. Dazu gibt
es zwei mögliche Varianten

   •   FetchType.EAGER         referenzierte Objekte werden zusammen mit dem Basisobjekt
                               instanziert und ihre Daten vollständig aus der Datenbank gela-
                               den, sofern ein Objekt nicht bereits geladen wurde
   •   FetchType.LAZY          es wird dynamisch ein Proxy-Objekt für ein referenziertes Ob-
                               jekt erzeugt und verwendet, auf die Datenbank wird erst zuge-
                               griffen, wenn die Daten des Objekts wirklich abgerufen werden

Bei den Entity-Bean-Klassen wird der Typ EAGER verwendet, damit ein Objekt mit allen rele-
vanten Daten und referenzierten Objekten vollständig geladen wird. Objekte, die von einem
Client verwendet werden, haben sonst später keinen Zugriff auf die Datenbank mehr, weil die
Entity-Beans vom Server abgekoppelt werden.


6.3.3.4. Entity-Bean-Prinzip

Das Prinzip bei der Arbeit mit Entity-Beans beruht bei Java-Enterprise-Anwendungen darauf,
daß die Bean-Objekte zwischen den Schichten und Komponenten einer Anwendung ausge-
tauscht werden. Bei entfernten Aufrufen der Enterprise-Anwendung soll dies auch die Per-
formance, z.B. gegenüber RMI erhöhen, da nur ein größeres Objekt übertragen wird, und wei-
tere Methodenaufrufe dann lokal erfolgen.




Abbildung 87: Ablauf des Entity-Bean-Prinzips bei einem Kontenobjekt

                                              - 198 -
                             Java-Enterprise - Enterprise JavaBeans

Bei der Buchhaltung bedeutet dies konkret, daß zwar die Methoden der Service-Session-Bean,
wie bei der RMI-Implementierung des Buchhaltungssystems, entfernt aufgerufen werden, die
Methoden getAccountByNumber() und getAccountingEntryById() aber ihrerseits keine
entfernten Objekte zurückgeben, sondern Objekte der definierten Entity-Bean-Klassen. Diese
Objekte sind serialisierbar und werden vollständig zum Client übertragen, weshalb es eben
wichtig ist, daß die Objekte vom EntityManager vorher vollständig geladen wurden. Die
persistenten Objekte werden dabei natürlich von der Persistenzschicht der Enterprise-
Anwendung abgekoppelt.

Der Client kann nun die Objekte und ihre Daten beliebig verwenden. Ein Problem besteht
natürlich dann, wenn der Client die Daten eines solchen Objekts ändert. Da das Entity-Bean-
Objekt keine Verbindung zur Datenbank mehr hat, wäre die Änderung nicht persistent, und
andere Clients würden auch nichts davon mitbekommen. Deshalb muß das Objekt nach einer
Änderung an den Applikations-Server zurückgeschickt werden.

An dieser Stelle kommen erstmals die beiden zusätzlichen Methoden der erweiterten Service-
Schnittstelle (IExtendedAccountingService), die bei der Service-Session-Bean als Busi-
ness-Interface verwendet wird, zum Einsatz. Durch den Aufruf von changeAccount() bzw.
changeAccountingEntry() kann ein geändertes Konten- bzw. Buchungsobjekt wieder an
den EntityManager auf dem Applikations-Server, zum Speichern der Daten in der Datenbank,
übergeben werden. Jede Client-Anwendung der Enterprise-JavaBean-Buchhaltung muß nach
dem Ändern von Daten die entsprechende Methode aufrufen.


6.3.3.5. Konto-Klasse

Die Konto-Klasse wird als CMP-Entity-Bean mit Annotations ausgezeichnet und entspre-
chend implementiert.




Abbildung 88: Beziehung der Entity-Beans

Jedem Kontenobjekt werden die Soll- und Haben-Buchungen jeweils als Set von Objekten
mit der Annotation @OneToMany zugeordnet, um die 1:n-Beziehung herzustellen. Neben der
üblichen Implementierung der Konto-Schnittstellte wird die Klasse mit Getter/Setter-
Methoden für die Sets der Buchungsobjekte und die Kontonummer ausgestattet.

package de.fhr.vs_iw.ejb;

@Entity
@Table(name = "account")
public final class Account implements IAccount, Serializable {

  private Set<AccountingEntry> credits;
  private Set<AccountingEntry> debits;

  public Account() {}




                                            - 199 -
                            Java-Enterprise - Enterprise JavaBeans

    @Transient
    public Set<Integer> getCreditEntries() {
      HashSet<Integer> ret = new HashSet<Integer>();
      for (AccountingEntry entry : credits) {
        ret.add(entry.getId());
      }
      return ret;
    }

    @Transient
    public Set<Integer> getDebitEntries() {
      HashSet<Integer> ret = new HashSet<Integer>();
      for (AccountingEntry entry : debits) {
        ret.add(entry.getId());
      }
      return ret;
    }

    @Id
    public Integer getNumber() {
      return number;
    }

    @OneToMany(mappedBy = "credit", fetch = FetchType.EAGER,
        cascade = CascadeType.ALL)
    public Set<AccountingEntry> getCredits() {
      return credits;
    }

    @OneToMany(mappedBy = "debit", fetch = FetchType.EAGER,
        cascade = CascadeType.ALL)
    public Set<AccountingEntry> getDebits() {
      return debits;
    }

    public void setCredits(Set<AccountingEntry> credits) {
      this.credits = credits;
    }

    public void setDebits(Set<AccountingEntry> debits) {
      this.debits = debits;
    }

    public void setNumber(Integer number) {
      this.number = number;
    }

    ...
}

Code 122: Class Account (EJB)

Der EntityManager lädt alle Daten für ein Konto und die Sets mit den referenzierten Bu-
chungen, so daß eine Instanz der Konto-Bean-Klasse dann völlig unabhängig verwendet wer-
den kann.




                                           - 200 -
                          Java-Enterprise - Enterprise JavaBeans


6.3.3.6. Buchungs-Klasse

Die Buchungs-Klasse wird mit Annotations passend zur Konto-Klasse versehen. Da es nicht
mehr reicht nur die Nummern der referenzierten Soll- und Haben-Konten anzugeben, wird die
Klasse mit Instanzvariablen vom Typ Account ausgestattet. Die zugehörigen Getter-
Methoden werden durch @ManyToOne und @JoinColumn als Gegenstück für die 1:n-
Beziehung mit der Konto-Klasse angegeben.

package de.fhr.vs_iw.ejb;

@Entity @Table(name = "accountingentry")
public class AccountingEntry implements IAccountingEntry, Serializable {
  private Account credit;
  private Account debit;

  @PersistenceContext(unitName = "EJBAccountingServiceDS")
  private EntityManager manager;

  public AccountingEntry() {}

  @Transient
  public Integer getCreditAccount() {
    return credit.getNumber();
  }

  @Transient
  public Integer getDebitAccount() {
    return debit.getNumber();
  }

  @Id @GeneratedValue
  public Integer getId() {
    return id;
  }

  public void setCreditAccount(Integer number) throws AccountingException {
    checkAccountNumber(number, true);
    if (number.equals(getDebitAccount())) {
      exceptionIdenticalAccounts();
    }
    setCredit(manager.find(Account.class, number));
  }

  public void setDebitAccount(Integer number) throws AccountingException {
    checkAccountNumber(number, true);
    if (number.equals(getCreditAccount())) {
      exceptionIdenticalAccounts();
    }
    setDebit(manager.find(Account.class, number));
  }

  public void setText(String text) {
    this.text = text;
  }

  @ManyToOne
  @JoinColumn(name = "credit")
  public Account getCredit() {
    return credit;
  }

                                         - 201 -
                            Java-Enterprise - Enterprise JavaBeans

    @ManyToOne
    @JoinColumn(name = "debit")
    public Account getDebit() {
      return debit;
    }

    public void setCredit(Account credit) {
      this.credit = credit;
    }

    public void setDebit(Account debit) {
      this.debit = debit;
    }

    public void setId(Integer id) {
      this.id = id;
    }

    ...
}

Code 123: Class AccountingEntry (EJB)

Damit auch Objekte dieser Bean-Klasse unabhängig sind, wird zur Zuordnung von Soll- und
Haben-Kontenobjekten direkt ein EntityManager benutzt, der wie bei der Service-Session-
Bean-Klasse durch Dependency-Injection vom Container zur Verfügung gestellt wird ist.


6.3.4. Weitere Bean-Arten

Neben den Session-Beans und den Entity-Beans, die in der Regel den Großteil der benötigten
Beans in einer Anwendung darstellen, gibt es noch weitere Arten von Enterprise JavaBeans
und andere Dienste die von einem Applikations-Server zur Verfügung gestellt werden.


6.3.4.1. JMS - Message-Driven-Beans

Message-Driven-Beans (MDB) machen Java-Enterprise-Anwendungen für asynchrone
Kommunikation zugänglich. Hierzu wird der Java-Messaging-Service (JMS) [15] verwendet,
der vom JBoss zur Verfügung gestellt wird. Diese Beans werden häufig bei der Kommunika-
tion mit Legacy-Systemen verwendet [73].

Wie die anderen Bean-Arten auch, werden MDBs von Applikations-Server verwaltet. Sie
haben dabei fast den gleichen Lebenszyklus wie zustandslose Session-Beans.




Abbildung 89: Lebenszyklus einer Message-Driven-Bean

Der Java-Messaging-Service unterstützt zwei verschiedene Varianten der Nachrichtenverar-
beitung, Nachrichtenschlangen (message queues) und ein Anmelde-Versende-System (pu-
blish-subscribe - topic) [75].


                                             - 202 -
                             Java-Enterprise - Enterprise JavaBeans

Bei Nachrichtenschlangen gibt es immer genau einen Abnehmer, der die eingehenden Nach-
richten erhält. Wenn es für eine Nachrichtenschlange zu einem Zeitpunkt keinen Abnehmer
gibt, so schlägt des senden von Nachrichten an diese Schlange fehl. Wenn die Nachrichten
schneller eintreffen als der Abnehmer sie verarbeiten kann, werden die Nachrichten in der
Warteschlange eingereiht und der Reihe nach abgearbeitet.




Abbildung 90: Nachrichtenschlange

Beim Anmelde-Versende-System werden die Nachrichten einfach verschickt, egal ob sich
jemand als Empfänger angemeldet hat. Jeder angemeldete Empfänger erhält jede Nachricht,
und wenn eine Nachricht nicht konsumiert wird, dann ist dies unerheblich.




Abbildung 91: Anmelde-Versende-System

Im Gegensatz zu anderen Bean-Klassen muß eine Message-Driven-Bean zwingend eine be-
stimmte Schnittstelle, nämlich javax.jms.MessageListener, implementieren. Diese fun-
giert im Prinzip wie ein lokales Business-Interface.
package javax.jms;

public interface MessageListener {
  void onMessage(Message message);
}

Code 124: Interface MessageListener

Wenn aufgrund der angegebenen Aktivierungskonfiguration eine Nachricht eintrifft wird die
Methode onMessage() mit einem Objekt der Klasse javax.jmx.Message aufgerufen.
Message-Driven-Beans werden durch die Annotation @MessageDriven gekennzeichnet. Bei
dieser Annotation wird durch den Parameter activationConfig angegeben, wann eine In-
stanz einer solchen Bean-Klasse aktiviert werden soll.

                                            - 203 -
                              Java-Enterprise - Enterprise JavaBeans

package de.fhr.vs_iw.ejb;

@MessageDriven(activationConfig = {
    @ActivationConfigProperty(propertyName = "destinationType",
        propertyValue = "javax.jms.Queue"),
    @ActivationConfigProperty(propertyName = "destination",
        propertyValue = "queue/vs_iw")
})
public class MessageBean implements MessageListener {
  public void onMessage(Message msg) {
    System.out.println("Nachricht erhalten: " + msg.toString());
  }
}

Code 125: Class MessageBean

Der Parameter activationConfig ist ein Array von @ActivationConfigProperty Annota-
tions. Jede dieser Annotations gibt eine Eigenschaft der Aktivierungskonfiguration an. Die
beiden erforderlichen Eigenschaften mit ihren möglichen Werten sind:

    •   destinationType  gibt die Art des Nachrichtensystems an
           o javax.jmx.Queue     Nachrichtenschlange
           o javax.jmx.Topic     Anmelde-Versende-System (Publish-Subscribe)

    •   destination      legt den Namen für das Ziel fest, beginnt mit einem Präfix
           o "queue/..."         bei einer Nachrichtenschlange
           o "topic/..."         bei einem Anmelde-Versende-System
Aufgrund dieser Angaben erstellt der JBoss eine Queue oder ein Topic, mit deren Hilfe die
Nachrichten weitergeleitet werden. Alternativ kann diese Konfiguration auch in einer separa-
ten XML-Datei erfolgen, damit sie von den Klassen der Anwendung unabhängig ist.
<server>
  <mbean code="org.jboss.mq.server.jmx.Queue"
         name="jboss.mq.destination:service=Queue,name=queue/vs_iw">
    <attribute name="JNDIName">queue/vs_iw</attribute>
    <depends optional-attribute-name="DestinationManager">
      jboss.mq:service=DestinationManager
    </depends>
  </mbean>
</server>

Resource 25: jmx-queue-service.xml

Diese Art der Konfiguration kann besonders bei einem Anmelde-Versende-System sinnvoll
sein, da hier auch oft vorkommt, daß weder der Sender noch der Empfänger einer Nachricht
als Java-Bean auf dem Server laufen, sondern jeweils als Client-Anwendungen (siehe 6.3.6.5)
realisiert werden, die den Server lediglich zur Kommunikation benutzen.
<server>
  <mbean code="org.jboss.mq.server.jmx.Topic"
         name="jboss.mq.destination:service=Topic,name=topic/vs_iw">
    <attribute name="JNDIName">topic/vs_iw</attribute>
    <depends optional-attribute-name="DestinationManager">
      jboss.mq:service=DestinationManager
    </depends>
  </mbean>
</server>

Resource 26: jms-topic-service.xml


                                             - 204 -
                           Java-Enterprise - Enterprise JavaBeans


6.3.4.2. JMX - Management-Beans

Management-Beans (MBeans) ermöglichen den Zugriff auf eine Anwendung die Java-
Management-Extension (JMX) [16]. Ein Management-Bean muß eine Management-
Schnittstelle implementieren, genauso wie jede Session-Bean ein Business-Interface imple-
mentieren muß. Im Prinzip kann jede Bean-Klasse als Management-Bean angegeben werden,
indem eine definierte Management-Schnittstelle hinzugefügt wird. Der Name einer Manage-
ment-Schnittstelle sollte normalerweise mit dem Suffix "...MBean" enden.

@Management
public interface MyManagementMBean {
  void create() throws Exception;
  void start() throws Exception;
  void stop();
  void destroy();

    ...

    <More Methods>
}

Code 126: Interface MyManagementMBean

Jede Management-Schnittstelle kann grundsätzlich aus beliebigen Methoden bestehen. Es gibt
aber die vier Basis-Methoden create(), start(), stop() und destroy() die zwar nicht
zwingend vorhanden sein müssen, aber wenn sie vorhanden sind, als Callback-Methoden
durch den Lebenszyklus der Management-Bean aufgerufen werden.




Abbildung 92: MBean-Lebenszyklus

Der Sinn darin besteht, daß z.B. die Nachrichtenverarbeitung einer Message-Driven-Bean von
außen gestoppt und wieder gestartet werden kann, auch wenn dieser Vorgang nicht zur eigent-
lichen Geschäftslogik einer Anwendung gehört, aber trotzdem möglich sein soll.
Beim JBoss ist es möglich die Methoden aller MBeans von der JMX-Console aus aufzurufen.
Man kann aber natürlich jederzeit eine eigene Verwaltungsanwendung entwickeln.




                                          - 205 -
                             Java-Enterprise - Enterprise JavaBeans


6.3.4.3. Service-Beans

Ein Service-Bean ist eigentlich keine Bean-Art im eigentlichen Sinne, sondern eine zusätzli-
che Eigenschaft für eine der anderen Bean-Klassen. Die Annotation @Service bei einer Bean-
Klasse bewirkt, daß diese Klasse als Singleton genau einmal instanziert wird, sofort nachdem
die Anwendung auf einem Server deployed bzw. gestartet wurde. Der Applikations-Server
sorgt dafür, daß alle Aufrufe von Methoden der Klasse durch diese eine Instanz ausgeführt
werden. Diese Annotation ist eine JBoss-spezifische Erweiterung und nicht Teil des EJB-
Standards.


6.3.4.4. TimerService

Der TimerService ist ein Dienst in der EJB-Spezifikation, der zu einer bestimmten Zeit den
Aufruf einer Methode einer Bean veranlaßt. Die Methode der Bean wird durch @Timeout an-
notiert und muß folgende Signatur haben.

   @Timeout
   public void timeoutHandler(Timer timer);

Wenn ein Timer abgelaufen ist wird diese Methode vom Container aufgerufen und das abge-
laufene Timer-Objekt als Parameter übergeben.

Erstellt wird ein Timer-Objekt durch die create()-Methoden einer TimerService-Instanz.

package javax.ejb;

public interface TimerService {
  Timer createTimer(Date initialExpiration, long intervalDuration,
    Serializable info);
  Timer createTimer(Date expiration, Serializable info);
  Timer createTimer(long initialDuration, long intervalDuration,
    Serializable info);
  Timer createTimer(long duration, Serializable info);
  Collection getTimers();
}

Code 127: Interface TimerService

Zugriff auf den TimerService wiederum erhält man über den SessionContext. Dieser wird
wie bereits angesprochen als Resource vom Container injiziert (siehe 6.2.3.2).

   @Resource
   private SessionContext ctx;

   ctx.getTimerService().createTimer(interval, interval, "Hourly Backup");

Ein Timer wird vom Applikations-Server verwaltet und normalerweise auch persistent ge-
speichert, damit er bei einem Neustart des Servers erhalten bleibt. Beim JBoss wird das Ver-
halten des TimerService in der Datei ejb-timer-service.xml konfiguriert.




                                            - 206 -
                              Java-Enterprise - Enterprise JavaBeans


6.3.4.5. Management-Service-Bean mit Timer-Service

Die angesprochenen Möglichkeiten für JavaBeans werden nun in einem Beispiel umgesetzt.
Der praktische Nutzen für die Buchhaltungsanwendung könnte z.B. die zeitgesteuerte Aus-
führung eines Backup-Vorgangs sein.
Da dieser Vorgang von der der Geschäftslogik her nicht erforderlich ist, und auch von außen
nicht aufgerufen werden soll, wird ein in einem MBean mit der folgenden Management-
Schnittstelle BackupTimerMBean implementiert.

package de.fhr.vs_iw.ejb;

@Management
public interface BackupTimerMBean {
  void create() throws Exception;
  void destroy();
}

Code 128: Interface BackupTimerMBean

Es sind hier nur die Methoden create() und destroy() jeweils zum Starten bzw. Stoppen
des Timers notwendig, wenn eine MBean-Instanz erstellt bzw. entfernt wird. Die Schnittstelle
bzw. die beiden Methoden werden in der Klasse BackupTimer implementiert.

package de.fhr.vs_iw.ejb;

@Service
@Management(BackupTimerMBean.class)
public final class BackupTimer implements BackupTimerMBean {
  @Resource
  private SessionContext ctx;

    private Timer theTimer = null;

    public BackupTimer() {}

    public void create() throws Exception {
      if (theTimer == null) {
        theTimer = ctx.getTimerService().createTimer(3600000, 3600000, "xx");
      }
    }
    public void destroy() {
      if (theTimer != null) {
        theTimer.cancel();
      }
      theTimer = null;
    }

    @Timeout
    public void timeoutHandler(Timer timer) {
      if (timer == theTimer) {
        System.out.println("Received Timer event: " + timer.getInfo());
      } else {
        timer.cancel();
      }
    }
}

Code 129: Class BackupTimer

                                             - 207 -
                             Java-Enterprise - Enterprise JavaBeans

Die Klasse erhält den SessionContext durch Dependency-Injection und kann damit in der
create()-Methode der Bean-Klasse einen Timer erstellen, der alle Stunde (3600000 Sekun-
den) abläuft. Dadurch wird die Methode timeoutHandler aufgerufen, die dann das angespro-
chene Backup der Buchhaltungsdaten durchführen könnte.
Damit die create()-Methode überhaupt aufgerufen wird, muß das Bean instanziert werden,
was aber normalerweise nur geschieht, wenn ein Aufruf der Bean-Klasse erfolgt, also wenn
irgendwie von außen (lokal oder remote) zugegriffen wird. Dieses Problem wird durch die
@Service Annotation gelöst, die ja die einmalige Instanzierung einer Bean-Klasse beim de-
ployen bzw. starten der Anwendung bewirkt. Außerdem wird dadurch der Timer auch sicher
nur einmal gestartet.
Durch den Aufruf von cancel() in der destroy()-Methode wird der Timer beendet und ge-
löscht, sobald die MBean-Instanz vom Server entfernt wird.
Die Methoden der Klasse BackupTimer müssen aber die Timer-Instanz bei jedem Aufruf ü-
berprüfen, weil der JBoss-Application-Server in der vorliegenden Version ein Problem bei der
gemeinsamen Verwendung der Annotations @Management und @Service hat, das bewirkt,
daß die Methoden der Management-Schnittstelle immer zweimal aufgerufen werden.


6.3.5. Deployment

Alle notwendigen Klassen und Dateien müssen nun noch in Archive verpackt und auf dem
Applikations-Server zur Verfügung gestellt werden. Bei einer Enterprise-JavaBeans-
Anwendung sollte nun nicht ein einfaches JAR-Archiv, das einfach nur alle Klassen und Da-
teien enthält, sondern ein Enterprise-Archiv (EAR) verwendet werden.
Außerdem ist hier anzumerken, daß der JBoss-Application-Server, zumindest in der vorlie-
genden Version 4.0.5.GA, ein Problem mit Enterprise-JavaBeans in einfachen JAR-Archiven
hat. Das Redeployment eines solchen Archivs schlägt immer fehl und der JBoss-Server-
Prozeß muß neu gestartet werden.


6.3.5.1. Enterprise-Archiv

Ein Enterprise-Archiv faßt andere Archive mit den einzelnen unterschiedlichen Komponenten
einer Anwendung zusammen. Dazu muß das Enterprise-Archiv, neben den untergeordneten
Archiven selbst, einen Deployment-Descriptor META-INF/application.xml enthalten.

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://java.sun.com/xml/ns/j2ee" version="1.4"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com /xml/ns/j2ee
                      http://java.sun.com/xml/ns/j2ee/application_1_4.xsd">
  <display-name>EJBAccountingService</display-name>
  <module>
    <ejb>sessionbeans.ejb3</ejb>
  </module>
  <module>
    <java>persistence.par</java>
  </module>
  <module>
    <java>model.jar</java>
  </module>
</application>

Resource 27: application.xml (EJB)


                                            - 208 -
                            Java-Enterprise - Enterprise JavaBeans

In diesem Deployment-Descriptor werden die einzelnen Module der Anwendung, die der Ap-
plikations-Server entpacken und laden muß, je nach ihrem Typ (<ejb>, <java>, <web>)
beschrieben.


6.3.5.2. Archiv für Session-Beans

Die Session-Beans der Buchhaltung (EJBccountingService, BackupTimer, Message-
Bean) werden mit einem Ant-Vorgang in ein EJB3-Archiv verpackt. Nicht alle diese Bean-
Klassen stellen Session-Beans dar, weshalb es bei genauer Betrachtung so ist, daß eigentlich
alle Nicht-Entity-Beans zusammen in ein Archiv verpackt werden.

<target name="ejb3">
  <delete file="${basedir}/temp/sessionbeans.ejb3" />
  <jar destfile="${basedir}/temp/sessionbeans.ejb3">
    <fileset dir="${basedir}/bin/">
      <include name="**/ejb/EJBAccountingService.class" />
      <include name="**/ejb/BackupTimer.class" />
      <include name="**/ejb/BackupTimerMBean.class" />
      <include name="**/ejb/MessageBean.class" />
    </fileset>
  </jar>
</target>

Resource 28: Ant-Vorgang für EJB3-Archiv

Wichtig dabei ist, daß die Endung des Archivs wirklich exakt ejb3 ist, damit die Bean-
Klassen durch den JBoss-Application-Server richtig verarbeitet werden. Dafür ist nach dem
EJB3-Standard kein zusätzlicher XML-Deployment-Descriptor notwendig, da die Annotati-
ons der Klassen verwendet werden und ausreichen müssen.


6.3.5.3. Archiv für Entitiy-Beans

Auch die Entity-Beans (Account, AccountingEntry) werden durch einen Ant-Vorgang in
ihrem eigenen Archiv mit der Endung PAR (Persistence-Archive) verpackt.

<target name="par">
  <delete file="${basedir}/temp/persistence.par" />
  <jar destfile="${basedir}/temp/persistence.par">
    <fileset dir="${basedir}/bin/">
      <include name="**/ejb/Account.class" />
      <include name="**/ejb/AccountingEntry.class" />
    </fileset>
    <metainf dir="${basedir}/resources/ejb">
      <include name="persistence.xml" />
    </metainf>
  </jar>
</target>

Resource 29: Ant-Vorgang für PAR-Archiv

Als zusätzlicher Deployment-Descriptor ist die XML-Datei persistence.xml notwendig,
welcher die Verwaltung der Entity-Beans durch den Applikations-Server definiert.



                                           - 209 -
                             Java-Enterprise - Enterprise JavaBeans

<?xml version="1.0" encoding="UTF-8"?>
<persistence>
  <persistence-unit name="EJBAccountingServiceDS">
    <jta-data-source>java:/MySqlDS</jta-data-source>
    <properties>
      <property name="hibernate.hbm2ddl.auto" value="update" />
      <property name="hibernate.dialect"
                 value="org.hibernate.dialect.MySQLDialect" />
    </properties>
  </persistence-unit>
</persistence>

Resource 30: Deployment-Descriptor persistence.xml

Hier wird der Name der Persistence-Unit definiert, auf den der @PersistenceContext für
den in den Bean-Klassen verwendeten EntityManager verweist (siehe 6.3.2.6). Mit den wei-
teren Angaben wird das Verhalten der Instanz des EntityManagers konfiguriert. Der Name
einer definierten Datenquelle, sowie einige zusätzliche Parameter für Hibernate werden ange-
geben.


6.3.5.4. Archiv für weitere Klassen

Alle weiteren Klassen, die für die Anwendung notwendig sind, werden durch einen weiteren
Ant-Vorgang in einem separaten JAR-Archiv aufgenommen.

<target name="jar">
  <delete file="${basedir}/temp/model.jar" />
  <jar destfile="${basedir}/temp/model.jar">
    <fileset dir="${basedir}/bin/">
      <include name="**/model/*.class" />
    </fileset>
  </jar>
</target>

Resource 31: Ant-Vorgang für JAR-Archiv

Konkret sind das hier die Schnittstellen und Klassen aus dem Buchhaltungsmodell
(de.fhr.vs_iw.model), auf denen die Bean-Klassen basieren.


6.3.5.5. Gemeinsame Ant-Build-Datei

Die verschiedenen Vorgänge zur Erstellung der Teil-Archive können in einer gemeinsamen
Ant-Build-Datei zusammengefaßt werden.

<?xml version="1.0" encoding="UTF-8"?>
<project name="EJBAccountingService" default="package-ear" basedir="..">
  <property file="${basedir}/ant/build.properties" />

  <target name="ejb3">
    ...
  </target>

  <target name="par">
    ...
  </target>

                                              - 210 -
                             Java-Enterprise - Enterprise JavaBeans


  <target name="jar">
    ...
  </target>

  <target name="package-ear" depends="ejb3, par, jar">
    <delete file="${basedir}/temp/${ant.project.name}.ear" />
    <ear destfile="${basedir}/temp/${ant.project.name}.ear"
      appxml="${basedir}/resources/ejb/application.xml">
      <fileset dir="${basedir}/temp/">
        <include name="sessionbeans.ejb3" />
        <include name="persistence.par" />
        <include name="model.jar" />
      </fileset>
      <metainf dir="${basedir}/resources/ejb/">
        <include name="jms-topic-service.xml" />
      </metainf>
    </ear>
    <copy file="${basedir}/temp/${ant.project.name}.ear"
          todir="${jboss.deploy}" overwrite="true" />
  </target>
</project>

Resource 32: Ant-Build-Datei ejb-deploy.xml

Mit den Teil-Archiven und dem Deployment-Descriptor application.xml wird in einem
weiteren Ant-Vorgang, der von den o.g. Vorgängen abhängig ist, das Enterprise-Archiv für
die Buchhaltung erstellt.
Im META-INF Unterverzeichnis innerhalb des Archivs wird die Konfigurationsdatei für das
Anmelde-Versende-System (jms-topic-service.xml, siehe 6.3.4.1) mit aufgenommen,
damit der JBoss das entsprechende Topic anlegen kann.
Nach dem Erstellen wird das Enterprise-Archiv in das Deploy-Verzeichnis des JBoss-
Application-Servers kopiert und so die Anwendung zur Verfügung gestellt und gestartet.




                                              - 211 -
                                Java-Enterprise - Enterprise JavaBeans


6.3.5.6. Einstellungen

Zur Konfiguration aller verwendeten Ant-Build-Dateien dient die Datei build.properties.

# Projekt-Eigenschaften
version = 2.0
manifest = ${basedir}/temp/MANIFEST.MF
developer = Florian Lutz

# Externe Pfade
jdk.home = C:/Program Files/Java/jdk1.5.0_12
jboss.home = D:/daemon/jboss-4.0.5.GA
jwsdp.home = D:/classpath/jwsdp-2.0
axis.home = D:/classpath/axis-1.4
hibernate.home = D:/classpath/hibernate-3.2
jacorb.home = D:/classpath/JacORB_2.3.0_beta2

# Modul-Eigenschaften
jboss.deploy = ${jboss.home}/server/default/deploy/
webservice.path = de/fhr/vs_iw/webservice

# Sonstiges
sun.javadoc = http://java.sun.com/j2se/1.5.0/docs/api/

Resource 33: build.properties

Darin werden vor allem Pfade zu Ressourcen eingestellt, die sich außerhalb des Eclipse-
Projekt-Verzeichnisses befinden, wie z.B. der Deploy-Pfad für den JBoss-Application-Server,
und noch weitere Angaben.




                                               - 212 -
                           Java-Enterprise - Enterprise JavaBeans



6.3.6. Client-Anwendung

Nun ist die Buchhaltung als Java-Enterprise-Anwendung auf dem JBoss-Application-Server
verfügbar. Was fehlt ist eine Client-Anwendung, die auf den Server zugreift.


6.3.6.1. Einbinden der nötigen Bibliotheken

Für die Clientseite einer Enterprise-Anwendung liefert der JBoss-Application-Server eine
Reihe von Bibliotheken mit. Diese Bibliotheken befinden sich alle im Verzeichnis

     client/

einer JBoss-Installation, und sind im Prinzip die Gleichen, wie auch im lib- oder deploy-
Verzeichnis, nur daß sie im client-Verzeichnis noch mal separat zusammengefaßt wurden.
Alle diese Client-Bibliotheken müssen in den CLASSPATH der Anwendung aufgenommen wer-
den.
Dieser Satz von Bibliotheken stellt den bereits erwähnten Application-Client-Container
(AAC) einer Java-Enterprise-Anwendung dar.


6.3.6.2. Aufruf der Anwendung auf dem Server

Die Anwendung arbeitet auf der Clientseite eigentlich direkt mit einer Session-Bean-Instanz
der Anwendung (IExtendedAccountingService) auf dem Server bzw. einem Stellvertreter-
Objekt, welches das entsprechende entfernte Business-Interface der Bean implementiert und
für die Verbindung zum Applikations-Server sorgt.
Die Proxy-Klasse der Enterprise-Buchhaltung muß daher nur auf die Service-Session-Bean
auf dem Server zugreifen. Die Benutzeroberfläche kann dann die Methoden der Session-Bean,
die ja von den bekannten Buchhaltungsschnittsstellen stammen, direkt verwenden.

package de.fhr.vs_iw.ejb;

public final class EJBProxy implements IAccountingProxy {

    public EJBProxy() {}

    public void close() {}

    public IAccountingService connect(String[] args) throws IOException {
      try {
        InitialContext ctx = new InitialContext();
        return (IAccountingService)ctx.lookup(JNDI_NAME);
      } catch (Exception e) {
        IOException ioe = new IOException(e.getMessage());
        ioe.initCause(e);
        throw ioe;
      }
    }

    private static final String JNDI_NAME
      = "EJBAccountingService/EJBAccountingService/remote";
}

Code 130: Class EJBProxy

                                          - 213 -
                            Java-Enterprise - Enterprise JavaBeans

Der Zugriff auf den Applikations-Server erfolgt über den InitialContext. Man erhält
Zugriff auf die gewünschte Session-Bean oder ein anderes Objekt auf dem Server durch die
lookup()-Methode unter Verwendung des zugehörigen JNDI-Namens. Den richtigen JNDI-
Namen erhält man entweder durch die entsprechende Kenntnis des Aufbaus und der Konfigu-
ration einer Anwendung oder aber einfach über die JNDI-Übersicht (siehe 6.2.5.2) des JBoss-
Application-Servers.
Zur Auflösung des Namens wird der JNDI-Namensdienst verwendet. Die Datei
jndi.properties (siehe 6.3.6.4) wird vom InitialContext automatisch gesucht und gela-
den, um die notwendigen Parameter zum Zugriff auf den JNDI-Namensdienst zu erhalten.


6.3.6.3. Senden einer Nachricht an die MessageBean

Auch wenn die eingeführte Message-Driven-Bean (siehe 6.3.4.1) mit der Geschäftslogik der
Buchhaltungsanwendung nichts zu tun hat, und eigentlich gar nicht benötigt wird, soll an ei-
nem einfachen Beispiel demonstriert werden, wie eine Nachricht gesendet wird, die einen
Aufruf der Bean bewirkt.

package de.fhr.vs_iw.ejb;

public final class MessageClient {

    private MessageClient() {}

    private static final String QUEUE_NAME = "queue/vs_iw";

    public static void main(String[] args) throws Exception {
      InitialContext ctx = new InitialContext();
      Queue queue = (Queue)ctx.lookup(QUEUE_NAME);

        QueueConnectionFactory factory
          = (QueueConnectionFactory)ctx.lookup("QueueConnectionFactory");
        QueueConnection con = (QueueConnection)factory.createConnection();
        QueueSession session = con.createQueueSession(true,
            Session.SESSION_TRANSACTED);

        Message m = session.createMessage();
        m.setStringProperty("bla", "blubb");
        m.setStringProperty("foo", "bar");

        QueueSender sender = session.createSender(queue);
        sender.send(queue, m);
        session.commit();

        sender.close();
        session.close();
        con.close();
    }
}

Code 131: Class MessageClient

Auch hier wird über den InitialContext mit dem JNDI-Namen die Nachrichtenschlange
aufgerufen, welche für die MessageBean definiert wurde. Anschließend muß eine Verbindung
zu dieser Nachrichtenschlange hergestellt werden. Dazu wird, ebenfalls über JNDI, eine ent-
sprechende Verbindungs-Fabrik (QueueConnectionFactory) vom Server abgerufen, mit der


                                           - 214 -
                                Java-Enterprise - Enterprise JavaBeans

dann eine Verbindung (QueueConnection) erstellt wird. Da auch das Messaging transakti-
onsbasiert abläuft, muß eine Sitzung (QueueSession) eröffnet werden.
Über ein Message-Objekt wird nun die Nachricht erzeugt, indem verschiedene Parameter,
Werte und JMS-Daten angegeben werden.
Anschließend kann über einen QueueSender für die Sitzung eine Nachricht an die Nachrich-
tenschlange verschickt werden. Durch commit() erfolgt dann der transaktionsorientierte Ver-
sand. Schließlich werden der Sender, die Sitzung und die Verbindung zur Nachrichtenschlan-
ge noch geschlossen.


6.3.6.4. Allgemeine Client-Einstellungen

Neben den Java-Klassen sind noch einige weitere Dateien mit Einstellungen für die Anwen-
dungen notwendig.

In der Datei jndi.properties werden Einstellungen zum Zugriff auf den JNDI-
Namensdienst des JBoss-Application-Servers vorgenommen. Wenn die Datei im CLASSPATH
vorhanden ist, wird sie vom InitialContext automatisch geladen und verarbeitet.

java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
java.naming.provider.url=127.0.0.1:1099

Resource 34: jndi.properties

In der Datei wird im wesentlichen die URL eingestellt, unter der der JNDI-Namensdienst er-
reichbar ist. Dies ist der Host des JBoss-Application-Servers (hier 127.0.0.1 - localhost) und
der Port des Dienstes (standardmäßig 1099).

Die Datei log4j.properties wird von den Klassen des Apache Logging Service Project
Log4j verwendet. Dieses Logging-Framework wird von den JBoss-Klassen sowohl auf der
Server- als auch auf der Client-Seite verwendet.

log4j.rootLogger=debug, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE}%5p%c{1}:%L-%m%n

Resource 35: log4j.properties

In der Datei werden Logger-Objekte definiert, und auch auf welche Weise die Ausgaben er-
folgen sollen, in welchem Format und mit welchem Ziel. Mit den obigen Angaben erfolgen
alle Ausgaben aller Log-Level kontinuierlich auf die Konsole in folgendem Format:

    02:34:14,556 DEBUG SocketManager:499 - Begin WriteTask.run

Für eine genaue Übersicht über die Konfgurationsmöglichkeiten des Logging-Frameworks
muß die zugehörige Dokumentation herangezogen werden [76].




                                               - 215 -
                              Java-Enterprise - Web-Services


6.4. Web-Services
6.4.1. Allgemeines

Beim JBoss in der Version 4.0.5.GA sind die JBoss-Web-Services-1.0.3 bereits integriert [21]
[22] [23]. Auf der Serverseite enthält das Archiv

   server/default/deploy/jbossws.sar

die nötigen Klassen und Dateien, so daß ein Web-Service ohne weitere Vorbereitung auf dem
Server deployed und gestartet werden kann. Für Web-Service-Clients müssen die Bibliothek

   client/jbossws-client.jar

und einige weitere dafür grundlegende Bibliotheken eingebunden werden. Die Implementie-
rung der JBoss-Web-Services entspricht dem WS-I Basic-Profile [24] und stellt drei Arten
von Endpunkten zur Verfügung:

   •   RPC/literal
   •   Document/literal
   •   Message style

In der ursprünglichen Web-Services-Spezifikation gibt es dann noch die Arten:

   •   RPC/encoded
   •   Document/encoded

die von JBoss allerdings nicht unterstützt werden, da sie nicht WS-I konform sind. Anhand
der WS-I Spezifikation wird ein Web-Service schließlich in einer WDSL-Datei definiert, die
die verfügbaren Methoden des Dienstes und die Art ihres Aufrufs als XML-Nachrichten be-
schreibt. Da dieses Format frei spezifiziert und unabhängig ist, sind Web-Services, die auf
derselben WSDL-Beschreibung beruhen, im Prinzip System- und Plattformunabhängig. (Java,
C++, VB, C#, .NET). Deshalb gibt es für viele Programmiersprachen Tools, die aus einer
WSDL-Datei die nötigen Artefakte erzeugen, um den beschriebenen Web-Service anzubieten
bzw. darauf zuzugreifen.
Zur Übertragung der Daten kommt bei den Web-Services normalerweise das auf XML basie-
rende SOAP-Protokoll zum Einsatz [26]. SOAP definiert ein Header-Element, das zusätzliche
Informationen beinhalten kann, und ein Body-Element, das dann die entsprechende WSDL-
konforme Nachricht enthält.

Ursprünglich wurde für die Web-Services im JBoss das AXIS-Framework [32] der Apache-
Foundation [05] verwendet. Bei der Version 4 haben die Entwickler begonnen, basierend auf
AXIS-1.1, die Web-Service-Funktionalitäten selbst zu implementieren. In der vorliegenden
Version 4.0.5 ist diese Entwicklung größtenteils abgeschlossen und alle Teile, die von AXIS
stammen, wurden ersetzt. Allerdings gibt es noch Defizite bei den JBoss-Web-Service-Tools
(siehe 6.4.3.1) für die automatisierte Erstellung von Web-Service-Clients, dort scheint die
Implementierung noch nicht vollständig zu sein.




                                          - 216 -
                                 Java-Enterprise - Web-Services


6.4.1.1. RPC-Style vs. Document-Style

Diese beiden Arten eines Web-Services unterscheiden sich in der spezifizierten
WSDL-Struktur [25]. Bei RPC ist der Name der aufgerufenen Methode als XML-Element im
XML-Baum der SOAP-Nachricht enthalten, bei Document nicht.
Der Hintergrund dafür ist die Idee, daß beim RPC-Style, wie bei RPC allgemein üblich, nur ein
einzelner Methodenaufruf ausgeführt, und dessen Rückgabewert als Antwort zurückgeliefert
werden soll.
Der Document-Style zielt auf den vereinbarten Austausch von komplexen Daten-Dokumenten
im XML-Format bei einer aufwendigen Geschäftslogik ab. Dabei ist der Name der aufgerufe-
nen Methode weniger wichtig, da nur die Verarbeitung des Anfragedokuments an sich von
Belang ist. Und auch bei der Antwort wird ein aufwendigeres Dokument erzeugt, daß eine
festgelegte Struktur hat und sich nicht mehr auf den ursprünglich aufgerufenen Methodenna-
men beziehen muß.

RPC/literal:                                       Document/literal:

<soap:envelope>
  <soap:body>                                      <soap:envelope>
    <myMethod>                                       <soap:body>
      <x>5</x>                                         <xElement>5</xElement>
      <y>5.0</y>                                       <yElement>5.0</yElement>
    </myMethod>                                      </soap:body>
  </soap:body>                                     </soap:envelope>
</soap:envelope>

Tabelle 18: Vergleich von SOAP-Nachrichten für RPC-Style und Document-Style

Dies führt auch dazu, daß der Document-Style vollkommen WSDL- bzw. SOAP-Schema kon-
form ist, und sehr leicht mit XML-Tools validiert werden kann, was bei RPC-Style schwierig
ist, da die Namen der aufgerufenen Methoden vom Benutzer bestimmt werden.


6.4.1.2. literal vs. encoded

Der Unterschied zwischen den Unterarten literal und encoded besteht darin, daß bei
literal keine Informationen über den Datentyp von Parametern und Rückgabewerten im
SOAP-Protokoll mitübertragen wird.

literal:                                 encoded:

<soap:envelope>                          <soap:envelope>
  <soap:body>                              <soap:body>
    <myMethod>                               <myMethod>
      <x>5</x>                                 <x xsi:type="xsd:int">5</x>
      <y>5.0</y>                               <y xsi:type="xsd:float">5.0</y>
    </myMethod>                              </myMethod>
  </soap:body>                             </soap:body>
</soap:envelope>                         </soap:envelope>

Tabelle 19: Vergleich von SOAP-Nachrichten für literal und encoded

Die Datentyp-Information ist normal nur Overhead, der die Performance verringert [25].


                                               - 217 -
                                Java-Enterprise - Web-Services



6.4.2. Die Buchhaltung als Web-Service-RPC-Endpunkt

Ein Web-Service wird in Java durch eine Endpunkt-Schnittstelle (SEI - Service Endpoint In-
terface) definiert. Da ein Web-Service bzw. SOAP keine verteilten Objekte unterstützt, muß
hier von den bisher verwendeten bekannten Schnittstellen mit ihrer Objekthierarchie etwas
Abstand genommen, und eine neue "flache" Schnittstelle definiert werden, die sich aber voll-
ständig an den Methoden der bekannten Schnittstellen orientiert.


6.4.2.1. Service-Schnittstelle

Die Methoden der Service-Schnittstelle können an sich unverändert bleiben, jedoch die bei-
den Factory-Methoden getAccountByNumber() und getAccountingEntryById(), die bis-
her die entsprechenden Objekte lieferten, entfallen. Dadurch ergibt sich folgende Schnittstel-
le:

package de.fhr.vs_iw.webservice;

@WebService
public interface WebAccounting extends Remote {
  Double calculateBalance(Integer number)
      throws RemoteException, IOException, AccountingException;
  void createAccount(Integer number, int type, String description)
      throws RemoteException, IOException, AccountingException;
  Integer createAccountingEntry(Integer debit, Integer credit,
      Double amount, String text)
      throws RemoteException, IOException, AccountingException;
  Integer[] getAccountingEntries()
      throws RemoteException, IOException;
  Integer[] getAccounts()
      throws RemoteException, IOException;
  void removeAccount(Integer number)
      throws RemoteException, IOException, AccountingException;
  void removeAccountingEntry(Integer id)
      throws RemoteException, IOException, AccountingException;

    ...
}

Code 132: Interface WebAccounting

Weitere Abweichungen ergeben sich durch die drei Grundregeln für zulässige Web-Service-
Endpunkt-Schnittstellen bei Java:

    •     Die Schnittstelle muß von java.rmi.Remote abgeleitet sein
    •     Alle Methoden müssen mit der throws-Angabe java.rmi.RemoteException verse-
          hen werden, wobei Superklassen davon nicht zulässig sind
    •     Die Typen der Parameter und Rückgabewerte sind auf jene Typen beschränkt, welche
          in der JAX-RPC-1.1 Spezifikation angegeben sind, wobei keine der Parameter-
          Klassen in irgendeiner Weise von java.rmi.Remote abgeleitet sein darf

Schließlich wird die Schnittstelle noch mit der Annotation @WebService gekennzeichnet, wo-
durch alle Methoden später zu Methoden des Web-Services werden.


                                           - 218 -
                               Java-Enterprise - Web-Services


6.4.2.2. Konto-Schnittstelle

Wie oben erwähnt ist für Web-Services eine "flache" Schnittstelle erforderlich. Deshalb wer-
den die Methoden der Konto-Schnittstelle in die WebAccounting-Schnittstelle integriert.
public interface WebAccounting extends Remote {
  ...

    Integer[] getCreditEntries(Integer number)
        throws RemoteException, IOException;
    Integer[] getDebitEntries(Integer number)
        throws RemoteException, IOException;
    String getDescription(Integer number)
        throws RemoteException, IOException;
    int getType(Integer number)
        throws RemoteException, IOException;
    void setDescription(Integer number, String description)
        throws RemoteException, IOException;
    void setType(Integer number, int type)
        throws RemoteException, IOException;
}

Code 133: Konto-Methoden für Interface WebAccounting

Sie müssen lediglich in der Art abgeändert werden, daß jede einzelne Methode die Nummer
des gewünschten Kontos als Parameter übernimmt und die Grundregeln für Endpunkt-
Schnittstellen beachtet.


6.4.2.3. Buchungs-Schnittstelle

Mit der Buchungs-Schnittstelle wird auf die gleiche Weise verfahren, wie mit der Konto-
Schnittstelle. Die Methoden werden um die Buchungs-ID als Parameter erweitert und in die
WebAccounting-Schnittstelle integriert.

public interface WebAccounting extends Remote {
  ...

    Double getAmount(Integer id)
        throws RemoteException, IOException;
    Integer getCreditAccount(Integer id)
        throws RemoteException, IOException;
    Integer getDebitAccount(Integer id)
        throws RemoteException, IOException;
    String getText(Integer id)
        throws RemoteException, IOException;
    void setAmount(Integer id, Double amount)
        throws RemoteException, IOException;
    void setCreditAccount(Integer id, Integer number)
        throws RemoteException, IOException, AccountingException;
    void setDebitAccount(Integer id, Integer number)
        throws RemoteException, IOException, AccountingException;
    void setText(Integer id, String text)
        throws RemoteException, IOException;
}

Code 134: Buchungs-Methoden für Interface WebAccounting


                                            - 219 -
                               Java-Enterprise - Web-Services


6.4.2.4. Implementierung

Das so entstandene Interface muß nun in einer Klasse implementiert werden. Damit die Ge-
schäftslogik nicht noch mal neu programmiert werden muß, soll der Web-Service auf die be-
reits vorhandenen Bean-Klassen der Buchhaltung zurückgreifen. Dazu wird im Konstruktor
der Klasse das Service-Bean über seinen JNDI-Namen referenziert und alle Methodenaufrufe
werden lediglich weitergegeben.

package de.fhr.vs_iw.webservice;

@WebService(
  name = "WebAccounting",
  serviceName = "WebAccounting",
  targetNamespace = "http://www.fh-regensburg.de/vs_iw/webservice",
  endpointInterface = "de.fhr.vs_iw.webservice.WebAccounting")
@SOAPBinding(style = SOAPBinding.Style.RPC)
public class WebAccountingImpl implements WebAccounting {

    private final IExtendedAccountingService ejbservice;

    private final static String JNDI_NAME
      = "EJBAccountingService/EJBAccountingService/remote";

    public WebAccountingImpl() throws NamingException {
      ejbservice = (IExtendedAccountingService)
        new InitialContext().lookup(JNDI_NAME);
    }

    ...

    public Double calculateBalance(Integer number)
        throws AccountingException, IOException {
      return ejbservice.calculateBalance(number);
    }

    public Integer[] getAccounts() throws IOException {
      final Set<Integer> set = ejbservice.getAccounts();
      return set.toArray(new Integer[0]);
    }

    ...

    public Double getAmount(Integer id) throws IOException {
      try {
        IAccountingEntry entry = ejbservice.getAccountingEntryById(id);
        return entry.getAmount();
      } catch (AccountingException e) {
        throw new RuntimeException(e);
      }
    }

    ...

    <Service-Methods>
    <Account-Methods>
    <AccountingEntry-Methods>
}

Code 135: Class WebAccountingImpl


                                          - 220 -
                                Java-Enterprise - Web-Services

Der Aufruf der einzelnen Konten- und Buchungsobjekte erfolgt innerhalb der Methoden, und
es wird nur der gewünschte Wert zurückgegeben. Weiterhin wird der Web-Service mit den
entsprechenden Annotations beschrieben. Die Klasse selbst erhält die Annotation
@WebService, mit zusätzlichen Angaben für die Bezeichnung des Services und des ge-
wünschten XML-Namespace. Hier wird das implementierte Interface noch mal explizit als
Endpunkt-Schnittstelle angegeben und der Typ des Web-Service wird mit der Annotation
@SOAPBinding für die SOAP-Nachrichten auf RPC gesetzt.

Eine Tücke beim JBoss besteht allerdings darin, daß der angegebene targetNamespace nicht
verwendet wird. Der XML-Namespace für den Web-Service wird aus dem Java-Paket-Namen
generiert und lautet dann in diesem Fall:

   http://webservice.vs_iw.fhr.de/jaws


6.4.2.5. Deployment

Der Web-Service wird wie eine Web-Anwendung in ein WAR-Archiv verpackt und vom
JBoss wie ein Servlet behandelt. Dazu ist folgende web.xml Beschreibungsdatei nötig:

<?xml version="1.0" encoding="UTF-8" ?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
                        http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
    version="2.4">

  <servlet>
    <servlet-name>WebAccountingService</servlet-name>
    <servlet-class>
      de.fhr.vs_iw.webservice.WebAccountingService
    </servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>WebAccountingService</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>
</web-app>

Resource 36: Web-Service-Descriptor web.xml

Dadurch wird angegeben, daß der Web-Service, unter dem Namen bzw. der URL "WebAc-
countingService" verfügbar sein soll. Nun kann über eine Ant-Datei alles Nötige entsprechend
in ein Archiv verpackt werden.

<?xml version="1.0" encoding="UTF-8" ?>
<project name="WebAccountingService" default="package-war" basedir="..">
  <property file="${basedir}/ant/build.properties" />
  <target name="package-war">
    <war warfile="${basedir}/temp/${ant.project.name}.war"
         webxml="${basedir}/resources/webservice/web.xml">

       <classes dir="${basedir}/bin">
         <include name="**/webservice/WebAccounting.class" />
         <include name="**/webservice/WebAccountingImpl.class" />
       </classes>

                                              - 221 -
                                 Java-Enterprise - Web-Services


      <metainf dir="${basedir}/generated/de/fhr/vs_iw/webservice">
        <include name="*.xml" />
        <include name="wsdl/*.wsdl" />
      </metainf>
    </war>
    <copy file="${basedir}/temp/${ant.project.name}.war"
          todir="${jboss.deploy}" overwrite="true" />
  </target>
</project>

Resource 37: Ant-Build-Datei webservice-deploy.xml

Mit diesem Ant-Skript werden alle nötigen Klassen für den Web-Service, der referenzierten
EJBs und des Buchhaltungs-Modells, sowie die erzeugten XML-Beschreibungsdateien in
einer WAR-Datei WebAccountingSerivice.war zusammengeführt. Die Datei web.xml dient
als Deployment-Descriptor und wird in das WEB-INF-Verzeichnis des Archivs gepackt. Das
fertige Archiv wird in das Deploy-Verzeichnis des JBoss-Servers kopiert.

Allerdings bietet es sich an, den Web-Service mit den JavaBean-Klassen zusammen als En-
terprise-Archiv zu verpacken und Beides gemeinsam zu deployen. Dazu wird das Web-
Archiv, das durch den o.g. Ant-Vorgang package-war erzeugt wird, als Web-Modul im De-
ployment-Descriptor application.xml des Enterprise-Archivs eingetragen.

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://java.sun.com/xml/ns/j2ee" version="1.4"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com /xml/ns/j2ee
                      http://java.sun.com/xml/ns/j2ee/application_1_4.xsd">
  <display-name>EJBAccountingService</display-name>
  <module>
    <ejb>sessionbeans.ejb3</ejb>
  </module>
  <module>
    <java>persistence.par</java>
  </module>
  <module>
    <java>model.jar</java>
  </module>
  <module>
    <web>
      <web-uri>webservice.war</web-uri>
      <context-root>WebAccountingService</context-root>
    </web>
  </module>
</application>

Resource 38: application.xml (Web-Service)

Außerdem kann der Ant-Vorgang zur Erstellung des Web-Archivs in die Ant-Build-Datei für
das Enterprise-Archiv ejb-deploy.xml mit aufgenommen werden, damit alles in einem Vor-
gang richtig verarbeitet und deployed werden kann.




                                              - 222 -
                                 Java-Enterprise - Web-Services



6.4.3. JBoss-Web-Service-Tools

Aus der Java-Schnittstelle, die den Endpunkt des Web-Services darstellt, können alle weiteren
Artefakte (Beschreibungsdateien und Client-Klassen) programmmäßig erzeugt werden.

Dazu sind die JBoss-Web-Service-Tools erforderlich, die sich im JBoss-Verzeichnis im
Archiv

   client/jbossws-client.jar

befinden. Das Archiv enthält eine Batch-Datei wstools.bat, mit der die Web-Service-Tools
auf der Kommandozeile aufgerufen werden können. Dabei muß als man Argumente

   wstools.bat –dest <zielverzeichnis> -config <konfigurationsdatei>

ein Zielverzeichnis und eine Konfigurationsdatei angeben.


6.4.3.1. Verwendung mit Ant

Günstiger ist die Einbindung der Web-Service-Tools als Ant-Task, der die gleichen Argumen-
te erfordert. Der classpath mit den benötigten Bibliotheken muß definiert werden.

<?xml version="1.0" encoding="UTF-8" ?>
<project name="AccountingServiceWeb" default="generate" basedir="..">
  <property file="${basedir}/ant/build.properties" />
  <property name="ws.package" value="de/fhr/vs_iw/webservice" />
  <path id="classpath">
    <pathelement path="${basedir}/bin" />
    <pathelement path="${java.home}" />
    <pathelement path="${jboss.home}/lib/endorsed/xercesImpl.jar" />
    <fileset dir="${jboss.home}/client">
      <include name="jbossws-client.jar" />
      <include name="jbossall-client.jar" />
      <include name="jboss-xml-binding.jar" />
      <include name="activation.jar" />
      <include name="mail.jar" />
      <include name="log4j.jar" />
      <include name="javassist.jar" />
      <include name="jbossretro-rt.jar" />
      <include name="jboss-backport-concurrent.jar" />
    </fileset>
  </path>
  <taskdef name="wstools" classname="org.jboss.ws.tools.ant.wstools">
    <classpath refid="classpath" />
  </taskdef>
  <target name="generate">
    <wstools dest="${basedir}/generated/${ws.package}"
             config="${basedir}/src/${ws.package}/wstools-java2wsdl.xml" />
    <sleep seconds="3" />
    <wstools dest="${basedir}/generated/"
             config="${basedir}/src/${ws.package}/wstools-wsdl2java.xml" />
  </target>
</project>

Resource 39: Ant-Build-Datei webservice-generate.xml


                                              - 223 -
                                Java-Enterprise - Web-Services


6.4.3.2. WSDL-Datei

Die jeweils spezifizierte Konfigurationsdatei für die Web-Service-Tools gibt an, was genau,
mit welchen Parametern erzeugt werden soll. Zur Erzeugung der WSDL-Datei muß die Kon-
figuration ein <java-wsdl>-Element enthalten.

<?xml version="1.0" encoding="UTF-8" ?>
<configuration xmlns="http://www.jboss.org/jbossws-tools"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.jboss.org/jbossws-tools
           http://www.jboss.org/jbossws-tools/schema/jbossws-tool_1_0.xsd">

  <java-wsdl>
    <service name="WebAccountig" style="rpc"
             endpoint="de.fhr.vs_iw.webservice.WebAccounting"/>
    <namespaces
      target-namespace="http://www.fh-regensburg.de/vs_iw/webservice"
      type-namespace="http://www.fh-regensburg.de/vs_iw/webservice/types"/>
    <mapping file="jaxrpc-mapping.xml"/>
    <webservices servlet-link="WebAccountingService"/>
  </java-wsdl>
</configuration>

Resource 40: WSTools-Konfiguration wstools-java2wsdl.xml

Durch ein <service>-Element wird mit folgenden Attributen, der Web-Service angegeben:

   •   name     Die Bezeichnung des Web-Services
   •   style    Die Art des Services (rpc, document, bare)
   •   endpoint Die Java-Endpunkt-Schnittstelle

Das <namepaces>-Element legt die zu verwendenden XML-Namensräume fest:

   •   target-namespace        XML-Namensraum für die Methoden von diesem Web-Service
   •   type-namspace           Separater Namensraum für die verwendeten Parametertypen

Das Element <mapping-file> gibt an, ob und wo eine Datei mit den Datentyp-Zuordnungen
für JAX-RPC erzeugt werden soll. Schließlich wird noch mit dem Element <webservices>
und seinem Attribut servlet-link, der Name des Servlets angegeben, das den Web-Service
später zur Verfügung stellen soll.

In der erzeugten WSDL-Datei fehlt noch die zu diesem Web-Service passende SOAP-
Adresse, unter der Service später verfügbar ist. An der Stelle des Platzhalters
REPLACE_WITH_ACTUAL_URL wird die URL des entsprechenden Web-Service-Sevlets

   http://localhost:8080/WebAccountingService

unter JBoss, hier für den lokalen Rechner, eingetragen. Die vollständige WSDL-Datei für den
Buchhaltungs-Service ist im Anhang E zu finden.




                                             - 224 -
                                Java-Enterprise - Web-Services

Der JBoss-Server bietet aber auch die Funktionalität, das WSDL-Schema eines Web-Service,
der auf dem Server deployed wurde, direkt abzurufen. Dazu wird die Service-URL

   http://localhost:8080/WebAccountingService?wsdl

mit dem Parameter ?wsdl aufgerufen. Das generierte Schema enthält bereits die richtige
SOAP-Adresse und kann direkt, durch Angabe der URL, verwendet werden, was für einen
dynamischen Client besonders interessant ist.


6.4.3.3. Client-Artefakte

Die Client-Artefakte werden durch eine Konfiguration mit einem <wsdl-java>-Element aus
einer WSDL-Datei erzeugt. Dies kann die soeben generierte Datei sein, aber auch jede andere,
von einem anderen spezifizierten Web-Service.

<?xml version="1.0" encoding="UTF-8" ?>
<configuration xmlns="http://www.jboss.org/jbossws-tools"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.jboss.org/jbossws-tools
          http://www.jboss.org/jbossws-tools/schema/jbossws-tool_1_0.xsd">

  <global>
    <package-namespace package="de.fhr.vs_iw.webservice.client"
             namespace="http://www.fh-regensburg.de/vs_iw/webservice" />
  </global>

  <wsdl-java file="de/fhr/vs_iw/webservice/wsdl/WebAccountigService.wsdl">
  </wsdl-java>
</configuration>

Resource 41: WSTools-Konfiguration wstools-wsdl2java.xml

Hier wird in einem globalen Abschnitt eine zusätzliche Einstellung definiert. Durch ein
<package-namespace>-Element wird angegeben, daß ein bestimmter XML-Namensraum
einer bestimmten Java-Package zugeordnet werden soll, der die erzeugten Dateien dann ange-
hören.


6.4.3.4. Erfahrungen mit den JBoss-Web-Service-Tools

Leider hat sich in der Praxis gezeigt, daß die JBoss-Web-Service-Tools bzw. die Implemen-
tierung der generierten Client-Klassen nach dem JAX-RPC-Standard bei den JBoss-Web-
Services noch nicht vollständig genug für eine einfache und unkomplizierte Verwendung ist.
Die Arbeit damit gestaltet sich bei der vorliegenden Version 1.0.3 noch schwierig bis unmög-
lich, weshalb für die Java-Client-Seite des Buchhaltungs-Web-Service ein anderes Frame-
work zu Einsatz kommt.




                                             - 225 -
                                  Java-Enterprise - Web-Services



6.4.4. Web-Services mit AXIS

Das AXIS-Framework [32] ist eine Implementierung des SOAP-Protokolls [26] von der Apa-
che Foundation [05], war die erste ihrer Art, und hat sich in der Vergangenheit vielfach be-
währt. Deshalb wird die Implementierung der Client-Seite des Buchhaltungs-Web-Service
damit ausgeführt.


6.4.4.1. Installation

Um das AXIS-Framework auf der Clientseite verwenden zu können, ist lediglich der Down-
load des Pakets (aktuelle Version 1.4) von der Homepage der Apache-Foundation nötig.

    http://ws.apache.org/axis/java/releases.html

Die darin enthaltenen Bibliotheken aus dem lib-Unterverzeichnis müssen nach dem entpak-
ken im CLASSPATH aufgenommen werden.


6.4.4.2. Ant-Vorgang

In den AXIS-Bibliotheken ist ein Ant-Vorgang zum Erzeugen der Client-Artefakte aus der
Web-Service-Beschreibungsdatei vorhanden. Die Beschreibung wird allerdings nicht aus der
vorhandenen WSDL-Datei, sondern direkt von einer URL des laufenden JBoss-Servers bezo-
gen. Dies hat den Vorteil, daß die SOAP-Adresse bereits richtig eingetragen ist, und es wird
auch gleich der richtige XML-Namensraum verwendet, der ja vom JBoss eigenmächtig ver-
geben wird (siehe 6.4.2.4).
Dieser Namenraum (http://webservice.vs_iw.fhr.de/jaws) wird einem Java-Paket
(de.fhr.vs_iw.webservice.axis) zugeordnet, damit die später erzeugten Klassen richtig
eingeordnet werden.

<?xml version="1.0" standalone="yes"?>
<project basedir=".." default="generate-client">
  <property file="${basedir}/ant/build.properties" />
  <target name="generate-client">

     <path id="classpath">...</path>
     <taskdef resource="axis-tasks.properties" classpathref="classpath" />

     <axis-wsdl2java output="${basedir}/generated"
       testcase="true"
       verbose="true"
       url="http://localhost:8080/WebAccountingService?wsdl">
       <mapping namespace="http://www.fh-regensburg.de/vs_iw/webservice"
         package="de.fhr.vs_iw.webservice" />
       <mapping namespace="http://webservice.vs_iw.fhr.de/jaws"
         package="de.fhr.vs_iw.webservice.axis" />
     </axis-wsdl2java>

  </target>
</project>

Resource 42: Ant-Build-Datei axis-generate-client.xml



                                                - 226 -
                                 Java-Enterprise - Web-Services


6.4.4.3. Client-Artefakte

Aus den Angaben in der WSDL-Beschreibung wurden die nötigen Schnittstellen und Klassen
generiert, die, unter Verwendung der von AXIS bereistgestellten Funktionalitäten, den Zugriff
auf einen konkreten Web-Service von Java aus ermöglichen.

Klasse                                             Beschreibung
AccountingException                                Buchhaltungsausnahme
IOException                                        Kommunikationsausnahme
WebAccounting
                                                   Schnittstelle mit den Methoden der Ge-
                                                   schäftslogik
WebAccountingBindingStub
                                                   Lokale Stellvertreterklasse, die alle Metho-
                                                   denaufrufe an den Web-Service weiterleitet
WebAccountingService
                                                   Schnittstellte mit Methoden zum Zugriff auf
                                                   den Web-Service
WebAccountingServiceLocator
                                                   Hilfsklasse um ein Zugriffsobjekt für den
                                                   Web-Service zu erhalten

Tabelle 20: Von AXIS erzeugte Client-Klassen

Die hier automatisch erzeugte Schnittstelle WebAccounting entspricht im Prinzip der ur-
sprünglichen, von Hand definierten, Web-Service-Schnittstelle, was ja auch dem Sinn der
Verwendung von Web-Services entspricht. Nur wird diese Schnittstelle durch die Namens-
raumzuordnung einem anderen Java-Paket zugeordnet und es darf wegen der Verbindung mit
AXIS auch keine Verwechslung erfolgen.


6.4.5. Client-Implementierung

Die erzeugten Klassen und Schnittstellen können nun verwendet werden, um eine Client-
Anwendung im Sinne der Logik der Buchhaltung zu implementieren.


6.4.5.1. Proxy-Klasse

Die Proxy-Klasse verwendet eine Instanz der erzeugten Klasse WebServiceLocator um eine
Verbindung mit dem Anbieter des Web-Service herzustellen. Die SOAP-Adresse des Web-
Service braucht nicht mehr explizit angegeben zu werden, da sie bei der Generierung durch
die WSDL-Datei vorgegeben wurde.
Durch den Aufruf der Methode getWebAccountingPort() erhält man einen Port für den
Web-Service, der über die Web-Service-Schnittstelle WebAccounting angesprochen wird,
und durch eine Instanz der Stellvertreterklasse WebAccountingBindingStub realisiert wird.
Durch diesen Port können nun die Methoden des entfernten Web-Service aus Java heraus auf-
gerufen werden.

Allerdings entspricht ja die Web-Service-Schnittstelle nicht mehr der ursprünglichen Buch-
haltungsschnittstelle, weshalb eine Adapter-Klasse für den Port notwendig ist, die wieder auf
der ursprünglichen Buchhaltungsschnittstelle basiert und als Service-Klasse instanziert wird.




                                               - 227 -
                                  Java-Enterprise - Web-Services

package de.fhr.vs_iw.webservice;

public final class WebServiceProxy implements IAccountingProxy {

    public WebServiceProxy() {}

    public void close() {}

    public IAccountingService connect(String[] args) throws IOException {
      try {
        WebAccountingServiceLocator locator
          = new WebAccountingServiceLocator();
        WebAccounting port = locator.getWebAccountingPort();
        return new AccountingService(port);
      } catch (Exception e) {
        IOException ioe = new IOException(e.getMessage());
        ioe.initCause(e);
        throw ioe;
      }
    }
}

Code 136: Class WebServiceProxy

Mit dem Port-Objekt wird eine Instanz der Service-Klasse gebildet, die dann von der
connect()-Methode der Proxy-Klasse zurückgegeben wird.


6.4.5.2. Service-Klasse

Die Methoden der Service-Klasse rufen die entsprechenden Methoden des Web-Service bzw.
des Stellvertreter-Objekts für den Web-Service (Port) auf. Dabei müssen vor allem die Aus-
nahmen, für die aus der WSDL-Beschreibung neue Klassen generiert wurden, auf die origina-
len Ausnahmen aus dem Buchhaltungsmodell zurückgeführt werden.

package de.fhr.vs_iw.webservice;

public final class AccountingService implements IAccountingService {

    private final Map<Integer, AccountingEntry> accountingEntries
      = new HashMap<Integer, AccountingEntry>();

    private Map<Integer, Account> accounts = new HashMap<Integer, Account>();

    private final WebAccounting port;

    public AccountingService(WebAccounting port) {
      this.port = port;
    }

    public Double calculateBalance(Integer number)
        throws IOException, AccountingException {
      try {
        return port.calculateBalance(number);
      } catch (de.fhr.vs_iw.webservice.axis.IOException e) {
        throw new IOException(e.getMessage1());
      } catch (de.fhr.vs_iw.webservice.axis.AccountingException e) {
        throw new AccountingException(e.getMessage1());
      }
    }

                                             - 228 -
                                 Java-Enterprise - Web-Services


    ...

    public IAccount getAccountByNumber(Integer number)
        throws IOException, AccountingException {
      try {
        port.checkAccountNumber(number);
      } catch (de.fhr.vs_iw.webservice.axis.IOException e) {
        throw new IOException(e.getMessage1());
      } catch (de.fhr.vs_iw.webservice.axis.AccountingException e) {
        throw new AccountingException(e.getMessage1());
      }
      Account account = accounts.get(number);
      if (account == null) {
        account = new Account(number, port);
        accounts.put(number, account);
      }
      return account;
    }

    public Set<Integer> getAccounts() throws IOException {
      try {
        Integer[] ar = port.getAccounts();
        HashSet<Integer> set = new HashSet<Integer>();
        if (ar != null) {
          for (Integer i : ar) {
            set.add(i);
          }
        }
        return set;
      } catch (de.fhr.vs_iw.webservice.axis.IOException e) {
        throw new IOException(e.getMessage1());
      }
    }

    ...
}

Code 137: Class AccountingService (Web-Services)

Die Auflistung der Kontonummern bzw. Buchungs-IDs muß manuell von einem int-Array
wieder in einen Set umgewandelt werden.
Damit von der flachen Web-Service-Schnittstelle ausgehend wieder eine objektorientierte
Verwendung des Systems möglich ist, werden die entsprechenden Methoden der Web-
Service-Schnittstelle durch Konten- und Buchungsobjekte adaptiert.
Die Service-Methoden getAccountByNumber() bzw. getAccountingEntryById() erzeugen
mit dem verwendeten Web-Service-Port jeweils ein Adapter-Objekt für die angegebene Kon-
tonummer bzw. Buchungs-ID. Diese Adapter-Objekte werden von der Service-Klasse in
HashMaps verwaltet, damit nicht unnötig viele Instanzen erzeugt werden.


6.4.5.3. Konto-Klasse

Bei der Konto-Klasse werden auch nur die passenden Methoden des Web-Service-Port aufge-
rufen. Jedes Objekt der Klasse kennt dabei seine Kontonummer, die bei jedem Web-Service-
Aufruf mit übergeben wird.



                                              - 229 -
                                Java-Enterprise - Web-Services

package de.fhr.vs_iw.webservice;

public final class Account implements IAccount {

    private final Integer number;
    private final WebAccounting port;

    protected Account(Integer number, WebAccounting port) {
      this.number = number;
      this.port = port;
    }

    public Set<Integer> getCreditEntries() throws IOException {
      try {
        Integer[] ar = port.getCreditEntries(number);
        HashSet<Integer> set = new HashSet<Integer>();
        for (Integer i : ar) {
          set.add(i);
        }
        return set;
      } catch (de.fhr.vs_iw.webservice.axis.IOException e) {
        throw new IOException(e.getMessage1());
      }
    }

    public Integer getNumber() {
      return number;
    }

    public AccountType getType() throws IOException {
      try {
        return AccountType.getAccountTypeById(port.getType(number));
      } catch (de.fhr.vs_iw.webservice.axis.IOException e) {
        throw new IOException(e.getMessage1());
      }
    }

    public void setType(AccountType type) throws IOException {
      try {
        port.setType(number, type.getId());
      } catch (de.fhr.vs_iw.webservice.axis.IOException e) {
        throw new IOException(e.getMessage1());
      }
    }

    ...
}

Code 138: Class Account (Web-Service)

Auch bei der Konto-Klasse werden die Auflistungen der Soll- bzw. Haben-Buchungen manu-
ell wieder zu Sets umgeformt.
Zusätzlich muß der Kontotyp speziell behandelt werden, da Elemente einer Java-Enumeration
natürlich nicht direkt übertragen werden können. Dafür werden die Methoden getId() und
getAccountTypeId() der Klasse AccountType verwendet, wodurch der Kontotyp als nume-
rischer Wert übertragen wird.




                                           - 230 -
                                Java-Enterprise - Web-Services


6.4.5.4. Buchungs-Klasse

Für die Buchungs-Klasse gilt natürlich dasselbe wie für die Konto-Klasse. Alle Methodenauf-
rufe werden unter Verwendung der Buchungs-ID lediglich an den Web-Service-Port weiter-
gegeben und die Ausnahmen entsprechend umgewandelt.

package de.fhr.vs_iw.webservice;

public final class AccountingEntry implements IAccountingEntry {

    private final Integer id;
    private final WebAccounting port;

    protected AccountingEntry(final Integer id, final WebAccounting port) {
      this.id = id;
      this.port = port;
    }

    public Double getAmount() throws IOException {
      try {
        return port.getAmount(id);
      } catch (final de.fhr.vs_iw.webservice.axis.IOException e) {
        throw new IOException(e.getMessage1());
      }
    }

    public Integer getId() {
      return id;
    }

    public void setCreditAccount(final Integer creditAccount)
        throws IOException, AccountingException {
      try {
        port.setCreditAccount(id, creditAccount);
      } catch (final de.fhr.vs_iw.webservice.axis.IOException e) {
        throw new IOException(e.getMessage1());
      } catch (final de.fhr.vs_iw.webservice.axis.AccountingException e) {
        throw new AccountingException(e.getMessage1());
      }
    }

    ...
}

Code 139: Class AccountingEntry (Web-Service)



6.4.6. Fazit

Die Verwendung von Web-Services zur Realisierung eines Verteilten Systems kann den Ent-
wicklungsaufwand durch die vielfachen Möglichkeiten der automatisierten Codegenerierung
einer Anwendung in den unterschiedlichsten Programmiersprachen stark vereinfachen.
Der Nachteil besteht allerdings darin, daß 90% der Datenübertragung Overhead durch die
Verwendung von XML sind, und deshalb die Performance von Web-Services eher kritisch zu
betrachten ist (siehe Anhang D).



                                                - 231 -
                                 JSF-Web-Oberfläche - Übersicht


7. JSF-Web-Oberfläche




7.1. Übersicht




Abbildung 93: JSF-Lebenszyklus




                                            - 232 -
                              Buchhaltung Mobil - Übersicht


8. Buchhaltung Mobil




8.1. Übersicht
Da dieses Tutorial unter der Thematik "Verteilte Systeme" steht, darf auch die Betrachtung
mobiler Endgeräte nicht fehlen.


8.1.1. Pocket-PC

Konkret soll die mögliche Anbindung eines Pocket-PC an die bestehenden Buchhaltungssy-
steme veranschaulicht werden. Als Testgerät kommt ein HP iPAQ hx2750 mit Intel ARM-
Prozessor zum Einsatz.

Das Gerät hat die folgenden Hardware-Spezifikationen:

   •   Taktung: 624 MHz
   •   Display: 320x240 Pixel, 256000 Farben
   •   ROM-Speicher: 128MB
   •   RAM-Speicher: 128MB
   •   Wireless LAN und Bluetooth
   •   Kartenslots für Compact-Flash II und SD-Karten


8.1.2. Windows-Mobile

Das Betriebssystem des Pocket-PC ist Windows Mobile 2003 (Version 4.21). Es hält sich
soweit wie möglich und praktikabel an die von Desktop Rechnern her bekannte Windows
API. Normalerweise werden die Programme dafür in C bzw. C++ erstellt, speziell für den
Pocket-PC compiliert, und können dort dann nativ ablaufen. Microsoft stellt zu diesem Zweck
die "eMbedded Visual Tools" [60] und den "Pocket-PC 2003 SDK" [61] zur Verfügung.
Außerdem gibt es eine "eMbedded Visual Basic Runtime" und das "Microsoft .NET Compact
Framework", unter anderem zur Integration in "Visual Studio .NET", um Anwendungen in
diesen beiden Programmiersprachen für den Pocket-PC entwickeln zu können.
Auch von anderen Herstellern gibt es Entwicklungsumgebungen und Tools für Pocket-PC
Anwendungen. [59]




                                          - 233 -
                              Buchhaltung Mobil - Übersicht



8.1.3. Java Micro Edition

Mit der Java Micro Edition (J2ME) [63] wird eine Java-Umgebung für mobile Geräte im all-
gemeinen, und damit auch für den Pocket-PC im speziellen, spezifiziert. Diese API ist in
Konfigurationen und Profile unterteilt, wobei im wesentlichen zwei Standardkonfigurationen
unterschieden werden [62]:

   •   CDC - Connected Device Configuration - Die allgemeine Konfiguration bzw. Spezifi-
       kation für mobile Geräte mit 32-bit Prozessor und ca. 2.5 MB Speicher für die Java
       Umgebung (CVM – Connected Device Configuration Virtual Machine). Die Umge-
       bung zielt auf Kompatibilität mit der API und den Werkzeugen der Java Standard Edi-
       tion (J2SE) ab.
   •   CLDC - Connected Limited Device Configuration - Eine sehr schlanke Java Konfigu-
       ration für Geräte mit nur 128kB bis 512 kB Speicher, die nicht J2SE-konform sein
       muß.

CDC-Anwendungen sind durch die angestrebte Kompatibilität mit der J2SE auch auf jedem
Desktop Rechner ablauffähig. Umgekehrt wird der von CDC unterstützte Umfang der
J2SE-API durch das verwendete Profil definiert:

   •   Foundation Profile - Keine Unterstützung für Benutzeroberflächen
   •   Personal Basic Profile - Unterstützung für einfache AWT-Komponenten
   •   Personal Profile - Vollständige Unterstützung von AWT und Applets

Die Funktionalitäten dieser drei Profile bauen aufeinander auf und können durch spezifische
Zusatzprofile erweitert werden:

   •   RMI Optional Package - Remote Method Invocation aus dem java.rmi Paket
   •   JDBC Optional Package - Datenbankzugriff mit der JDBC 3.0 API
   •   Mobile Media Optional Package - Zugriff auf Audio und Video Dienste

Schließlich gibt es für mobile Geräte zwei brauchbare kommerzielle Implementierungen einer
Virtuellen Java Maschine nach diesen Spezifikationen [62]:

   •   IBM J9 JVM
   •   NSICOM CrEme JVM


8.1.4. Mysaifu JVM

Neben den kommerziellen JVMs gibt es aber eine freie Implementierung unter der GPL, die
Mysaifu JVM von "freebeans", die unter

   http://www2s.biglobe.ne.jp/~dat/java/project/jvm/index_en.html

heruntergeladen werden kann und für die Buchhaltungsanwendung verwendet wird. Sie hält
sich nicht direkt an die J2ME-Spezifikation, da sie ca. 10MB Speicher zur Ausführung benö-
tigt, hat aber zum Ziel die API der J2SE möglichst vollständig auf dem Pocket-PC zu unter-
stützen [64].


                                          - 234 -
                              Buchhaltung Mobil - Installation


8.2. Installation
Der erste Schritt für die Buchhaltung auf dem Pocket-PC ist die Installation der JVM.


8.2.1. Laufzeitumgebung

Nach dem Herunterladen und entpacken der Mysaifu JVM (jvm.Release.zip) wird die in
dem Archiv enthaltene CAB-Datei (jvm.Release.cab) auf den Pocket-PC kopiert (Active-
Sync, WLAN, etc.). Durch das Ausführen der CAB-Datei (antippen) wird die JVM im Ver-
zeichnis \Program Files\Mysaifu JVM installiert. Deinstalliert werden kann die JVM unter
Einstellungen -> Programme entfernen.



8.2.2. Aufruf der JVM

                                        Aufgerufen wird die JVM selbst direkt im Installati-
                                        onsverzeichnis

                                            \Program Files\Mysaifu JVM\bin\jre

                                        durch Ausführen (antippen) von jvm.exe.


                                        In der Hauptansicht wird nun eine Java-Klasse oder
                                        ein jar-Archiv (vs_iw_fibu_pocketpc_1.0.0.jar)
                                        ausgewählt, das gestartet werden soll.


                                        Außerdem kann man angeben ob die Konsole ange-
                                        zeigt werden soll, und es können einige Optionen für
                                        das Programm eingestellt werden.


Abbildung 94: Haupansicht der Mysaifu JVM



8.2.3. Entwicklungsumgebung

Da sich die Mysaifu JVM an die J2SE-Spezifikationen hält, können zur Entwicklung diesel-
ben Werkzeuge (Compiler, usw.) wie auch auf Desktop-PCs verwendet werden, ohne daß
spezielle Maßnahmen zur Vorbereitung der Programme für den Pocket-PC notwendig sind.
Von dem her wurde die Buchhaltungsanwendung für den Pocket-PC "ganz normal" in Eclipse
entwickelt. Wichtig ist nur, daß man keine API's und Klassen der J2SE verwendet, die auf
dem Pocket-PC nicht verfügbar sind.




                                            - 235 -
                        Buchhaltung Mobil - Buchhaltungsoberfläche


8.3. Buchhaltungsoberfläche
Für die Buchhaltung auf dem Pocket-PC wurde eine entsprechende Anwendung mit
AWT-Oberfläche erstellt, die unter der Mysaifu-JVM lauffähig ist.


8.3.1. Verfügbare Proxy-Module

Die Oberfläche dient wiederum nur als Client, der auf vorhandene Buchhaltungssysteme
zugreifen soll. Allerdings wird ein fester Satz Proxy-Klassen vorgegeben, die alle direkt im
jar-Archiv der Anwendung enthalten sind, und es wird nicht nach beliebigen, auf dem Pocket-
PC vorhandenen, Proxy-Klassen gesucht.

Die verfügbaren Proxy-Klassen umfassen:

   •   dummy        Dummy-Klasse, die alle Methoden implementiert
   •   exception    Dummy-Klasse, bei der jede Methode eine Ausnahme wirft
   •   notimpl      Dummy-Klasse, bei der jede Methode NotImplemented wirft
   •   local        Lokale Datenhaltung (siehe 4.2) (Standard-Einstellung)
   •   socket       Socket-Verbindung mit Host und Port (siehe 5.2)


                                        Die gewünschte Proxy-Klasse wird mit ihren not-
                                        wendigen Parametern auf der Kommandozeile ange-
                                        geben bzw. vor dem Aufruf der JVM im Feld für die
                                        Kommandozeilenargumente eingegeben.

                                        Außerdem kann ein CLASSPATH für weitere nötige
                                        Bibliotheken definiert, und das aktuelle Verzeichnis
                                        eingestellt werden.




Abbildung 95: Allgemeine Optionen der Mysaifu JVM




                                            - 236 -
                         Buchhaltung Mobil - Buchhaltungsoberfläche



8.3.2. Aufbau der Oberfläche

                                          Nach dem Starten der Anwendung wird über die
                                          angegebene Proxy-Klasse die Service-Klasse gela-
                                          den und im Statusbereich die erfolgreiche Ausfüh-
                                          rung oder eine aufgetretene Ausnahme angezeigt.

                                          Mit den zwei Schaltflächen "Konto" bzw. "Bu-
                                          chung" am oberen Fensterrand kann die entspre-
                                          chende Ansicht ausgewählt werden, die dann im
                                          freien mittleren Bereich eingeblendet wird.

                                          Windows Mobile typisch wird das Menü der An-
                                          wendung am unteren Bildschirmrand angezeigt.




Abbildung 96: Erste Ansicht der Pocket-Buchhaltung


8.3.2.1. Konto und Buchungsliste

Ja nach Auswahl wird eine Liste der vorhandenen Konten bzw. Buchungen angezeigt. Durch
das Selektieren eines Listeneintrags werden die genaueren Daten im Statusbereich eingeblen-
det. Mit den Schaltflächen neben der Liste können die entsprechenden Aktionen aufgerufen
werden.




Abbildung 97: Ansicht der Konten und Buchungen der Poket-Buchhaltung



                                              - 237 -
                         Buchhaltung Mobil - Buchhaltungsoberfläche


8.3.2.2. Konto anlegen oder ändern

Zum Anlegen eines neuen Kontos werden die entsprechenden Eingabefelder für die Daten
angezeigt. Beim Ändern eines Kontos wird dieselbe Ansicht verwendet, nur daß die Daten des
zu ändernden Kontos vorher eingetragen werden.




Abbildung 98: Pocket-Buchhaltung: Konto anlegen oder ändern


8.3.2.3. Buchung anlegen oder ändern

Bei den Buchungen verhält es sich analog zu den Konten. In der Ansicht werden die Eingabe-
felder entweder leer oder mit den Daten der zu ändernden Buchung angezeigt.




Abbildung 99: Pocket-Buchhaltung: Buchung anlegen oder ändern

                                             - 238 -
                         Buchhaltung Mobil - Buchhaltungsoberfläche


8.3.2.4. Konto oder Buchung löschen

Auch für das Löschen von Konten und Buchungen wird eine gemeinsame Ansicht verwendet,
in der nur die jeweiligen Daten eingetragen werden.




Abbildung 100: Pocket-Buchhaltung: Konto oder Buchung löschen


8.3.2.5. Konto-Details

Für Konten gibt es noch eine weitere Ansicht, für die separate Auflistung der Soll- und Ha-
ben-Buchungen eines Kontos (getCreditAccounts(), getDebitAccounts()). Außerdem sei
hier das Aussehen der Anwendung auf einem Desktop-PC dargestellt.




Abbildung 101: Pocket-Buchhaltung: Konto-Details

                                             - 239 -
                         Buchhaltung Mobil - Buchhaltungsoberfläche



8.3.3. Ausnahmen

Wenn bei einem Aufruf einer Buchhaltungsmethode eine Ausnahme, ein Problem oder ein
anderes Ereignis auftritt, wird dieses im Statusbereich dargestellt und passend farblich hinter-
legt (siehe 3.4.2).




Abbildung 102: Pocket-Buchhaltung: Einblendungen im Statusbereich



8.3.4. Menü

Das Menü der Anwendung wird am unteren Bildschirmrand angezeigt. Das Symbol ganz
links gehört jedoch nicht zur Anwendung, sondern dient zum Ausklappen der "Tastatur" von
Windows Mobile.


8.3.4.1. Schnittstellen-Menü

                                          Über das Schnittstellen-Menü können einige Ver-
                                          waltungsfunktionen aufgerufen werden.




Abbildung 103: Pocket-Buchhaltung: Schnittstellen-Menü



   •   Neu verbinden - Schließt die gerade geladene Service-Klasse (close()) und lädt sie
       neu (conect()). Das ist nützlich falls sich der Pocket-PC in den StandBy-Modus
       schaltet und dadurch die Verbindung zu Buchhaltungssystem abreißt.

   •   Daten erzeugen - Benutzt die Utilities-Klasse, um Testdaten zu erzeugen
       (siehe 3.7.1.1)

   •   Beenden - Schließt die Anwendung




                                             - 240 -
                         Buchhaltung Mobil - Buchhaltungsoberfläche


8.3.4.2. Hilfe-Menü

                                          Im Hilfe-Menü kann eine Info-Ansicht aufgerufen
                                          werden, die einige Fakten zu der Anwendung wie-
                                          dergibt, und eine Auflistung der verfügbaren Proxy-
                                          Klassen.


Abbildung 104: Pocket-Buchhaltung: Hilfe-Menü




Abbildung 105: Pocket-Buchhaltung: Info-Ansicht und Auflistung der Proxy-Klassen




                                              - 241 -
               Buchhaltung Mobil - Anbindung an die vorhandenen Systeme


8.4. Anbindung an die vorhandenen Systeme
Wie bereits mehrfach erwähnt soll die Buchhaltungsoberfläche auf dem Pocket-PC über die
entsprechenden Proxy-Klassen an die vorhandenen Module des Buchhaltungssystems ange-
bunden werden.


8.4.1. Lokale und Dummy-Klassen

Die Dummy-Klassen und die lokale Datenhaltung können direkt wie auf einem Desktop-PC
benutzt werden, dienen jedoch mehr zum Testen der Oberfläche.


8.4.2. An das Socket-Modul

Auch das bereits vorhandene Socket-Modul kann direkt verwendet werden, um die Verbin-
dung zu einem Buchhaltungs-Server herzustellen, da die Funktionalitäten und Klassen aus
den dafür notwendigen Paketen java.io und java.net vollständig vorhanden sind.


8.4.3. Über RMI

Die Verwendung von RMI ist im ersten Ansatz nicht möglich, da die Mysaifu JVM diese API
nicht unterstützt und die notwendigen Klassen nicht implementiert wurden.


8.4.4. Andere Technologien

Auch die Verwendung von anderen Technologien ist auf dem Pocket-PC mit der Mysaifu-
JVM nicht möglich, da deren Anforderungen derzeit von der Mysaifu JVM nicht erfüllt wer-
den.


8.5. Erfahrungen und Probleme
Die Programmierung der Oberfläche konnte wirklich direkt so erfolgen, wie für einen Desk-
top-Rechner auch. Nur bei der Verwendung des List-Steuerelements gab es einen Unter-
schied während der Programmausführung. Auf dem Pocket-PC ist das programmgesteuerte
Auswählen eines Elements der Liste nicht möglich.
Ansonsten ist die Anwendung auf dem Pocket-PC genauso brauchbar wie auf dem Desktop-
PC. Die Mysaifu JVM könnte wohl auch im produktiven Einsatz für mobile Anwendungen
verwendet werden.




                                         - 242 -
                                        Schlußwort


Schlußwort
Dieses Tutorial führt schrittweise von eigenen Implementierungen benötigter Funktionalitäten
hin zur Verwendung von mächtigen Frameworks. Dabei dient das Thema Buchhaltung als
Ausgangspunkt, der den Einstieg erleichtern soll, und als gemeinsame Basis.
Dabei hat sich gezeigt, daß eben der Einstieg durch die Vorgaben aus dem Buchhaltungsmo-
dell mit eigenen Implementierungen sehr gut möglich war, diese Vorgaben allerdings bei der
Verwendung zusammen mit den verschiedenen Technologien zum Teil auch hinderlich waren
und manchmal unnötige Umstände verursacht haben.
Bei der Anwendungsentwicklung muß also im Vorfeld gezielt darauf geachtet werden, welche
Technologien zum Einsatz kommen sollen, und die Anwendung sollte dann entsprechend auf
deren Verwendung hin entwickelt werden, um deren Vorteile vollständig nutzen zu können
und um unnötige Probleme zu vermeiden.
Vor allem der neue Enterprise JavaBeans 3.0 Standard kann die Anwendungsentwicklung aus
der Sicht des Programmierers erheblich vereinfachen, wenn man sich daran orientiert. Aller-
dings ist diese Technologie noch sehr neu und die dazu verfügbaren Ressourcen sind noch
beschränkt, und so kann es momentan noch vorkommen, daß man bei Problemen auf sich
alleine gestellt ist, und im Prinzip noch Pionier-Arbeit leisten muß.




                                          - 243 -
                                    Verzeichnisse


Verzeichnisse
Abkürzungsverzeichnis
AAC       Application Client Container
API       Application Programming Interface
AJP       Apache JServ Protocol
AS        Application Server [45]
AWT       Abstract Windowing Toolkit
BMP       Bean-Managed-Persistence
BPEL      Business Process Execution Language [43]
BPM       Business Process Management [44]
CDC       Connected Device Configuration
CIFS      Common Internet File System
CLDC      Connected Limited Device Configuration
CMP       Container-Managed-Persistence
CORBA     Common Object Request Broker Architecture [79]
CVM       Connected Device Configuration Virtual Machine
DAO       Data Access Object (Entity-Bean)
DBMS      Database Management System
DD        Deployment-Descriptor
DII       Dynamic Invokation Interface
DOM       Document Object Model
DSI       Dynamic Skeleton Interface
DTO       Data Transfer Object
EJB       Enterprise JavaBeans [45]
ERM       Entity-Relationship-Model
HQL       Hibernate Query Language
HSQLDB    Hypersonic-SQL-Database-Engine [14]
HTTP      Hypertext Transfer Protocol
HTTPS     Hypertext Transfer Protocol Secure
GIOP      General Inter-ORB Protocol [79]
IDE       Integrated Development Environment [30]
IDL       Interface Definition Language
IIOP      Internet Inter-ORB Protocol [79]
IMAP      Internet Message Access Protocol
IoC       Inversion of Control
IOR       Interoperable Object Reference
J2ME      Java 2 Platform, Micro Edition [63]
J2SE      Java 2 Platform, Standard Edition [01]
J2EE      Java 2 Platform, Enterprise Edition [01]
JAAS      Java Authentication and Authorization Service [07]
JAF       Java Beans Activation Framework [49]
JAX-RPC   Java-API for XML-Based RPC [55]
JAXB      Java Architecture for XML Binding [69]
JAXR      Java-API for XML-Registries [54]
JBPM      JBoss Business Process Management
JCA       J2EE Connector Architecture [56]
JCP       Java Community Process
JDBC      Java Database Connectivity
JDK       Java Development Kit [01]

                                       - 244 -
                                  Verzeichnisse

JEMS      JBoss Enterprise Middleware System
JMS       Java Messaging Service [15]
JMX       Java Management Extensions [16]
JNDI      Java Naming and Directory Interface [17]
JPA       Java Persistence API
JRMP      Java Remote Method Protocol
JSF       Java Server Faces
JSP       Java Server Page
JSR       Java Specification Request
JTA       Java Transaction API [51]
JTS       Java Transaction Service [52]
JVM       Java Virtual Machine [01]
JWSDP     Java Web-Services Developer Pack
MDA       Model Driven Architecture
MIME      Multipurpose Internet Mail Extensions
MOM       Message Oriented Middleware [15]
MVC       Model-View-Controller
NFS       Network File System
NNTP      Network News Transfer Protocol
OID       Object Identifier
OMG       Object Management Group [79]
OTS       Object Transaction Service
RMI       Remote Method Invocation [34]
RPC       Remote Procedure Call
PCMCIA    People Can't Memorize Computer Industry Acronyms
PDL       Process Definiton Language
POA       Portable Object Adapter
POJI      Plain Old Java Interface
POJO      Plain Old Java Object
POP3      Post Office Protocol, Version 3
SAX       Simple API for XML
SDK       Software Development Kit
SEI       Service Endpoint Interface
SMB       Server Message Block
SMTP      Simple Mail Transfer Protocol
SOA       Service-oriented Architecture
SOAP      Simple Object Access Protocol [26]
StAX      Streaming API for XML
UIL2      Unified Invocation Layer Version 2
URL       Uniform Resource Locator
VM        Virtual Machine
W3C       World Wide Web Consortium
WS        Web-Services [24]
WSE       Web-Services Enhancements
WSDK      Web-Services Development Kit
WSDL      Web-Service Description Language
WSTools   JBoss-Web-Service-Tools [23]
XML       Extensible Markup Language
XML-RPC   Extensible Markup Language - Remote Procedure Call
XOP       XML-binary Optimized Packaging


                                     - 245 -
                                                        Verzeichnisse


Abbildungsverzeichnis
Abbildung 1: Objektmodell der Buchhaltung ............................................................................ 4
Abbildung 2: Schnittstellenmodell der Buchhaltung ................................................................. 5
Abbildung 3: Vekettung der Module des Buchhaltungssystems ............................................. 15
Abbildung 4: Mögliche Verkettungsszenarien......................................................................... 16
Abbildung 5: Hauptansicht der Buchhaltungsoberfläche......................................................... 17
Abbildung 6: Schnittstellen-Menü ........................................................................................... 18
Abbildung 7: Service-Klasse laden .......................................................................................... 19
Abbildung 8: Hinweisanzeige bei der Auswahl der Proxy-Klasse .......................................... 19
Abbildung 9: Kontenliste ......................................................................................................... 21
Abbildung 10: Neues Konto anlegen ....................................................................................... 21
Abbildung 11: Konto bearbeiten .............................................................................................. 22
Abbildung 12: Konto löschen .................................................................................................. 22
Abbildung 13: Buchungsliste ................................................................................................... 23
Abbildung 14: Neue Buchung erfassen.................................................................................... 23
Abbildung 15: Buchung bearbeiten.......................................................................................... 24
Abbildung 16: Buchung löschen .............................................................................................. 24
Abbildung 17: Liste der Haben- und Soll-Buchungen eines Kontos ....................................... 25
Abbildung 18: Saldo eines Kontos........................................................................................... 25
Abbildung 19: Nachrichtenfenster bei Ausnahmen ................................................................. 26
Abbildung 20: Statusleiste grau ............................................................................................... 26
Abbildung 21: Statusleiste grün ............................................................................................... 27
Abbildung 22: Statusleiste gelb................................................................................................ 27
Abbildung 23: Statusleiste rot .................................................................................................. 27
Abbildung 24: Einblendung bei nicht implementierten Methoden (einzelne Werte) .............. 27
Abbildung 25: Einblendung bei nicht implementierten Methoden (ganze Auflistung)........... 27
Abbildung 26: Liste der laufenden Vorgänge .......................................................................... 33
Abbildung 27: Ansicht-Menü................................................................................................... 33
Abbildung 28: Threading-Menü............................................................................................... 35
Abbildung 29: Ablauf im Modus Singlethreaded .................................................................... 36
Abbildung 30: Ablauf im Modus Synchron einreihen ............................................................. 37
Abbildung 31: Ablauf im Modus Asynchron und Multithreaded ............................................ 38
Abbildung 32: Ablauf beim Abbruch eines Vorgangs............................................................. 39
Abbildung 33: Test-Menü ........................................................................................................ 41
Abbildung 34: Funktions-Test-Dialog ..................................................................................... 41
Abbildung 35: Geschwindigkeits-Test-Dialog......................................................................... 42
Abbildung 36: Threadsicherheits-Testdialog ........................................................................... 42
Abbildung 37: Logging-Menü ................................................................................................. 43
Abbildung 38: Look & Feel Menü........................................................................................... 46
Abbildung 39: Benutzeroberfläche im "Metal" Look & Feel .................................................. 46
Abbildung 40: Hilfe-Menü....................................................................................................... 47
Abbildung 41: Verzeichnis-Auswahl-Dialog........................................................................... 48
Abbildung 42: Eclipse-Dialog "New Project".......................................................................... 52
Abbildung 43: Eclipse-Dialog "New Java Project" ................................................................. 53
Abbildung 44: Eclipse Package Explorer................................................................................. 53
Abbildung 45: Eclipse-Dialog "New Folder" .......................................................................... 54
Abbildung 46: Eclipse Package Explorer................................................................................. 54
Abbildung 47: Eclipse-Dialog "Import" - Select ..................................................................... 55
Abbildung 48: Eclipse-Dialog "Import" - File system............................................................. 56
Abbildung 49: Eclipse Package Explorer................................................................................. 56

                                                             - 246 -
                                                       Verzeichnisse

Abbildung 50: Eclipse-Dialog "Properties for VS_IW_FiBu" ................................................ 57
Abbildung 51: Eclipse-Dialog "JAR Selection" ...................................................................... 57
Abbildung 52: Eclipse Package Explorer................................................................................. 58
Abbildung 53: Eclipse-Dialog "Run" - Empty......................................................................... 58
Abbildung 54: Eclipse-Dialog "Run" - Java Application ........................................................ 59
Abbildung 55: Eclipse-Dialog "New Java Package" ............................................................... 60
Abbildung 56: Eclipse Package Explorer................................................................................. 60
Abbildung 57: Eclipse-Dialog "Implemented Interfaces Selection"........................................ 61
Abbildung 58: Eclipse-Dialog "New Java Class" .................................................................... 61
Abbildung 59: Eclipse Package Explorer................................................................................. 62
Abbildung 60: Eclipse-Gesamtansicht des neu erstellten Projekts .......................................... 62
Abbildung 61: ERM-Modell 1 für die Buchhaltung mit Hibernate ....................................... 116
Abbildung 62: ERM-Modell 2 für die Buchhaltung mit Hibernate ....................................... 120
Abbildung 63: Datenpakte ..................................................................................................... 124
Abbildung 64: Anfrage-Antwort............................................................................................ 124
Abbildung 65: Entfernter Methodenaufruf ............................................................................ 124
Abbildung 66: Eclipse Package Explorer............................................................................... 125
Abbildung 67: Schematischer Ablauf von RMI..................................................................... 142
Abbildung 68: RMI-Kommunikations-Architektur ............................................................... 142
Abbildung 69: Nachladen von Java-Klassen ......................................................................... 150
Abbildung 70: Java 2 Enterprise Komponenten-Model......................................................... 169
Abbildung 71: Windows Umgebungsvariablen ..................................................................... 173
Abbildung 72: Paket-Auswahl bei der JBoss-Installation...................................................... 174
Abbildung 73: JBoss-Verzeichnisstruktur ............................................................................. 175
Abbildung 74: Konsolenfenster eines laufenden JBoss ......................................................... 180
Abbildung 75: Hypersonic Datenbank-Manager ................................................................... 184
Abbildung 76: Eclipse-Dialog "New Project"........................................................................ 186
Abbildung 77: Eclipse-Dialog "Select a JBoss configuration" .............................................. 187
Abbildung 78: Eclipse-Dialog "New Server" - Define .......................................................... 187
Abbildung 79: Eclipse-Dialog "New Server" - Create........................................................... 188
Abbildung 80: Eclipse Package Explorer - Servers ............................................................... 188
Abbildung 81: Eclipse Servers-Ansicht ................................................................................. 189
Abbildung 82: Eclipse-Console mit laufendem JBoss ........................................................... 189
Abbildung 83: Lebenszyklus einer zustandslosen Session-Bean........................................... 192
Abbildung 84: Lebenszyklus einer zustandsbehafteten Session-Bean .................................. 192
Abbildung 85: Interceptor-Aufruf.......................................................................................... 193
Abbildung 86: Lebenszyklus einer Entity-Bean .................................................................... 197
Abbildung 87: Ablauf des Entity-Bean-Prinzips bei einem Kontenobjekt ............................ 198
Abbildung 88: Beziehung der Entity-Beans........................................................................... 199
Abbildung 89: Lebenszyklus einer Message-Driven-Bean.................................................... 202
Abbildung 90: Nachrichtenschlange ...................................................................................... 203
Abbildung 91: Anmelde-Versende-System ........................................................................... 203
Abbildung 92: MBean-Lebenszyklus..................................................................................... 205
Abbildung 93: JSF-Lebenszyklus .......................................................................................... 232
Abbildung 94: Haupansicht der Mysaifu JVM ...................................................................... 235
Abbildung 95: Allgemeine Optionen der Mysaifu JVM........................................................ 236
Abbildung 96: Erste Ansicht der Pocket-Buchhaltung .......................................................... 237
Abbildung 97: Ansicht der Konten und Buchungen der Poket-Buchhaltung ........................ 237
Abbildung 98: Pocket-Buchhaltung: Konto anlegen oder ändern ......................................... 238
Abbildung 99: Pocket-Buchhaltung: Buchung anlegen oder ändern ..................................... 238
Abbildung 100: Pocket-Buchhaltung: Konto oder Buchung löschen .................................... 239

                                                            - 247 -
                                                Verzeichnisse

Abbildung 101: Pocket-Buchhaltung: Konto-Details ............................................................ 239
Abbildung 102: Pocket-Buchhaltung: Einblendungen im Statusbereich............................... 240
Abbildung 103: Pocket-Buchhaltung: Schnittstellen-Menü .................................................. 240
Abbildung 104: Pocket-Buchhaltung: Hilfe-Menü ................................................................ 241
Abbildung 105: Pocket-Buchhaltung: Info-Ansicht und Auflistung der Proxy-Klassen....... 241




                                                     - 248 -
                                                         Verzeichnisse


Codeverzeichnis
Code 1: Interface IAccount ........................................................................................................ 6
Code 2: Enumeration AccountType........................................................................................... 7
Code 3: Interface IAccountingEntry .......................................................................................... 7
Code 4: Interface IAccountingService ....................................................................................... 8
Code 5: Interface IAccountingProxy.......................................................................................... 9
Code 6: Interface IExtendedAccountingService ........................................................................ 9
Code 7: Class AccountingException........................................................................................ 10
Code 8: "Verpacken" einer beliebigen Exception als IOException ......................................... 11
Code 9: Class NotImplemented (Exception)............................................................................ 11
Code 10: Methoden loadProxy(…) der Utilities-Klasse .......................................................... 12
Code 11: Class NoInterfaceException ..................................................................................... 13
Code 12: Klassenvariable ARGS ............................................................................................. 19
Code 13: Ausgabe des Stacktrace mit Hilfe von StringWriter und PrintWriter ...................... 26
Code 14: Class AbstractRowTableModel<T> ......................................................................... 28
Code 15: Class AccountTableModel........................................................................................ 29
Code 16: Class AccountingEntryTableModel.......................................................................... 29
Code 17: Class RunnableServiceTask...................................................................................... 30
Code 18: Class MethodCallTask (Adapterschicht).................................................................. 30
Code 19: Class AbstractSwingTask ......................................................................................... 31
Code 20: Class Caller (GUI-Vorgang)..................................................................................... 31
Code 21: Interface SwingTaskListener .................................................................................... 32
Code 22: Class ThreadingService ............................................................................................ 34
Code 23: Verwendung von ExecutorService im ThreadingService......................................... 35
Code 24: Class Logger ............................................................................................................. 43
Code 25: Ermittlung des Klassennamens und des Threadnamens aus dem Stacktrace ........... 44
Code 26: In Spalten formatierte Log-Ausgabe ........................................................................ 44
Code 27: Ermittlung des Methodennamens aus dem Stacktrace ............................................. 44
Code 28: Hierarchische Methodenausgabe des Loggers.......................................................... 45
Code 29: Look & Feel Initialisierung ...................................................................................... 45
Code 30: Utilities-Methode setLookAndFeel()........................................................................ 47
Code 31: Class ClassFileLoader .............................................................................................. 48
Code 32. Interface ClassFileLoaderListener............................................................................ 49
Code 33: Interface ClassFileReflection.................................................................................... 50
Code 34: Class ClassFileDecoder ............................................................................................ 50
Code 35: Method classFound() in ServiceSelectDialog .......................................................... 51
Code 36: Class ClassFileEntry................................................................................................. 51
Code 37: HashMaps für Konten- und Buchungs-Objekte ....................................................... 64
Code 38: HashMaps für Soll- und Haben-Buchungen............................................................. 64
Code 39: Class AccountingService (Lokal) ............................................................................. 67
Code 40: Class Account (Lokal) .............................................................................................. 68
Code 41: Class AccountingEntry (Lokal) ................................................................................ 70
Code 42: Class LocalProxy ...................................................................................................... 70
Code 43: Class Account (Serialize) ......................................................................................... 71
Code 44: Class AccountingEntry (Serialize) ........................................................................... 72
Code 45: Method loadData() (Serialize) .................................................................................. 72
Code 46: Method storeData() (Serialize) ................................................................................. 73
Code 47: Class SerializeProxy ................................................................................................. 73
Code 48: Class AccountingService (JAXB - Variante 1) ........................................................ 80
Code 49: Class AccountingEntry (JAXB - Variante 1) ........................................................... 80

                                                              - 249 -
                                                        Verzeichnisse

Code 50: Methode afterUnmarshal()........................................................................................ 81
Code 51: Class Account (JAXB - Variante 1) ......................................................................... 82
Code 52: Class AccountingService (JAXB - Variante 2) ........................................................ 85
Code 53: Class Account (JAXB - Variante 2) ......................................................................... 86
Code 54: Class AccountingEntry (JAXB - Variante 2) ........................................................... 87
Code 55: Class JAXBProxy1 ................................................................................................... 89
Code 56: Class JAXBUtil ........................................................................................................ 90
Code 57: Method writeXML() (JAXBUtil) ............................................................................. 90
Code 58: Method readXML() (JAXBUtil)............................................................................... 91
Code 59: Method getSchema() (JAXBUtil)............................................................................. 91
Code 60: Class MySQLProxy .................................................................................................. 93
Code 61: Method query() (MySQL) ........................................................................................ 93
Code 62: Method querySet() (MySQL) ................................................................................... 94
Code 63: Method execute() (MySQL) ..................................................................................... 94
Code 64: Method prepare() (MySQL) ..................................................................................... 95
Code 65: Verwaltung der Konten- und Buchungsobjekte (MySQL)....................................... 96
Code 66: Class Account (Hibernate-Beispiel) ....................................................................... 101
Code 67: Beispiel für 1:1-Beziehung mit Primär-Schlüssel .................................................. 103
Code 68: Beispiel für 1:1-Beziehung mit Fremd-Schlüssel................................................... 104
Code 69: Beispiel für 1:n-Beziehung ..................................................................................... 105
Code 70: Beispiel für n:n-Beziehung ..................................................................................... 105
Code 71: Manuelle Transaktions-Demarkation (1)................................................................ 109
Code 72: Manuelle Transaktions-Demarkation (2)................................................................ 109
Code 73: Selbst implementierte Versionskontrolle................................................................ 111
Code 74: Klasse mit automatischer Versionskontrolle durch Hibernate ............................... 111
Code 75: Class AccountingService (Hibernate - Variante 1) ................................................ 117
Code 76: Class Account (Hibernate - Variante 1).................................................................. 118
Code 77: Class AccountingEntry (Hibernate - Variante 1).................................................... 119
Code 78: Class AccountingService (Hibernate - Variante 2) ................................................ 120
Code 79: Class Account (Hibernate - Variante 2).................................................................. 121
Code 80: Class AccountingEntry (Hibernate - Variante 2).................................................... 121
Code 81: Class HibernateProxy2 (Hibernate - Variante 2).................................................... 122
Code 82: Class SocketServer ................................................................................................. 126
Code 83: Class SocketProxy .................................................................................................. 128
Code 84: run()-Methode von SocketServer ........................................................................... 129
Code 85: Class Request.......................................................................................................... 130
Code 86: Class ClientSideRequest......................................................................................... 130
Code 87: Class ServerSideResponse...................................................................................... 131
Code 88: Class Response ....................................................................................................... 132
Code 89: Class ServerSideResponse...................................................................................... 133
Code 90: Class ClientSideResponse ...................................................................................... 133
Code 91: Class StringUnMarshaller (Teil 1).......................................................................... 134
Code 92: Class StringUnMarshaller (Teil 2).......................................................................... 134
Code 93: Class StringUnMarshaller (Teil 3).......................................................................... 135
Code 94: Method mashalSet()................................................................................................ 136
Code 95: Method unmarshalSet() .......................................................................................... 136
Code 96: Methoden marshalThrowable() und unmarshalThrowable().................................. 136
Code 97: Methode remoteObjectResponse() ......................................................................... 138
Code 98: Methoden getOid() und genOid() ........................................................................... 138
Code 99: Implementierung einiger Buchhaltungsmethoden (Socket - AccountingService) . 139
Code 100: Method request1() (SocketProxy)......................................................................... 140

                                                             - 250 -
                                                       Verzeichnisse

Code 101: Method request2() (SocketProxy)......................................................................... 140
Code 102: Method request() (SocketProxy)........................................................................... 140
Code 103: Method requestObject() (SocketProxy)................................................................ 141
Code 104: Method calculateBalance() bei RMI..................................................................... 144
Code 105: Class LocateRegistry ............................................................................................ 144
Code 106: Interface Registry.................................................................................................. 145
Code 107: Class RMIServer................................................................................................... 145
Code 108: Class AccountingService (RMI)........................................................................... 147
Code 109: Class Account (RMI)............................................................................................ 147
Code 110: Class AccountingEntry (RMI).............................................................................. 148
Code 111: Class RMIProxy.................................................................................................... 149
Code 112: Class CorbaServer (Methoden)............................................................................. 158
Code 113: Class CorbaServer (main)..................................................................................... 159
Code 114: Class CorbaServerAccount................................................................................... 160
Code 115: Class CorbaServerEntry........................................................................................ 161
Code 116: Class AccountingService (CORBA)..................................................................... 163
Code 117: Class Account (CORBA)...................................................................................... 164
Code 118: Class AccountingEntry (CORBA)........................................................................ 164
Code 119: Class CorbaProxy ................................................................................................. 165
Code 120: Interceptor-Methode ............................................................................................. 194
Code 121: Class EJBAccountingService ............................................................................... 196
Code 122: Class Account (EJB)............................................................................................. 200
Code 123: Class AccountingEntry (EJB)............................................................................... 202
Code 124: Interface MessageListener .................................................................................... 203
Code 125: Class MessageBean .............................................................................................. 204
Code 126: Interface MyManagementMBean......................................................................... 205
Code 127: Interface TimerService ......................................................................................... 206
Code 128: Interface BackupTimerMBean ............................................................................. 207
Code 129: Class BackupTimer............................................................................................... 207
Code 130: Class EJBProxy .................................................................................................... 213
Code 131: Class MessageClient............................................................................................. 214
Code 132: Interface WebAccounting ..................................................................................... 218
Code 133: Konto-Methoden für Interface WebAccounting................................................... 219
Code 134: Buchungs-Methoden für Interface WebAccounting............................................. 219
Code 135: Class WebAccountingImpl................................................................................... 220
Code 136: Class WebServiceProxy........................................................................................ 228
Code 137: Class AccountingService (Web-Services)............................................................ 229
Code 138: Class Account (Web-Service)............................................................................... 230
Code 139: Class AccountingEntry (Web-Service)................................................................. 231




                                                            - 251 -
                                                         Verzeichnisse


Resourcenverzeichnis
Resource 1: Ant-Build-Datei jaxb-generate-schema-v1.xml ................................................... 82
Resource 2: schema-v1.xsd ...................................................................................................... 84
Resource 3: fibu-v1.xml ........................................................................................................... 84
Resource 4: schema-v2.xsd ...................................................................................................... 88
Resource 5: fibu-v2.xml ........................................................................................................... 88
Resource 6: SQL-Befehle zum Anlegen der Konten- und Buchungs-Tabelle......................... 95
Resource 7: SQL-Befehle der Service-Klasse (MySQL)......................................................... 96
Resource 8: SQL-Befehle der Konto-Klasse (MySQL)........................................................... 97
Resource 9: SQL-Befehle der Buchungs-Klasse (MySQL)..................................................... 98
Resource 10: Hibernate Datenbankeinstellungen .................................................................. 107
Resource 11: Socket-Protokoll - Anfrage .............................................................................. 129
Resource 12: Socket-Protokoll - Antwort .............................................................................. 132
Resource 13: CORBA Accounting.idl ................................................................................... 154
Resource 14: Ant-Build-Datei corba-idlj.xml ........................................................................ 156
Resource 15: Bedeutung der CORBA-Suffixe....................................................................... 157
Resource 16: jacorbsettings.cmd............................................................................................ 167
Resource 17: jacorbnaming.cmd ............................................................................................ 167
Resource 18: jacorbserver.cmd .............................................................................................. 167
Resource 19: jacorbclient.cmd ............................................................................................... 168
Resource 20: Hypersonic-Datenquelle hsqldb-ds.xml ........................................................... 176
Resource 21: MySQL-Datenquelle mysql-ds.xml ................................................................. 177
Resource 22: PostegreSQL-Datenquelle postgres-ds.xml ..................................................... 178
Resource 23: Oracle-Datenquelle oracle-ds.xml.................................................................... 179
Resource 24: JNDI-Ansicht des allgemeinen Java-Namensraumes ...................................... 183
Resource 25: jmx-queue-service.xml ..................................................................................... 204
Resource 26: jms-topic-service.xml ....................................................................................... 204
Resource 27: application.xml (EJB)....................................................................................... 208
Resource 28: Ant-Vorgang für EJB3-Archiv......................................................................... 209
Resource 29: Ant-Vorgang für PAR-Archiv.......................................................................... 209
Resource 30: Deployment-Descriptor persistence.xml .......................................................... 210
Resource 31: Ant-Vorgang für JAR-Archiv .......................................................................... 210
Resource 32: Ant-Build-Datei ejb-deploy.xml ...................................................................... 211
Resource 33: build.properties................................................................................................. 212
Resource 34: jndi.properties................................................................................................... 215
Resource 35: log4j.properties................................................................................................. 215
Resource 36: Web-Service-Descriptor web.xml .................................................................... 221
Resource 37: Ant-Build-Datei webservice-deploy.xml ......................................................... 222
Resource 38: application.xml (Web-Service) ........................................................................ 222
Resource 39: Ant-Build-Datei webservice-generate.xml....................................................... 223
Resource 40: WSTools-Konfiguration wstools-java2wsdl.xml............................................. 224
Resource 41: WSTools-Konfiguration wstools-wsdl2java.xml............................................. 225
Resource 42: Ant-Build-Datei axis-generate-client.xml ........................................................ 226




                                                              - 252 -
                                                       Verzeichnisse


Tabellenverzeichnis
Tabelle 1: Beispiel-Buchungsatz................................................................................................ 4
Tabelle 2: Verwendete Begriffe ................................................................................................. 5
Tabelle 3: Modulares Konzept der Programmteile .................................................................. 12
Tabelle 4: JAXB Paket-Annotations ........................................................................................ 76
Tabelle 5: JAXB Klassen-Annotations .................................................................................... 76
Tabelle 6: JAXB Enum-Annotations ....................................................................................... 77
Tabelle 7: JAXB Feld-Annotations.......................................................................................... 78
Tabelle 8: JAXB Objekt-Fabrik-Annotations .......................................................................... 79
Tabelle 9: JAXB Adapter-Annotations .................................................................................... 79
Tabelle 10: Einbindung von Hibernate als zusätzliche Schicht ............................................... 99
Tabelle 11: Schematisher Aufbau der Hibernate-Komponenten ............................................. 99
Tabelle 12: Hibernate-Beziehungs-Semantik......................................................................... 104
Tabelle 13: Zuordnung der Basisdatentypen.......................................................................... 155
Tabelle 14: Zuordnung der komplexen IDL-Angaben........................................................... 155
Tabelle 15: Erzeugte CORBA-Artefakte ............................................................................... 157
Tabelle 16: Bean-Callback-Methoden und Annotations........................................................ 191
Tabelle 17: Annotation für Dependency-Injection................................................................. 193
Tabelle 18: Vergleich von SOAP-Nachrichten für RPC-Style und Document-Style ............ 217
Tabelle 19: Vergleich von SOAP-Nachrichten für literal und encoded................................. 217
Tabelle 20: Von AXIS erzeugte Client-Klassen .................................................................... 227




                                                            - 253 -
                                      Verzeichnisse


Quellenverzeichnis
[01] Homepage der Firma Sun-Microsystems
     http://java.sun.com/

[02] Homepage des JBoss-Application-Server
     http://www.jboss.com/

[03] Deutsche Version der JBoss-Homepage
     http://de.jboss.com/

[04] Wikipedia-Artikel über den JBoss
     http://de.wikipedia.org/wiki/JBoss_Application_Server

[05] Homepage der Apache-Foundation
     http://www.apache.de/

[06] Hompage des Tomcat Web-Containers
     http://tomcat.apache.org/

[07] Homepage des Java Authentication and Authorization Service (JAAS)
     http://java.sun.com/products/jaas/

[08] Wikipedia-Artikel über die Java 2 Enterprise Edition (J2EE)
     http://de.wikipedia.org/wiki/J2EE

[09] Homepage des Java-basierten Installer-Generators IzPack
     http://www.izforge.com/izpack/

[10] Wikipedia-Artikel über MySQL
     http://de.wikipedia.org/wiki/MySQL

[11] Homepage der MySQL-Datenbank
     http://www.mysql.com/

[12] Wikipedia-Artikel über Finanzbuchhaltung
     http://de.wikipedia.org/wiki/Finanzbuchhaltung

[13] Wikipedia-Artikel über Buchhaltung
     http://de.wikipedia.org/wiki/Buchhaltung

[14] Homepage der Hypersonic-Datenbank (HSQLDB)
     http://www.hsqldb.org/

[15] Homepage des Java Messaging Service (JMS)
     http://java.sun.com/products/jms/

[16] Homepage der Java Management Extensions (JMX)
     http://java.sun.com/javase/technologies/core/mntr-mgmt/javamanagement/



                                          - 254 -
                                        Verzeichnisse

[17] Homepage des Java Naming and Directory Interface (JNDI)
     http://java.sun.com/javase/technologies/core/jndi/index.jsp

[18] Heiko W. Rupp, JBoss - Server-Handbuch für J2EE-Entwickler und Administratoren,
     dpunkt-Verlag, 1. Auflage 2005, ISBN 3-89864-318-2

[19] Homepage der Open-Source-Datenbank PostgreSQL
     http://www.postgresql.org/

[20] Violet, ein einfacher schlanker Java-UML-Editor
     http://horstmann.com/violet/

[21] FAQ zu den Web-Services beim JBoss
     http://wiki.jboss.org/wiki/Wiki.jsp?page=JBWSFAQ

[22] Homepage der JBoss-Web-Services
     http://labs.jboss.com/portal/jbossws/

[23] JBoss-Web-Services User-Guide
     http://labs.jboss.com/portal/jbossws/user-guide/en/html/index.html

[24] Homepage der Web-Services Interoperability Organization, Basic Profile Version 1.0
     http://www.ws-i.org/Profiles/BasicProfile-1.0-2004-04-16.html

[25] IBM-Homepage: Welche Art von Web-Service sollte man benutzen?
     http://www-128.ibm.com/developerworks/webservices/library/ws-whichwsdl/

[26] SOAP-Spezifikation vom W3C
     http://www.w3.org/TR/soap/

[27] Homepage der Hibernate-Projekts
     http://www.hibernate.org/

[28] Java-Web-Services Homepage
     http://java.sun.com/webservices/jwsdp/index.jsp

[29] Artikel "Threads and Swing" bei Sun
     http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html

[30] Homepage des Eclipse-Projects
     http://www.eclipse.org/

[31] Sun-Dokument über die abgelehnten Methoden bei Threads
     http://java.sun.com/j2se/1.4.2/docs/guide/misc/threadPrimitiveDeprecation.html

[32] Homepage des AXIS-Framework bei der Apache-Foundation
     http://ws.apache.org/axis/

[33] Homepage zu Remote Method Invocation (RMI)
     http://java.sun.com/javase/technologies/core/basic/rmi/index.jsp


                                             - 255 -
                                       Verzeichnisse

[34] Wikipedia-Artikel zu RMI
     http://de.wikipedia.org/wiki/Remote_Method_Invocation

[35] RMI-Tutorial von Sun
     http://java.sun.com/docs/books/tutorial/rmi/index.html

[36] Ziele und Funktionsweisen von RMI
     http://www.sbgl.de/rmi/

[37] Homepage der Java Database Connectivity (JDBC)
     http://java.sun.com/javase/technologies/database/index.jsp

[38] Homepage der JBoss Eclipse IDE
     http://www.jboss.org/products/jbosside

[39] Homepage des JBoss Cache Projekts
     http://www.jboss.org/products/jbosscache

[40] Homepage des JBoss Framework zur aspektorientierten Programmierung
     http://labs.jboss.com/portal/jbossaop

[41] Homepage von JBoss jBPM
     http://www.jboss.com/products/jbpm

[42] Homepage der JBoss Rules
     http://www.jboss.com/products/rules

[43] Wikipedia-Artikel zu BPEL
     http://de.wikipedia.org/wiki/Business_Process_Execution_Language

[44] Wikipedia-Artikel zu BPM
     http://de.wikipedia.org/wiki/Prozessmanagement

[45] Java 2 Enterprise Edition Homepage
     http://java.sun.com/javaee/

[46] Wikipedia-Artikel über EJB-Container
     http://de.wikipedia.org/wiki/EJB-Container

[47] Verwendung des Application-Client-Container (AAC)
     http://docs.sun.com/source/817-2173-10/dcacc.html

[48] Wikipedia-Artikel über die Java Management Extensions (JMX)
     http://de.wikipedia.org/wiki/JMX

[49] Homepage des Java Beans Activation Framework (JAF)
     http://java.sun.com/products/javabeans/jaf/downloads/index.html

[50] Wikipedia-Artikel über den Java Authentication and Authorization Service
     http://de.wikipedia.org/wiki/JAAS


                                           - 256 -
                                       Verzeichnisse

[51] Homepage der Java Transaction API (JTA)
     http://java.sun.com/products/jta/

[52] Homepage des Java Transaction Service (JTS)
     http://java.sun.com/products/jts/

[53] Homepage der JavaMail-API bei Sun
     http://java.sun.com/products/javamail/

[54] Homepage der Java-API for XML Registries (JAXR)
     http://java.sun.com/webservices/jaxr/index.jsp

[55] Homepage der Java-API for XML-Based RPC (JAX-RPC)
     http://java.sun.com/webservices/jaxrpc/

[56] Homepage der Java Connector Architecture (JCA)
     http://java.sun.com/j2ee/connector/

[57] Wikipedia-Artikel über die Java Connector Architecture (JCA)
     http://de.wikipedia.org/wiki/J2EE_Connector_Architecture

[58] Java on Pocket-PC (Unofficial FAQ)
     http://blog.vikdavid.com/2004/12/java_on_pocketp.html

[59] Introduction to Pocket-PC Development
     http://www.codeproject.com/ce/Pocket_PC_Development.asp

[60] Windows Mobile Developer Center
     http://msdn.microsoft.com/windowsmobile/default.aspx

[61] Windows Mobile Developer Resources
     http://www.microsoft.com/windowsmobile/developers/default.mspx

[62] Sun CDC FAQ
     http://java.sun.com/products/cdc/faq.html

[63] Homepage der Java Micro Edition (J2ME)
     http://java.sun.com/javame/index.jsp

[64] Homepage der Mysaifu JVM
     http://www2s.biglobe.ne.jp/~dat/java/project/jvm/index_en.html

[65] JSR-250: Common Annotations for the Java Platform
     http://jcp.org/en/jsr/detail?id=250

[66] JSR-220: Enterprise Java Beans 3.0
     http://jcp.org/en/jsr/detail?id=220

[67] Hibernate Reference Doumentation
     http://www.hibernate.org/hib_docs/v3/reference/en/html_single/


                                           - 257 -
                                       Verzeichnisse

[68] Hibernate Annotations Reference Guide
     http://www.hibernate.org/hib_docs/annotations/reference/en/html_single/

[69] Home der Java Architecture for XML Bindung (JAXB)
     http://java.sun.com/webservices/jaxb/

[70] Wikipedia-Artikel über die Java Architecture for XML Binding (JAXB)
     http://de.wikipedia.org/wiki/JAXB

[71] The Java EE 5 Tutorial, JAXB, Page 5
     http://java.sun.com/javaee/5/docs/tutorial/doc/JAXB5.html

[72] JAXB: Issue 268 - Map handling broken
     https://jaxb.dev.java.net/issues/show_bug.cgi?id=268

[73] Wikipedia-Artikel über Enterprise JavaBeans
     http://de.wikipedia.org/wiki/Enterprise_Java_Beans

[74] EJB Glossar
     http://www.objects-at-work.de/pmwiki/EJBGlossar/HomePage

[75] Wikipedia-Artikel zum Java Messaging Service (JMS)
     http://de.wikipedia.org/wiki/Java_Message_Service

[76] Homepage des Apache Logging Services Project Log4j
     http://logging.apache.org/log4j/docs/

[77] Dynamic code downloading using Java RMI
     http://java.sun.com/j2se/1.5.0/docs/guide/rmi/codebase.html

[78] JacORB Homepage
     http://www.jacorb.org/

[79] Homepage der Object Management Group (OMG)
     http://www.omg.org/

[80] Wikipedia-Artikel über CORBA
     http://de.wikipedia.org/wiki/CORBA

[81] CORBA-Dienste - Ausarbeitung zum Hauptseminar Middleware
     http://www.marclayer.de/stud/proj/cos/CORBAservices.php

[82] Eclipse Europa Projektseite
     http://www.eclipse.org/europa/

[83] Spezifikation des Formats von class-Dateien
     http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html

[84] Werner Eberling, Jan Leßner, Enterprise JavaBeans 3 – Das EJB3-Praxisbuch für Ein-
     und Umsteiger, Hanser-Verlag, 1. Auflage 2007, ISBN 978-3-446-41085-5


                                          - 258 -
                                  Anhänge


Anhänge
Anhang A: System-Eigenschaften des JBoss
awt.toolkit                 sun.awt.windows.WToolkit
catalina.base               D:\daemon\jboss-4.0.5.GA\server\default
catalina.ext.dirs           D:\daemon\jboss-4.0.5.GA\server\default\lib
catalina.home               D:\daemon\jboss-4.0.5.GA\server\default
catalina.useNaming          false
file.encoding               Cp1252
file.encoding.pkg           sun.io
file.separator              \
hibernate.bytecode.provider    javassist
java.awt.graphicsenv        sun.awt.Win32GraphicsEnvironment
java.awt.printerjob         sun.awt.windows.WPrinterJob
java.class.path             C:\Program Files\Java\jdk1.5.0_12\lib\tools.jar;
                            D:\ daemon\jboss-4.0.5.GA\bin\\run.jar
java.class.version          49.0
java.endorsed.dirs          D:\daemon\jboss-4.0.5.GA\bin\..\lib\endorsed
java.ext.dirs               C:\Program Files\Java\jdk1.5.0_12\jre\lib\ext
java.home                   C:\Program Files\Java\jdk1.5.0_12\jre
java.io.tmpdir              C:\DOCUME~1\Hawkeye\LOCALS~1\Temp\
java.library.path           .;
                            C:\WINDOWS;
                            C:\WINDOWS\system32;
                            D:\Tools\bin;
                            C:\Program Files\Java\jdk1.5.0_12\bin;
                            C:\Dev-Cpp\bin;
                            D:\Studium\mico-win32-bin;
java.naming.factory.initial    org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs org.jboss.naming:org.jnp.interfaces
java.protocol.handler.pkgs     org.jboss.net.protocol
java.rmi.server.RMIClassLoaderSpi org.jboss.system.JBossRMIClassLoader
java.rmi.server.codebase    http://belldandy:8083/
java.runtime.name           Java(TM) 2 Runtime Environment, Standard Edition
java.runtime.version        1.5.0_12-b04
java.specification.name     Java Platform API Specification
java.specification.vendor Sun Microsystems Inc.
java.specification.version 1.5
java.vendor                 Sun Microsystems Inc.
java.vendor.url             http://java.sun.com/
java.vendor.url.bug         http://java.sun.com/cgi-bin/bugreport.cgi
java.version                1.5.0_12
java.vm.info                mixed mode
java.vm.name                Java HotSpot(TM) Server VM
java.vm.specification.name       Java Virtual Machine Specification
java.vm.specification.vendor     Sun Microsystems Inc.
java.vm.specification.version 1.0
java.vm.vendor              Sun Microsystems Inc.
java.vm.version             1.5.0_12-b04
jboss.bind.address          0.0.0.0
jboss.home.dir              D:\daemon\jboss-4.0.5.GA
jboss.server.base.dir       D:\daemon\jboss-4.0.5.GA\server
jboss.server.data.dir       D:\daemon\jboss-4.0.5.GA\server\default\data
jboss.server.home.dir       D:\daemon\jboss-4.0.5.GA\server\default
jboss.server.log.dir        D:\daemon\jboss-4.0.5.GA\server\default\log
jboss.server.name           default
jboss.server.temp.dir       D:\daemon\jboss-4.0.5.GA\server\default\tmp
line.separator
os.arch                     x86
os.name                     Windows XP

                                   - 259 -
                                  Anhänge

os.version                 5.1
path.separator             ;
program.name               run.bat
server.loader              ${catalina.home}/server/classes,
                           ${catalina.home}/server/lib/*.jar
shared.loader              ${catalina.base}/shared/classes,
                           ${catalina.base}/shared/lib/*.jar
sun.arch.data.model        32
sun.boot.class.path
              D:\daemon\jboss-4.0.5.GA\bin\..\lib\endorsed\resolver.jar;
              D:\daemon\jboss-4.0.5.GA\bin\..\lib\endorsed\serializer.jar;
              D:\daemon\jboss-4.0.5.GA\bin\..\lib\endorsed\xalan.jar;
              D:\daemon\jboss-4.0.5.GA\bin\..\lib\endorsed\xercesImpl.jar;
              D:\daemon\jboss-4.0.5.GA\bin\..\lib\endorsed\xml-apis.jar;
              C:\Program Files\Java\jdk1.5.0_12\jre\lib\rt.jar;
              C:\Program Files\Java\jdk1.5.0_12\jre\lib\i18n.jar;
              C:\Program Files\Java\jdk1.5.0_12\jre\lib\sunrsasign.jar;
              C:\Program Files\Java\jdk1.5.0_12\jre\lib\jsse.jar;
              C:\Program Files\Java\jdk1.5.0_12\jre\lib\jce.jar;
              C:\Program Files\Java\jdk1.5.0_12\jre\lib\charsets.jar;
              C:\Program Files\Java\jdk1.5.0_12\jre\classes
sun.boot.library.path       C:\Programme\Java\jdk1.5.0_12\jre\bin
sun.cpu.endian              little
sun.desktop                 windows
sun.io.unicode.encoding     UnicodeLittle
sun.jnu.encoding            Cp1252
sun.management.compiler     HotSpot Server Compiler
sun.os.patch.level          Service Pack 2
user.country                DE
user.dir                    D:\ daemon\jboss-4.0.5.GA\bin
user.home                   C:\Documents and Settings\luf39862
user.language               de
user.name                   luf39862
user.timezone               Europe/Berlin




                                   - 260 -
                            Anhänge


Anhang B: Erweiterte Darstellung des asynchronen Threading




                             - 261 -
                                  Anhänge


Anhang C: Notwendige Bibliotheken
Java Architecture for XML Binding (JAXB)

  jaxb1-impl.jar                            jaxb-xjc.jar
  jaxb-api.jar                              jsr173_api.jar
  jaxb-impl.jar                             sjsxp.jar


Hibernate-Core

  ant-1.6.5.jar                             jacc-1_0-fr.jar
  ant-antlr-1.6.5.jar                       javassist.jar
  ant-junit-1.6.5.jar                       jaxen-1.1-beta-7.jar
  ant-launcher-1.6.5.jar                    jboss-cache.jar
  antlr-2.7.6.jar                           jboss-common.jar
  ant-swing-1.6.5.jar                       jboss-jmx.jar
  asm.jar                                   jboss-system.jar
  asm-attrs.jar                             jdbc2_0-stdext.jar
  c3p0-0.9.0.jar                            jgroups-2.2.8.jar
  cglib-2.1.3.jar                           jta.jar
  checkstyle-all.jar                        junit-3.8.1.jar
  cleanimports.jar                          log4j-1.2.11.jar
  commons-collections-2.1.1.jar             oscache-2.1.jar
  commons-logging-1.0.4.jar                 proxool-0.8.3.jar
  concurrent-1.3.2.jar                      swarmcache-1.0rc2.jar
  connector.jar                             syndiag2.jar
  dom4j-1.6.1.jar                           versioncheck.jar
  ehcache-1.2.jar                           xerces-2.6.2.jar
  jaas.jar                                  xml-apis.jar


Hibernate-Annotations

  ejb3-persistence.jar                      lucene-core-2.0.0.jar


Web-Services

  jaxws-api.jar                             jsr181-api.jar
  jaxws-rt.jar                              jsr250-api.jar
  jaxws-tools.jar


JBoss-Web-Services-Tools

  jaxb-api.jar                              policy-1.0.jar
  jaxb-impl.jar                             stax-api-1.0.jar
  jboss-jaxws.jar                           wsdl4j.jar
  jbossws-core.jar                          xmlsec.jar
  jbossws-jboss-integration.jar

                                  - 262 -
                                  Anhänge


AXIS

  axis.jar                                  jaxrpc.jar
  axis-ant.jar                              log4j-1.2.8.jar
  commons-discovery-0.2.jar                 saaj.jar
  commons-logging-1.0.4.jar                 wsdl4j-1.5.1.jar


JBoss-Client

  activation.jar                            jbossha-client.jar
  antlr-2.7.6.jar                           jboss-iiop-client.jar
  avalon-framework.jar                      jboss-jaxrpc.jar
  commons-httpclient.jar                    jbossjmx-ant.jar
  commons-logging.jar                       jboss-jsr77-client.jar
  concurrent.jar                            jbossmq-client.jar
  ejb3-persistence.jar                      jboss-remoting.jar
  getopt.jar                                jboss-saaj.jar
  hibernate-annotations.jar                 jboss-serialization.jar
  hibernate-client.jar                      jboss-srp-client.jar
  jacorb.jar                                jboss-system-client.jar
  javassist.jar                             jbossws-client.jar
  javax.servlet.jar                         jboss-xml-binding.jar
  jbossall-client.jar                       jmx-client.jar
  jboss-annotations-ejb3.jar                jmx-invoker-adaptor-client.jar
  jboss-aop-jdk50-client.jar                juddisaaj.jar
  jboss-aspect-jdk50-client.jar             log4j.jar
  jboss-client.jar                          logkit.jar
  jboss-common-client.jar                   mail.jar
  jbosscx-client.jar                        scout.jar
  jboss-deployment.jar                      trove.jar
  jboss-ejb3-client.jar                     wsdl4j.jar
  jboss-ejb3x.jar                           xmlsec.jar


JacORB

  antlr-2.7.2.jar                           logkit-1.2.jar
  avalon-framework-4.1.5.jar                picocontainer-1.2.jar
  backport-util-concurrent.jar              wrapper.dll
  idl.jar                                   wrapper-3.1.0.jar
  jacorb.jar




                                  - 263 -
                                         Anhänge


Anhang D: Performance-Messung
Durch die Möglichkeit der Geschwindigkeitsmessung in der Buchhaltungsoberfläche, die
1000 Buchungen anlegt und wieder löscht, ist eine grobe Aussage über die Performance der
verschiedenen Technologien möglich.


Datenhaltung

Da bei der Datenhaltung die Zugriffe auf die Konten- und Buchungsobjekte fast immer direkt
im Speicher stattfinden, ist die Dauer der Geschwindigkeitsmessung meist konstant. Die Ab-
weichungen ergeben sich hier bei der Zeit die nötig ist, um die Daten zu Laden bzw. zu Spei-
chern, wozu jeweils die von der Utility-Klasse erzeugten Testdatensätze verwendet werden.

Technologie             Laden               Speichern          Geschwindigkeitstest
Local                   00:00:010           00:00:010          00:00:020
Serialize               00:00:060           00:00:010          00:00:020
JAXB (V1)               00:00:600           00:00:030          00:00:020
JAXB (V2)               00:00:700           00:00:030          00:00:020
MySQL                   00:00:030           00:00:010          00:05:400
Hibernate (V1)          00:13:130           00:00:010          02:40:750 (mit commit())
Hibernate (V2)          00:00:240           00:00:020          00:00:060

Die hohen Werte bei Hibernate (V1) ergeben sich dadurch, daß nach jeden Aufruf manuell ein
commit() ausgeführt wird. Bei der zweiten Variante erfolgen die Zugriffe nur im Speicher.



Transport

Die Transport-Technologien werden alle mit einer Instanz der lokalen Datenhaltung auf der
Serverseite getestet. Server und Client laufen dabei beide auf demselben Rechner.

Technologie             Verbinden           Schließen          Geschwindigkeitstest
Socket                  00:00:020           00:00:010          00:03:525
RMI                     00:00:300           00:00:010          00:03:060
CORBA                   00:00:400           00:00:010          00:05:870
JacORB                  00:30:410           00:00:010          00:02:460

Auffallend ist die deutlich längere Ladezeit beim JacORB, der dafür später im laufenden Be-
trieb eine bessere Performance erzielt.


Enterprise JavaBeans

Hier kommen die Enterprise JavaBeans auf dem JBoss-Application-Server mit Hibernate und
einer MySQL-Datenbank zum Einsatz.

Technologie             Verbinden           Schließen          Geschwindigkeitstest
JBoss - EJB             00:00:930           00:00:010          02:37:497
JBoss - Web-Services    00:00:810           00:00:010          04:24:410

                                          - 264 -
                                          Anhänge


Anhang E: WSDL-Datei für den Buchhaltungsservice
<?xml version="1.0" encoding="UTF-8"?>
<definitions name='WebAccountigService'
             targetNamespace='http://www.fh-regensburg.de/vs_iw/webservice'
             xmlns='http://schemas.xmlsoap.org/wsdl/'
             xmlns:ns1='http://www.fh-regensburg.de/vs_iw/webservice/types'
             xmlns:soap='http://schemas.xmlsoap.org/wsdl/soap/'
             xmlns:tns='http://www.fh-regensburg.de/vs_iw/webservice'
             xmlns:xsd='http://www.w3.org/2001/XMLSchema'>
 <types>
  <schema targetNamespace='http://www.fh-regensburg.de/vs_iw/webservice/types'
          xmlns='http://www.w3.org/2001/XMLSchema'
          xmlns:soap11-enc='http://schemas.xmlsoap.org/soap/encoding/'
          xmlns:tns='http://www.fh-regensburg.de/vs_iw/webservice/types'
          xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>
   <complexType name='AccountingException'>
    <sequence>
     <element name='message' nillable='true' type='string'/>
    </sequence>
   </complexType>
   <complexType name='IOException'>
    <sequence>
     <element name='message' nillable='true' type='string'/>
    </sequence>
   </complexType>
   <complexType name='Integer.Array'>
    <sequence>
     <element maxOccurs='unbounded' minOccurs='0' name='value' nillable='true' type='int'/>
    </sequence>
   </complexType>
   <element name='AccountingException' type='tns:AccountingException'/>
   <element name='IOException' type='tns:IOException'/>
  </schema>
 </types>
 <message name='IWebAccountingService_calculateBalance'>
  <part name='Integer_1' type='xsd:int'/>
 </message>
 <message name='IWebAccountingService_calculateBalanceResponse'>
  <part name='result' type='xsd:double'/>
 </message>
 <message name='IOException'>
  <part element='ns1:IOException' name='IOException'/>
 </message>
 <message name='AccountingException'>
  <part element='ns1:AccountingException' name='AccountingException'/>
 </message>
 <message name='IWebAccountingService_createAccount'>
  <part name='Integer_1' type='xsd:int'/>
  <part name='int_2' type='xsd:int'/>
  <part name='String_3' type='xsd:string'/>
 </message>
 <message name='IWebAccountingService_createAccountResponse'/>
 <message name='IWebAccountingService_createAccountingEntry'>
  <part name='Integer_1' type='xsd:int'/>
  <part name='Integer_2' type='xsd:int'/>
  <part name='Double_3' type='xsd:double'/>
  <part name='String_4' type='xsd:string'/>
 </message>
 <message name='IWebAccountingService_createAccountingEntryResponse'>
  <part name='result' type='xsd:int'/>
 </message>
 <message name='IWebAccountingService_getAccountingEntries'/>
 <message name='IWebAccountingService_getAccountingEntriesResponse'>
  <part name='result' type='ns1:Integer.Array'/>
 </message>
 <message name='IWebAccountingService_getAccounts'/>
 <message name='IWebAccountingService_getAccountsResponse'>
  <part name='result' type='ns1:Integer.Array'/>
 </message>
 <message name='IWebAccountingService_getAmount'>
  <part name='Integer_1' type='xsd:int'/>
 </message>
 <message name='IWebAccountingService_getAmountResponse'>
  <part name='result' type='xsd:double'/>
 </message>
 <message name='IWebAccountingService_getCreditAccount'>


                                           - 265 -
                                         Anhänge

 <part name='Integer_1' type='xsd:int'/>
</message>
<message name='IWebAccountingService_getCreditAccountResponse'>
 <part name='result' type='xsd:int'/>
</message>
<message name='IWebAccountingService_getCreditEntries'>
 <part name='Integer_1' type='xsd:int'/>
</message>
<message name='IWebAccountingService_getCreditEntriesResponse'>
 <part name='result' type='ns1:Integer.Array'/>
</message>
<message name='IWebAccountingService_getDebitAccount'>
 <part name='Integer_1' type='xsd:int'/>
</message>
<message name='IWebAccountingService_getDebitAccountResponse'>
 <part name='result' type='xsd:int'/>
</message>
<message name='IWebAccountingService_getDebitEntries'>
 <part name='Integer_1' type='xsd:int'/>
</message>
<message name='IWebAccountingService_getDebitEntriesResponse'>
 <part name='result' type='ns1:Integer.Array'/>
</message>
<message name='IWebAccountingService_getDescription'>
 <part name='Integer_1' type='xsd:int'/>
</message>
<message name='IWebAccountingService_getDescriptionResponse'>
 <part name='result' type='xsd:string'/>
</message>
<message name='IWebAccountingService_getText'>
 <part name='Integer_1' type='xsd:int'/>
</message>
<message name='IWebAccountingService_getTextResponse'>
 <part name='result' type='xsd:string'/>
</message>
<message name='IWebAccountingService_getType'>
 <part name='Integer_1' type='xsd:int'/>
</message>
<message name='IWebAccountingService_getTypeResponse'>
 <part name='result' type='xsd:int'/>
</message>
<message name='IWebAccountingService_removeAccount'>
 <part name='Integer_1' type='xsd:int'/>
</message>
<message name='IWebAccountingService_removeAccountResponse'/>
<message name='IWebAccountingService_removeAccountingEntry'>
 <part name='Integer_1' type='xsd:int'/>
</message>
<message name='IWebAccountingService_removeAccountingEntryResponse'/>
<message name='IWebAccountingService_setAmount'>
 <part name='Integer_1' type='xsd:int'/>
 <part name='Double_2' type='xsd:double'/>
</message>
<message name='IWebAccountingService_setAmountResponse'/>
<message name='IWebAccountingService_setCreditAccount'>
 <part name='Integer_1' type='xsd:int'/>
 <part name='Integer_2' type='xsd:int'/>
</message>
<message name='IWebAccountingService_setCreditAccountResponse'/>
<message name='IWebAccountingService_setDebitAccount'>
 <part name='Integer_1' type='xsd:int'/>
 <part name='Integer_2' type='xsd:int'/>
</message>
<message name='IWebAccountingService_setDebitAccountResponse'/>
<message name='IWebAccountingService_setDescription'>
 <part name='Integer_1' type='xsd:int'/>
 <part name='String_2' type='xsd:string'/>
</message>
<message name='IWebAccountingService_setDescriptionResponse'/>
<message name='IWebAccountingService_setText'>
 <part name='Integer_1' type='xsd:int'/>
 <part name='String_2' type='xsd:string'/>
</message>
<message name='IWebAccountingService_setTextResponse'/>
<message name='IWebAccountingService_setType'>
 <part name='Integer_1' type='xsd:int'/>
 <part name='int_2' type='xsd:int'/>
</message>


                                          - 266 -
                                         Anhänge

<message name='IWebAccountingService_setTypeResponse'/>
<portType name='IWebAccountingService'>
 <operation name='calculateBalance' parameterOrder='Integer_1'>
  <input message='tns:IWebAccountingService_calculateBalance'/>
  <output message='tns:IWebAccountingService_calculateBalanceResponse'/>
  <fault message='tns:IOException' name='IOException'/>
  <fault message='tns:AccountingException' name='AccountingException'/>
 </operation>
 <operation name='createAccount' parameterOrder='Integer_1 int_2 String_3'>
  <input message='tns:IWebAccountingService_createAccount'/>
  <output message='tns:IWebAccountingService_createAccountResponse'/>
  <fault message='tns:IOException' name='IOException'/>
  <fault message='tns:AccountingException' name='AccountingException'/>
 </operation>
 <operation name='createAccountingEntry'
            parameterOrder='Integer_1 Integer_2 Double_3 String_4'>
  <input message='tns:IWebAccountingService_createAccountingEntry'/>
  <output message='tns:IWebAccountingService_createAccountingEntryResponse'/>
  <fault message='tns:IOException' name='IOException'/>
  <fault message='tns:AccountingException' name='AccountingException'/>
 </operation>
 <operation name='getAccountingEntries'>
  <input message='tns:IWebAccountingService_getAccountingEntries'/>
  <output message='tns:IWebAccountingService_getAccountingEntriesResponse'/>
  <fault message='tns:IOException' name='IOException'/>
 </operation>
 <operation name='getAccounts'>
  <input message='tns:IWebAccountingService_getAccounts'/>
  <output message='tns:IWebAccountingService_getAccountsResponse'/>
  <fault message='tns:IOException' name='IOException'/>
 </operation>
 <operation name='getAmount' parameterOrder='Integer_1'>
  <input message='tns:IWebAccountingService_getAmount'/>
  <output message='tns:IWebAccountingService_getAmountResponse'/>
  <fault message='tns:IOException' name='IOException'/>
 </operation>
 <operation name='getCreditAccount' parameterOrder='Integer_1'>
  <input message='tns:IWebAccountingService_getCreditAccount'/>
  <output message='tns:IWebAccountingService_getCreditAccountResponse'/>
  <fault message='tns:IOException' name='IOException'/>
 </operation>
 <operation name='getCreditEntries' parameterOrder='Integer_1'>
  <input message='tns:IWebAccountingService_getCreditEntries'/>
  <output message='tns:IWebAccountingService_getCreditEntriesResponse'/>
  <fault message='tns:IOException' name='IOException'/>
 </operation>
 <operation name='getDebitAccount' parameterOrder='Integer_1'>
  <input message='tns:IWebAccountingService_getDebitAccount'/>
  <output message='tns:IWebAccountingService_getDebitAccountResponse'/>
  <fault message='tns:IOException' name='IOException'/>
 </operation>
 <operation name='getDebitEntries' parameterOrder='Integer_1'>
  <input message='tns:IWebAccountingService_getDebitEntries'/>
  <output message='tns:IWebAccountingService_getDebitEntriesResponse'/>
  <fault message='tns:IOException' name='IOException'/>
 </operation>
 <operation name='getDescription' parameterOrder='Integer_1'>
  <input message='tns:IWebAccountingService_getDescription'/>
  <output message='tns:IWebAccountingService_getDescriptionResponse'/>
  <fault message='tns:IOException' name='IOException'/>
 </operation>
 <operation name='getText' parameterOrder='Integer_1'>
  <input message='tns:IWebAccountingService_getText'/>
  <output message='tns:IWebAccountingService_getTextResponse'/>
  <fault message='tns:IOException' name='IOException'/>
 </operation>
 <operation name='getType' parameterOrder='Integer_1'>
  <input message='tns:IWebAccountingService_getType'/>
  <output message='tns:IWebAccountingService_getTypeResponse'/>
  <fault message='tns:IOException' name='IOException'/>
 </operation>
 <operation name='removeAccount' parameterOrder='Integer_1'>
  <input message='tns:IWebAccountingService_removeAccount'/>
  <output message='tns:IWebAccountingService_removeAccountResponse'/>
  <fault message='tns:IOException' name='IOException'/>
  <fault message='tns:AccountingException' name='AccountingException'/>
 </operation>
 <operation name='removeAccountingEntry' parameterOrder='Integer_1'>


                                          - 267 -
                                         Anhänge

  <input message='tns:IWebAccountingService_removeAccountingEntry'/>
  <output message='tns:IWebAccountingService_removeAccountingEntryResponse'/>
  <fault message='tns:IOException' name='IOException'/>
  <fault message='tns:AccountingException' name='AccountingException'/>
 </operation>
 <operation name='setAmount' parameterOrder='Integer_1 Double_2'>
  <input message='tns:IWebAccountingService_setAmount'/>
  <output message='tns:IWebAccountingService_setAmountResponse'/>
  <fault message='tns:IOException' name='IOException'/>
 </operation>
 <operation name='setCreditAccount' parameterOrder='Integer_1 Integer_2'>
  <input message='tns:IWebAccountingService_setCreditAccount'/>
  <output message='tns:IWebAccountingService_setCreditAccountResponse'/>
  <fault message='tns:IOException' name='IOException'/>
  <fault message='tns:AccountingException' name='AccountingException'/>
 </operation>
 <operation name='setDebitAccount' parameterOrder='Integer_1 Integer_2'>
  <input message='tns:IWebAccountingService_setDebitAccount'/>
  <output message='tns:IWebAccountingService_setDebitAccountResponse'/>
  <fault message='tns:IOException' name='IOException'/>
  <fault message='tns:AccountingException' name='AccountingException'/>
 </operation>
 <operation name='setDescription' parameterOrder='Integer_1 String_2'>
  <input message='tns:IWebAccountingService_setDescription'/>
  <output message='tns:IWebAccountingService_setDescriptionResponse'/>
  <fault message='tns:IOException' name='IOException'/>
 </operation>
 <operation name='setText' parameterOrder='Integer_1 String_2'>
  <input message='tns:IWebAccountingService_setText'/>
  <output message='tns:IWebAccountingService_setTextResponse'/>
  <fault message='tns:IOException' name='IOException'/>
 </operation>
 <operation name='setType' parameterOrder='Integer_1 int_2'>
  <input message='tns:IWebAccountingService_setType'/>
  <output message='tns:IWebAccountingService_setTypeResponse'/>
  <fault message='tns:IOException' name='IOException'/>
 </operation>
</portType>
<binding name='IWebAccountingServiceBinding' type='tns:IWebAccountingService'>
 <soap:binding style='rpc' transport='http://schemas.xmlsoap.org/soap/http'/>
 <operation name='calculateBalance'>
  <soap:operation soapAction=''/>
  <input>
   <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice' use='literal'/>
  </input>
  <output>
   <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice' use='literal'/>
  </output>
  <fault name='IOException'>
   <soap:fault name='IOException' use='literal'/>
  </fault>
  <fault name='AccountingException'>
   <soap:fault name='AccountingException' use='literal'/>
  </fault>
 </operation>
 <operation name='createAccount'>
  <soap:operation soapAction=''/>
  <input>
   <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice' use='literal'/>
  </input>
  <output>
   <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice' use='literal'/>
  </output>
  <fault name='IOException'>
   <soap:fault name='IOException' use='literal'/>
  </fault>
  <fault name='AccountingException'>
   <soap:fault name='AccountingException' use='literal'/>
  </fault>
 </operation>
 <operation name='createAccountingEntry'>
  <soap:operation soapAction=''/>
  <input>
   <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice' use='literal'/>
  </input>
  <output>
   <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice' use='literal'/>
  </output>


                                          - 268 -
                                        Anhänge

 <fault name='IOException'>
  <soap:fault name='IOException' use='literal'/>
 </fault>
 <fault name='AccountingException'>
  <soap:fault name='AccountingException' use='literal'/>
 </fault>
</operation>
<operation name='getAccountingEntries'>
 <soap:operation soapAction=''/>
 <input>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </input>
 <output>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </output>
 <fault name='IOException'>
  <soap:fault name='IOException' use='literal'/>
 </fault>
</operation>
<operation name='getAccounts'>
 <soap:operation soapAction=''/>
 <input>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </input>
 <output>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </output>
 <fault name='IOException'>
  <soap:fault name='IOException' use='literal'/>
 </fault>
</operation>
<operation name='getAmount'>
 <soap:operation soapAction=''/>
 <input>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </input>
 <output>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </output>
 <fault name='IOException'>
  <soap:fault name='IOException' use='literal'/>
 </fault>
</operation>
<operation name='getCreditAccount'>
 <soap:operation soapAction=''/>
 <input>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </input>
 <output>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </output>
 <fault name='IOException'>
  <soap:fault name='IOException' use='literal'/>
 </fault>
</operation>
<operation name='getCreditEntries'>
 <soap:operation soapAction=''/>
 <input>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </input>
 <output>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </output>
 <fault name='IOException'>
  <soap:fault name='IOException' use='literal'/>
 </fault>
</operation>
<operation name='getDebitAccount'>
 <soap:operation soapAction=''/>
 <input>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </input>
 <output>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </output>
 <fault name='IOException'>
  <soap:fault name='IOException' use='literal'/>
 </fault>


                                         - 269 -
                                        Anhänge

</operation>
<operation name='getDebitEntries'>
 <soap:operation soapAction=''/>
 <input>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </input>
 <output>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </output>
 <fault name='IOException'>
  <soap:fault name='IOException' use='literal'/>
 </fault>
</operation>
<operation name='getDescription'>
 <soap:operation soapAction=''/>
 <input>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </input>
 <output>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </output>
 <fault name='IOException'>
  <soap:fault name='IOException' use='literal'/>
 </fault>
</operation>
<operation name='getText'>
 <soap:operation soapAction=''/>
 <input>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </input>
 <output>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </output>
 <fault name='IOException'>
  <soap:fault name='IOException' use='literal'/>
 </fault>
</operation>
<operation name='getType'>
 <soap:operation soapAction=''/>
 <input>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </input>
 <output>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </output>
 <fault name='IOException'>
  <soap:fault name='IOException' use='literal'/>
 </fault>
</operation>
<operation name='removeAccount'>
 <soap:operation soapAction=''/>
 <input>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </input>
 <output>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </output>
 <fault name='IOException'>
  <soap:fault name='IOException' use='literal'/>
 </fault>
 <fault name='AccountingException'>
  <soap:fault name='AccountingException' use='literal'/>
 </fault>
</operation>
<operation name='removeAccountingEntry'>
 <soap:operation soapAction=''/>
 <input>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </input>
 <output>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </output>
 <fault name='IOException'>
  <soap:fault name='IOException' use='literal'/>
 </fault>
 <fault name='AccountingException'>
  <soap:fault name='AccountingException' use='literal'/>
 </fault>


                                         - 270 -
                                        Anhänge

</operation>
<operation name='setAmount'>
 <soap:operation soapAction=''/>
 <input>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </input>
 <output>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </output>
 <fault name='IOException'>
  <soap:fault name='IOException' use='literal'/>
 </fault>
</operation>
<operation name='setCreditAccount'>
 <soap:operation soapAction=''/>
 <input>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </input>
 <output>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </output>
 <fault name='IOException'>
  <soap:fault name='IOException' use='literal'/>
 </fault>
 <fault name='AccountingException'>
  <soap:fault name='AccountingException' use='literal'/>
 </fault>
</operation>
<operation name='setDebitAccount'>
 <soap:operation soapAction=''/>
 <input>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </input>
 <output>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </output>
 <fault name='IOException'>
  <soap:fault name='IOException' use='literal'/>
 </fault>
 <fault name='AccountingException'>
  <soap:fault name='AccountingException' use='literal'/>
 </fault>
</operation>
<operation name='setDescription'>
 <soap:operation soapAction=''/>
 <input>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </input>
 <output>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </output>
 <fault name='IOException'>
  <soap:fault name='IOException' use='literal'/>
 </fault>
</operation>
<operation name='setText'>
 <soap:operation soapAction=''/>
 <input>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </input>
 <output>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </output>
 <fault name='IOException'>
  <soap:fault name='IOException' use='literal'/>
 </fault>
</operation>
<operation name='setType'>
 <soap:operation soapAction=''/>
 <input>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </input>
 <output>
  <soap:body namespace='http://www.fh-regensburg.de/vs_iw/webservice'   use='literal'/>
 </output>
 <fault name='IOException'>
  <soap:fault name='IOException' use='literal'/>
 </fault>


                                         - 271 -
                                          Anhänge

  </operation>
 </binding>
 <service name='WebAccountigService'>
  <port binding='tns:IWebAccountingServiceBinding' name='IWebAccountingServicePort'>
   <soap:address location='http://localhost:8080/WebAccountingService'/>
  </port>
 </service>
</definitions>




                                           - 272 -
                                  Anhänge


Anhang F: Changelog der Anwendung
Version 2.0:
  - Generische Adapterschicht zur Reduzierung von redundantem Code
  - Generische Vorgänge mit Callback-Schnittstellen
  - FiBu-Schnittstellen und Geschäftslogik für J2EE-Anwendungen angepasst
  - EJB-Implementierung angepasst
  - Schnittstelle zur Entkopplung der Geschäftslogik
  - Vollständiger eigener Klassenlader für den Proxy-Suchvorgang

Version 1.6:
  - Zusammengefaßte Methodenaufrufe getrennt
  - Eigener Vorgang mit separatem Thread für jeden Aufruf
  - Adapter-Schicht in eigenes Paket ausgelagert
  - Bessere Entkopplung der Oberfläche durch die Adapter-Schicht
  - Fehler bei der Anzeige von Adapater-Objekten mit null-Werten behoben

Version 1.5:
  - Absolutes Layout durch Komponenten mit Layout-Manager ersetzt
  - Dynamische Unterstützung von mehreren Look & Feel Auswahlmöglichkeiten
  - Weitere Einstellungen im Menu
  - Allgemeine Fehlerkorrekturen
  - Threading in eigenes Paket ausgelagert
  - Verbesserter Code durch Eclipse-Plugins Checkstyle und Findbugs

Version 1.2:
  - Fängt Ausnahmen besser ab
  - Komplett überarbeitetes Laden von Proxy-Klassen
  - Eigener Klassenlader mit Listener-Schnittstelle für Lader-Ereignisse
  - Decoder für class-Dateien mit eigener Reflection-Schnittstelle
  - Menu-Einstellungen werden automatisch in Properties-Datei gespeichert

Version 1.1.2:
  - Version zur Herausgabe in der Vorlesung
  - Fängt mehr Ausnahmen ab
  - Verbesserung des Ladens von Proxy-Klassen
  - Beim Laden von Buchungen und Konten werden mehrere Methodenaufrufe
    zusammengefasst
  - Leicht abgeänderte Testfunktionen

Version 1.1:
  - Entkoppelt die Schnittstellenaufrufe in separaten Threads
  - Kann Proxy-Klassen laden, die in einem Unterverzeichnis und
    gleichzeitig im CLASSPATH vorhanden sind
  - Eine Implementierung für jede Technologie
  - Testfunktionen für die Implementierungen

Version 1.0:
  - Erste vollständig lauffähige Version
  - Oberfläche kann alle FiBu-Methoden ansprechen
  - Feste Bindung an die Schnittstellen




                                   - 273 -