This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Dietmar Abt s Maste rkurs Clien t/Server-Programm ierung mit Java
Dietmar Abts
Masterkurs ClientjServerProgrammierung mit Java Anwendungen entwickeln mit Standard-Technologien: JDBC, UDP, TCP, HTTP, XM L-RPC, RMI , JMS und JAX-WS Mit 81 Abbildungen 3., erweiterte Auflage STUDIUM
VIEWEG+ TEUBNER
Bibliografische Informationd der Deutschen Nationalb ibliothek Oie Deutsche Nationalbibliothek verzeichnet diese Publikat ion in der Deutsch en Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über abrufbar.
Das in diesem Werk enthal tene Programm-Material ist mit keiner Verpfl ichtung oder Garantie irgendeiner Art verbunden . Der Autor übern immt infolgedessen keine Verantwortung und wird keine daraus folgende oder sonstige Haftung übernehmen, die auf irgendeine Art aus der Benutzung dieses Programm-Materials oder Teilen davon entsteht. Höchste inhaltliche und technische Qualität unserer Produkte ist unser Ziel. Bei der Produktion und Auslieferung unserer Bücher wollen wir die Umwelt schonen: Dieses Buch ist auf säurefreiem und chlorfrei gebleichtem Papier gedruckt. Oie Einschweißfolie besteht aus Polyäthylen und damit aus organischen Grunds toffen, die weder bei der Herstellung noch bei der Verbrennung Schadstoffe freisetzen.
1. Auflage 2003 Diese Auflage erschien unter dem Titel 2. Auflage 2007 3., erwe iter te Auflage 2010
Vorwort zur dritten Auflage Komponenten verteilter Anwendungen kooperieren im Netz miteinander und versuchen dabei die Ressourcen der beteiligten Systeme optimal zu nutzen. Eine sehr verbreitete Anwendung dieser Art ist das World Wide Web. Verteilte Systeme stellen aber auch hohe Anforderungen an die Softwareentwickler. Diese müssen sich mit Problemen der Kommunikation und Koordination nebenläufiger Prozesse in einem heterogenen Umfeld beschäftigen. Moderne Programmierkonzepte, Frameworks und eine ausgereifte technische Infrastruktur bieten Unterstützung bei der Entwicklung verteilter Anwendungen. Das vorliegende Buch bietet eine kompakte Einführung in aktuelle Technologien auf der Basis von Java SE mit zahlreichen Programmbeispielen und übungsaufgaben. Die verschiedenen Themen können mit Grundkenntnissen der Programmiersprache Java erarbeitet werden. Der Quellcode von über 110 Programmbeispielen (inkl, Lösungen zu den Aufgaben) ist im Internet auf der Website des Verlags direkt neben den bibliographischen Angaben zu diesem Buch verfügbar,
http:/ /www. viewegteubner.de Gegenüber der zweiten Auflage "WUrden zahlreiche Verbesserungen vorgenommen und neue Programmbeispiele und Aufgaben hinzugefügt. Sie sind für die aktuellen Versionen der hier eingesetzten Produkte und Frameworks CMySQL. Apache Derby. Apache Tomcat, Apache XML-RPC. Apache ActiveMQ. Metro Web Service Stack) lauffähig.
Einige neue Themen "WUrden aufgenommen: •
Bearbeitung von JüBC-Rowsets ohne Verbindung zur Datenbank,
•
Gruppenkommunikation mit Multicasting,
•
Nutzung des Pakets com.sun.net.httpser ver zur Implementierung von Webservern,
•
Nachrichtenfilter und Transaktionssteuerung bei JMS und
•
Entwicklung von Web Services mit JAX-WS.
Danken möchte ich zum Schluss Frau Dr. Christel Roß vom Lektorat IT für die gute Zusammenarbeit sowie meinen Leserinnen und Lesern, Studierenden und Fachkollegen für die guten Vorschläge. die in dieser Auflage berücksichtigt wurden. Ich hoffe. dass Sie viel Spaß bei der Erarbeitung des Stoffes und der Entwicklung eigener Programme haben. Ratingen, Mai 2010
Dietmar Abts
abts@hs nlederrhel n.de
Inhaltsverzeichnis 1
Einleitung und Grundlagen
1
1.1
Vorbemerkungen
1
1.2
Verteilte Systeme
5
1.3
Das Client/Server-Modell
8
1.4
Mehrstufige Architekturen
11
1.5
Middleware und Transparenz
13
1.6
Grundbegriffe des lntemets
18
1.7
Die Klasse InetAddress
23
1.8
Aufgaben
25
2
Datenbankanwendungen mit]DBC
27
2.1
Die Architektur von ]DBC-Anwendungen
27
2.2
Erste Beispiele Die Beispiel-Datenbank 2.2.1 Verbindungsaufbau 2.2.2 Datenbankabfragen mit SELECT 2.2.3
31 31 33 37
2.3
]DBC-Datentypen
41
2.4
Ausführung von SQL-Anweisungen Transaktionen 2.4.1 2.4.2 Datenbankänderungen Prepared Statements 2.4.3
45 45 46 55
2.5
Ein einfaches Frontend für SQL-Datenbanken
58
2.6
Speicherung großer Objekte
64
2.7
Navigation und Änderungen in der Ergebnismenge
68
2.8
CachedRowSet - eine verbindungslose Ergebnismenge
74
2.9
Exkurs, XML-Dokumente aus SQL-Abfragen erzeugen
80
2.10
Exkurs, Stored Procedures
91
2.11
Aufgaben
95
3
Verbindungslose Konuuunikation mit VDP
99
3.1
Das Protokoll DDP
99
3.2
DatagramSocket und DatagramPacket
3.3
Ein Echo-Server und -Client
103
3.4
Feste Verbindungen
106
3.5
OnIine-Unterhaltung
109
3.6
Punkt-zu-Mehrpunkt-Verbindungen
113
100
VIII
Inhaltsverzeichnis
3.7
Aufgaben
116
4
Client/Server-Anwendungen mit TCP
117
4.1
Das Protokoll TCP
117
4.2
TCP-Sockets
118
4.3
Ein Echo-Server und -Client
121
4.4
Ein Download-Programm
128
4.5
Ein Chat-Programm
135
4.6
Klassen über das Netz laden
143
4.7
Remote Procedure Call
150
4.8
Thread-Pooling
156
4.9
Ein Framework für TCP-Server
158
4.10
Aufgaben
162
5
Implementierung eines HTTP-Servers
165
5.1
Das Protokoll HTTP
165
5.2
Ein einfacher File-Server
175
5.3
Ein HTTP-Serverfür SQL-Abfragen
179
5.4
Ein einfacher Webserver
186
5.5
Webseiten dynamisch erzeugen
192
5.6
Protokollierung von HTTP-Nachrichten
200
5.7
Aufgaben
202
6
XML Remote Procedure Calls (XML-RPC)
205
6.1
Grundkonzept und ein erstes Beispiel
205
6.2
XML-RPC-Datentypen
212
6.3
Komplexe Datenstrukturen
221
6.4
Dynamische Proxies
233
6.5
Filterung von IP-Adressen
235
6.6
Einbettung von XML-RPC in Apache Tomcat
237
6.7
Nutzung einer Erweiterung in Apache XML-RPC
243
6.8
Aufgaben
247
7
Entfernter Methodenaufruf mit RMI
249
7.1
Remote Method Invocation
249
7.2
Dienstauskunft
259
7.3
Transport by reference
260
7.4
Mobile Agenten
264
Inhaltsverzeichnis
IX
7.5
CaIIbacks
268
7.6
Exkurs, RMI mit HOP
274
7.7
Aufgaben
278
8
Nachrichtendienste mitJMS
281
8.1
Java Message Service
282
8.2
Das Point-to-Point-ModeII
285
8.3
Das Request-Reply-ModeII
297
8.4
Das Publish-Subscribe-ModeII
301
8.5
Dauerhafte Subscriber
305
8.6
Filtern von Nachrichten
310
8.7
Transaktionen
314
8.8
FaIIbeispiel Händler/Lieferant
319
8.9
Aufgaben
325
9
Web Services mitJAX-WS
327
9.1
Basiskonzepte von Web Services
327
9.2
Entwicklungsplattform und API
332
9.3
Ein erstes Beispiel
333
9.4
Ein Web Service zur Artikelverwaltung
338
9.5
Web Services mit Tomcat veröffentlichen
343
9.6
Oneway-Operationen
344
9.7
Asynchrone Aufrufe
346
9.8
Web Services und binäre Daten
351
9.9
Top Down oder Bottom Up
356
9.10
Aufgaben
361
Das Werkzeug Apache Ant
363
Internet-Quellen
367
Literaturhinweise
369
Stichwortverzeichnis
373
1
Einleitung und Grundlagen
1.1
Vorbemerkungen
Java wird heute als sehr gut geeignete Programmiersprache für große Intemet- und Intranet-Anwendungen insbesondere auf der Serverseite eingesetzt. Die Plattformunabhängigkeit, reichhaltige Klassenbibliotheken und die Unterstützung zahlreicher APls (Application Programming lnterl'aces) macht Java zur bevorzugten Sprache für verteilte Anwendungen in einem heterogenen Umfeld, Das vorliegende Buch beschäftigt sich mit der Implementierung Zielsetzung von Client/Server-Anwendungen auf Basis der IntemetProtokolle, Angesichts des Unrt'angs der Themen musste eine Auswahl getroffen werden, die auch in einem vier Semesterwochenstunden umfassenden Kurs behandelt werden kann. Ziel des Buches ist es , über die Themenvielfalt angemessen zu informieren und in die Einzelthemen systematisch mit der nötigen Tiefe einzuführen. Besonderer Wert "WUrde dabei auf praxisnahe Programmbeispiele und Übungsaufgaben gelegt. Dieses Buch ist in neun Kapitel gegliedert, die jeweils einen Aufbau des Buches Schwerpunkt behandeln, Obwohl die Kapitel weitestgehend in sich geschlossen sind und unabhängig voneinander bearbeitet werden können, wird empfohlen, diese in der hier vorgegebenen Reihenfolge durchzuarbeiten. Die vorgestellten Themen werden anhand vollständiger, lauffähiger Programmbeispiele verdeutlicht übungsaufgaben am Ende eines jeden Kapitels regen zur selbständigen Beschäftigung mit dem dargebotenen Stoff an, Das Buch hat folgende Kapitel,
Einleitung und Grundlagen Dieses Kapitel gibt eine Einführung in die verteilte Verarbeitung auf der Basis des Client/Seruer-Modells und stellt mehrstufige Architekturen VOL Sinn und Zweck von Middleware werden an einem ersten Programmbeispiel erläutert. Hier werden auch grundlegende Begriffe des lntemets (wie IPAdressen, Adressauflösung), die zum Verständnis der übrigen Kapitel nötig sind, erklärt 2 Datenbankanwendungen mit JDBC In mehrstufigen Architekturen verteilter Systeme, so z. B. bei dynamischen Wehanwendungen, erzeugt der Applikationsserver Datenbankabfragen und schickt sie zur Ausführung an den Datenbankserver. JDEC ist das Standard-API für den Zugriff auf relationale Datenbanken mittels SQL-Anwei-
2
1
Einleitung und Grundlagen
sungen, die zur Laufzeit dynamisch aufgebaut werden können. Wir beschäftigen uns mit den Grundlagen von ]DBC. die interessante Client/Server-Anwendungen in den folgenden Kapiteln ermöglichen. Ein Exkurs zeigt. wie das Ergebnis einer SQL-Abfrage in ein XML- bzw. HTML-Dokument transformiert werden kann. Ein weiterer Exkurs gibt eine kurze Einführung in die ]DBC-Schnittstelle zu Stared Procedures am Beispiel von MySQL. 3 Verbindungslose Kommunikation mit UDP Das User Datagram Protacol (UDP) ist neben TCP ein wichtiges Transportprotokoll im Internet. Es ermöglicht. mit geringem Aufwand Datenpakete zu versenden, ohne vorher eine Verbindung zum Empfänger aufbauen zu müssen. Wir entwickeln u. a. ein Programm, mit dem Benutzer an verschiedenen Rechnern im Netz eine Unterhaltung online führen können, und stellen das Multicasting vor. 4 ClienVServer-Anwendungen mit TCP Fast alle Internet-Dienste nutzen das Transmission Contral Protacol (TCP). Im Gegensatz zu UDP stellt TCP eine verlässliche Ende-zu-Ende-Verbindung zwischen Sender und Empfänger her. Der Anwendungsentwickler muss sich bei der übertragung eines Datenstroms nicht um die Aufteilung in einzelne Pakete kümmern. Sockets sind die Endpunkte einer TCP-Verbindung. Sie ermöglichen es. das Senden und Empfangen von Daten wie das Schreiben und Lesen von Dateien zu behandeln. Multithreading versetzt den Server in die Lage. mehrere Client-Anfragen gleichzeitig zu bearbeiten. Wir entwickeln u. a. ein Dateiübertragungsprogramm sowie eine Anwendung, mit der ein so genanntes "Chatten" innerhalb einer Gruppe von Teilnehmern möglich ist. Der Einsatz wiederverwendbarer Threads eines Thread-Pools reduziert den Ressourcenverbrauch und kann die Effizienz der Anwendung erhöhen. Das Kapitel wird mit der Vorstellung eines Framewarfes für TCP-Server abgeschlossen. 5 Implementierung eines HTTP-5ervers Dieses Kapitel behandelt die Grundlagen des Anwendungsprotokolls Hypertext Transfer Protacol (HTTP). das die Kommunikationsfunktionalität des Warld Wide Weh definiert. HTTP verwendet auf der Transportschicht TCP. Wir implementieren u. a. einen speziellen HTTP-Server, der beliebige SQL-Anweisungen vom Webbrowser entgegennimmt und ausführt, sowie einen einfachen Webserver zur übertragung von statischen Webseiten und anderen Ressourcen. Dieser Webserver wird dann so erweitert, dass anwendungsspezifische Klassen zur Laufzeit nachgeladen werden können, um mit deren Hilfe Webseiten ad hoc zu generieren.
1.1
Vorbemerkungen
6 XML Remote Procedure Call(XML-RPC) In diesem Kapitel behandeln wir Remote Procedure Calls (RPC) und zeigen anhand des XMI-RPC-Verrahrens , wie HTTP und XML als Datenaustauschformat für den entfernten Prozeduraufruf eingesetzt werden können. Auf dieser Basis implementieren wir einfache Web Services und zeigen, wie die XML-RPC-Verarbeitung in Apache Tomcat eingebunden werden kann. 7 Entfernter Methodenaulruf mit RMI Remote Method Invocation (RMI) ist eine einfache und elegante Art, ClientiServer-Anwendungen in Java zu implementieren, Der Entwickler kann sich ganz auf die fachliche Funktionalität konzentrieren, netzspezifische Details bleiben ihm im Gegensatz zur Socket-Programmierung weitestgehend verborgen, Entfernte Methoden werden prinzipiell wie lokale Methoden aufgerufen, Mit Hilfe der Objektserialisierung kann RMI beliebige Objekte als Aufrufparameter entfernter Methoden übertragen, Dabei kann auch der zugehörige Bytecode, sofern er lokal nicht vorhanden ist, ad hoc transferiert werden, Damit sind dann sehr flexible Anwendungen möglich, mobile Agenten, Anwendungen mit automatischem Rückruf (Callback) bei Eintritt bestimmter Ereignisse, Das Kapitel schließt mit einem Exkurs, der zeigt, wie RMI mit Hilfe des CORBA-Protokolls IIOP CORBA-fähig gemacht werden kann, 8 Nachrichtendienste mit JMS Ein wichtiger Standard für die asynchrone Verteilung von Nachrichten auf der Basis nachrichtenorientierter Middleware ist der Java Message Service UMS), Bekannte Kommunikationsmodelle, um Nachrichten auszutauschen sind: das Pointto-Point-Modell und das Publisb-Subscribe-Modell. In diesem Kapitel werden die wichtigsten Funktionen des JMS-API vorgestellt und anhand vieler Beispiele demonstriert. 9 Web Services mit JAX-WS Neben den Basistechnologien für SOAP-basierte Web Services wird das Java API for XMI Web Services (JAX- WS) vorgestellt, mit dem auf einfache Art und Weise normale Klassen mit Hilfe von geeigneten Annotationen zu Web Services erweitert werden können (Code-First-Ansatz), Neben asynchronen Aufrufen von Web Services wird die Übertragung binärer Daten mit MTOM sowie die Generierung von Quellcode aus dem WSDL-Dokument (Contract-First-Ansatz) vorgestellt
3
1
4
Einleitung und Grundlagen
Erwartungen an
Von den Leserinnen und Lesern werden erwartet:
den Leser
•
Grundlegende Java-Kenntnisse, insbesondere: Konzepte der objektorientierten Programmierung, Dateiverarbeitung und Thread-Programmierung .
•
Grundlegende Kenntnisse auf dem Gebiet der relationalen Datenbanken und SQL.
•
Das Wissen um Internet und World Wide Web und der Umgang mit einem Webbrowser.
•
Grundlagenkenntnisse in HTML und XML.
Buchtipps hierzu finden Sie am Ende des Buches im Abschnitt "Literaturhinweise" . Die Beispiele
Alle Beispielprogramme und Lösungen zu den übungsaufgaben wurden mit Hilfe von Java SE 6 unter Windows Vista entwickelt und im Netz getestet. Selbstverständlich sind die hier vorgestellten Programme ohne Änderung auch auf UNIX!Linux-Systemen lauffähig. Als Entwicklungsumgebung wurde Eclipse eingesetzt. Selbstverständlich kann auch jede andere Java-Entwicklungsumgebung genutzt werden.
Werkzeug Ant
Zur Vereinfachung der Programmentwicklung wurde das BuildWerkzeug Ant der Apache Software Foundation genutzt (siehe Quellenverzeichnis am Ende des Buches). Im Anhang befindet sich eine kurze Anleitung zur Installation und Nutzung von Ant. Weitere Einzelheiten, z. B. zu den eingesetzten Datenbanksystemen und JDBC-Treibem. können den entsprechenden Kapiteln entnommen werden.
Download
Sämtliche Programme und Lösungen stehen im Internet zur Verfügung und können heruntergeladen werden. Hinweise zu weiteren Tools und Klassenbibliotheken erfolgen in den Kapiteln. in denen sie erstmalig benutzt werden. Das Quellenverzeichnis am Ende des Buches enthält die Bezugsquellen.
1.2
Verteilte Systeme
1.2
5
Verteilte Systeme
Umfassende gesellschaftliche und wirtschaftliche Entwicklungen zwingen Unternehmen, sich flexibel an Veränderungen anzupassen und schnell auf neue Marktsituationen zu reagieren. Betrieblichen Anwendungssystemen kommt hier eine besondere Bedeutung zu. Wie sind die Anwendungssysteme zu implementieren, um höchstmögliche Flexibilität, Verlässlichkeit und Anpassbarkeit zu erreichen?
Monolithische Systeme (alle Anwendungen laufen auf einem Rechner) sind nur noch bedingt geeignet Heute ist die Strukturierung der Software in Client und Server weit verbreitet. Die folgenden Faktoren haben die fundamentalen Änderungen wesentlich beeinflusst: •
Leistungsexplosion in der Mikroprozessortechnik,
•
schnelle Datennetze,
•
Fortschritte in der Softwaretechnik,
•
Abkehr von hierarchischen Organisationsstrukturen,
•
Bedürfnis nach Integration bisher nicht integrierter Systeme,
•
Internet-Technologie.
Waren die 1960er Jahre noch durch Batch-Processing-Systeme, Lochstreifen und Lochkarten geprägt, konnten Anfang der 1970er Jahre in so genannten Timesharing-Systemen Transaktionen im Dialog gestartet werden, Minicomputer (UNIX-Systeme) erschienen als zweiter Gerätetyp neben dem Host. Anfang der 1980er Jahre kamen die Personal Computer als Stand-alone-Systeme oder vemetzt im LAN hinzu, Der wichtigste Trend ab 1990 ist durch das Aufkommen von ClientlServer-Systemen für die verteilte Verarbeitung bestimmt. Hierzu zählen insbesondere die heutigen Internet-Anwendungen eWeb-Informationssysteme, E-Business-Anwendungen).
Verteilte Systeme ClientlServer-Systeme Personal Computer TimesharingSysteme Batch-ProcessingSysteme
I 1960
I 1970
1980
1990
2000
Bild 1.1.·
Entwicklungsstufen
6
1
Einleitung und Grundlagen
Jede Anwendung kann in drei wesentliche Schichten eingeteilt werden: 1. Präsentation Diese Schicht (auch Benutzungsschnittstelle genannt) hat die Aufgabe, den Dialog mit dem Benutzer zu steuern sowie Daten und Befehle an das System zu übermitteln.
2. Verarbeitung Diese Schicht enthält die Verarbeitungslogik Hier wird der eigentliche Nutzen der Anwendung erzielt. 3. Datenhaltung Diese Schicht hat die Aufgabe, die Daten abzuspeichem und bei Bedarf wieder zur Verfügung zu stellen. Bild 1.2 zeigt eine Webanwendung, die dem Benutzer das Produktangebot eines Unternehmens präsentiert. Die drei Schichten Präsentation, Verarbeitung und Datenhaltung sind jeweils auf eigenen Rechnern implementiert. Der Webbrowser ist die Benutzungsoberfläche, die die abgefragten Informationen grafisch präsentiert. Der Webserver erzeugt Datenbankabfragen, bereitet die Abfrageergebnisse als Webseite auf und übermittelt diese an den Client. Der Datenhankserver verwaltet den Produktkatalog und führt die Datenbankabfragen aus. Webbrowser, Webserver und Datenbankserver sind autonome Teilsysteme, die koordiniert miteinander kooperieren. Lokales Netz einer Firma
Bild 12.· Beispiel einer
Webanwendung Webbrowser
Webserver
Datenbankserver
Webbrowser
Verteiltes System
Verteilung bedeutet die Zuordnung von Funktionen und Daten auf mehrere Rechner. Ein Anwendungssystem wird dabei in funktionale Komponenten zerlegt, die dann bei der Installation im Netz so verteilt werden, dass die Komponenten optimal unterstützt werden. Abstrakt formuliert ist ein verteiltes System eine Menge von Komponenten, die kooperieren, um eine gemeinsame Aufgabe zu erfüllen.
1.2
Verteilte Systeme
7
Da es im Allgemeinen mehrere Varianten für die Installation der KonftgurationsKomponenten im Netz gibt, besteht das Konfigurationsproblem, problern die logiscbe Struktur der Anwendung auf die pbysiscbe Struktur (Rechner im Netz) so abzubilden, dass bei vorgegebenen Randbedingungen ein maximaler Nutzen erzielt wird. üb eine Verteilung überhaupt möglich ist, entscheidet sich bereits beim Entwurf der Anwendungssoftware. Der Entwurf muss unabhängig von einer späteren physischen Konfiguration sein. Dem Benutzer muss die konkrete physische Aufteilung verborgen bleiben,
logische Struktur
physische Struktur Sachbearbeiter
Sachbearbeiter
Bild 13·
Abbildung der logischen auf die physische Struktur
[5 Kundendal en Produktdaten
Auftragsbearbeitung
Produktdaten
Der Vergleich der verteilten Verarbeitung mit der großrechnerdominierten, zentralen Verarbeitung ergibt folgende Vor- und Nachteile,
•
Besseres Abbild der Realität Vorteile Die Verteilung unterstützt die "schlanke" Organisation. Leistungen werden dort erbracht, wo sie benötigt werden. Daten werden dort erfasst, wo sie im Geschäftsprozess entstehen.
•
Wirtscbaftlicbkeit Teure Ressourcen (Geräte, Softwarekomponenten) stehen mehreren Rechnern zur Verfügung und können gemeinsam genutzt werden. Automatisierte Aufgaben werden möglichst dort ausgeführt, wo sie am wirtschaftlichsten sind.
•
Bessere Lastverteilung Da mehrere Rechner mit jeweils eigenem Betriebssystem arbeiten, kann durch teilweise parallele Verarbeitung die Gesamtbearbeitungszeit verkürzt werden.
•
Bessere Skalierbarkeit Einzelne Komponenten können leichter an einen steigenden Bedarf angepasst werden.
•
Fehlertoleranz Beim Ausfall eines Rechners bleiben die anderen betriebsbe-
1
8
Einleitung und Grundlagen
reit. Die Aufgaben des ausgefallenen Rechners können oft von einem anderen Rechner übernommen werden. Nachteile
•
Höhere Komplexität durch Verteilung und Heterogenität Die Verteilung der Komponenten großer Systeme und die Heterogenität bei Betriebssystemen, Programmiersprachen, Datenformaten und Kommunikationsprotokollen stellen hohe Anforderungen an die Entwickler, wenn das Gesamtsystem überschaubar bleiben soll.
•
Komplexe Netzinfrastrukturen Netzarchitektur und Netzmanagement sind das Rückgrat der Systeme eines Unternehmens. Die Integration heterogener Systeme mit Komponenten unterschiedlicher Hersteller- und Standardarchitekturen stellt hohe Anforderungen an die Administration.
•
Höhere Sicherheitsrisiken Verteilte Systeme bieten mehr Möglichkeiten für unberechtigte Zugriffe. Im Netz übertragene Nachrichten können abgehört oder sogar verändert werden. Der Einsatz von Verschlüsselungsverfahren und Firewall-Systemen sind wirksame Schutzmaßnahmen.
Aufgabe von so genannten Verteilungsplattformen ist es, diese Komplexität beherrschbar zu machen.
1.3
Das Client/Server-Modell
Das ClientlServer-Modell ist das am weitesten verbreitete Modell für die verteilte Verarbeitung. Client/Server
ClientiServer bezeichnet die Beziehung , in der zwei Programme zueinander stehen. Der Client stellt eine Anfrage an den Server, eine gegebene Aufgabe zu erledigen. Der Server erledigt die Aufgabe und liefert das Ergebnis beim Client ab. Ein Kommunikationsdienst verbindet Client und Server miteinander. Ein ClientlServer-System besteht also aus
Eine Softwarearchitektur
•
einem oder mehreren Clients (Auftraggebern),
•
einem Server (Auftragnehmer) und
•
einem Kommunikationsdienst (Netzwerk, Vermittler).
Client und Server sind Programme, die auch auf demselben Rechner laufen können. Sie müssen also nicht notwendig über ein Netz verbunden sein. Das macht deutlich, dass es sich hierbei um eine Software- und keine Hardwarearchitektur handelt. In der Praxis werden natürlich Client und Server auf unterschiedlichen Rechnern verteilt, um die bekannten Vorteile eines verteilten Systems zu erzielen.
1.3
Das Client/Server-Modell
9
Gelegentlich werden auch die Trägersysteme (Rechner), auf de- Hardware-Sicht nen die Programme laufen, als Client bzw. Server bezeichnet. Wir nutzen die Begriffe Client bzw. Server als Homonyme. Aus dem Zusammenhang wird dann klar, welche Bedeutung gemeint ist. Ein Server kann die Erledigung einer Aufgabe weiter delegieren und dazu auf andere Server zugreifen, Er spielt dann selbst die Rolle eines Client, Der Webserver in Bild 1.2 hat diese Doppelfunktion.
Clien!
I.
Hnfrage
'I
1. Anfrage
Clien! 4. Antwort
Bild 1.4.· Client/Server-Modell
Server
Server/ Clien!
2. Anfrage
Server 3. Antwort
Im Folgenden sind charakteristische Merkmale von Client und Server zusammengefasst: •
Ein Programm wird vorübergehend zum Client, wenn es ei- Client-Merkmale nen Dienst von einem anderen Programm (Server) anfordert. Darüber hinaus kann das Frogramm andere Aufgaben lokal ausführen.
•
Der Client läuft in der Regel auf dem Rechner eines Benutzers und leitet den Kontakt mit einem Server aktivein.
•
Der Client kann im Laufe einer Sitzung auf mehrere Server zugreifen, kontaktiert aber aktiv nur jeweils einen Server.
•
Der Server ist ein Programm, das einen bestimmten Dienst Server-Merkmale bereitstellt. Er kann in der Regel gleichzeitig mehrere Clients bedienen.
•
Häufig werden Server automatisch beim Hochfahren des Rechners gestartet und beim Herunterfahren beendet.
•
Der Server wartet passiv auf die Verbindungsaufnahme durch einen Client.
•
Da Server eigenständige Programme sind, können mehrere Server unabhängig voneinander auf einem einzigen Rechner als Trägersystem laufen.
1
10
Einleitung und Grundlagen
• Parallelbetrieb
ist ein grundlegendes Merkmal von Servern. Mehrere Clients können einen bestimmten Dienst in Anspruch nehmen, ohne warten zu müssen, bis der Server mit der Erledigung der laufenden Anfrage fertig ist. In den späteren Java-Programmbeispielen werden die Client-Anfragen jeweils in einem neuen Thread bedient.
Bild 105'
Interaktionsarten
synchrone Kommunikation Client
~
Server
Anfrage
warte auf Antwort
r
Antwort
asynchrone Kommunikation Client
Server
Anfrage arbeite weiter
Rückruf mit Antwort
synchron/asynchron Die Interaktion zwischen Client und Server kann synchron oder asyncbron erfolgen (siehe Bild 1.5).
Bei der synchronen Kommunikation wartet der Client nach Absenden der Anfrage an den Server so lange, bis er eine Rückantwort erhält, und kann dann erst andere Aktivitäten ausführen. Im asyncbronen Fall sendet der Client die Anfrage an den Server und arbeitet sofort weiter. Beim Eintreffen der Rückantwort werden dann bestimmte Ereignisbehandlungsroutinen beim Client aufgernfen. Die durch den Server initiierten Rückrufe (Callbacks) erfordern allerdings zusätzliche Kontrolle beim Client, wenn ungewünschte Unterbrechungen der Arbeit vermieden werden sollen.
1.4
1.4
Mehrstufige Architekturen
11
Mehrstufige Architekturen
Bei einstufigen Architekturen wird die gesamte Anwendung (Präsentation, Verarbeitung und Datenhaltung) auf einem Rechner implementiert. Die Benutzerinteraktion erfolgt über Terminals, die direkt oder über einen Terminal- bzw. Kommunikationsserver an den Rechner angeschlossen sind. Verteilte Anwendungen haben eine mehrstufige Architektur Multi- Tier(Multi-Tier-Architektur) , Beispielsweise werden Applikations- Architekturen und Datenbankserver verschiedenen Ebenen zugeordnet. Bei der zweistufigen Architektur ist die Gesamtanwendung zwei Ebenen zugeordnet: Client und Server. Bild 1.6.· .00
•
Netz
Datenbankserver
Client
Für die Arbeitsteilung zwischen Client und Server existieren verschiedene Alternativen, je nachdem, wo die Schichten Präsentation, Verarbeitung und Datenhaltung angesiedelt sind. Bild 1.7 zeigt fünf Altemativen der Aufgabenverteilung bei der zweistufigen Architektur eines Client/Server-Systems. Von Alternative 1 bis 5 wird immer mehr Funktionalität auf den Client übertragen. Altemative 1 zeigt den Fall einer Webanwendung, bei der HTMLSeiten vom Server generiert und vom Webbrowser angezeigt werden. Ein Beispiel zur Alternative 2 ist eine Webanwendung, bei der eingegebene Daten über ein Applet im Webbrowser an den Server zur Verarbeitung weitergeleitet werden. Bei den Altemativen 3 bis 5 hat der elient einen Teil der Verarbeitung bzw, Datenhaltung selbst übernommen. Alternative 4 zeigt den Fall eines typischen Datenbankservers.
Eine zweistuftge Architektur
1
12 Bild 1.7.· Alternativen der ~ Aufgabenvetteilung
1
eI I
Datenhaltung Verarbeitung
~
rn
Pr äsent atioo
I1 I1 I
Daemettuno Verarbeitung
1 1 1 1
Einleitung und Grundlagen
Datenhaltunq Verarbeitung
II I
Datenh atuno
I1
Verarbeitung
I II II
Datenhaltung
I
Netz
"E
.s iJ
1
I
Pr äsent atioo
II
Präsentat ion
1 1
Verarbeitung Präsentation
2
II II
Präsent ation
4
3
Datenhaltung Verarbeitunq Präsentation
I I I
5
Bei der dreistufigen Architektur wird die Gesamtanwendung auf drei Ebenen aufgeteilt z.B, Client, Applikationsserver und Datenbankserver. Dieses Modell ist auch Basis vieler ERP-Systeme (Enterprise Resümee Planning). Bild 1.8.· Eine dreistuftge Architektur
o
Q Applikationsserver
Datenbankserver
Client
Insbesondere bei Internet- und Intranet-Anwendungen findet man eine vierstufige Architektur, bei der ein Webserver dem Applikationsserver vorgeschaltet ist. Bild 1.9· Eine vierstufige Architektur
In mehrstufigen Architekturen sind die Komponenten eines Anwendungssystems auf potentiell heterogene Trägersysteme in einem Netzwerk verteilt. Um die Komplexität des Gesamtsystems bei der Bedienung oder Entwicklung der Anwendung vor dem Benutzer bzw. Entwickler verborgen zu halten, bedarf es einer geeigneten Software-Infrastruktur, die die Interaktion zwischen den Komponenten in besonderer Weise unterstützt. Die verteilte Verarbeitung hat zu einem neuen Typ systemnaher Software geführt, der so genannten Middleuiare.
Middleware ist Kommunikationssoftware, die den Austausch von Informationen zwischen den verschiedenen Komponenten eines verteilten, heterogenen Systems unterstützt. Die Anwendungen werden dabei von den komplexen Details der internen Vorgänge abgeschirmt Bild 1,10·
Anwendungskomponenten 1
1
1
1
1
1
1
Middleware 1
Middleware ___I
1
1
1
_
Betriebssysteme, Protokolle, Netzwerk In der Informatik nennt man eine Sache transparent, wenn sie "durchsichtig", also nicht sichtbar ist. In verteilten Systemen sollen interne Abläufe und Implementie- Verteilung rungsdetails für den Benutzer oder Anwendungsentwickler nicht verbergen sichtbar sein. Verteilungstransparenz verbirgt die Komplexität verteilter Systeme. Es existieren mehrere untergeordnete Transparenzbegriffe, die jeweils einen bestimmten Aspekt bezeichnen. Beispiele hierfür sind:
•
Ortstransparenz Der Ort einer Ressource ist dem Benutzer nicht bekannt. Er identifiziert sie über einen Namen, der keine Information über ihren Aufenthaltsort enthält
•
ZugrijJstransparenz Die Form des Zugriffs auf eine Ressource ist einheitlich und unabhängig davon, ob die Ressource lokal oder auf einem entfernten Rechner zur Verfügung steht. Unterschiede verschiedener Betriebssysteme und Dateisysteme werden verborgen,
1
14
Einleitung und Grundlagen
•
Nebenläufigkeitstransparenz Der gleichzeitige Zugriff mehrerer Benutzer auf dieselbe Ressource (z.B, Datenbanktabelle) erfolgt ohne gegenseitige Beeinflussung und lässt keine falschen Ergebnisse entstehen.
•
Replikationstransparenz Sind aus Verfügbarkeits- oder Performance-Gründen mehrere Kopien einer Ressource (z. B. eines Datenbestandes) vorhanden, so merkt der Benutzer nicht, ob er auf das Original oder eine Kopie zugreift. Das System sorgt dafür. dass alle Kopien bei Änderungen konsistent bleiben.
•
Migrationstransparenz Ressourcen können von einem Rechner auf einen anderen verlagert werden, ohne dass der Benutzer dies bemerkt.
•
Fehlertransparenz Der Benutzer soll nicht mit allen auftretenden Fehlern im System konfrontiert werden. Das Auftreten von Fehlern und die Fehlerbehebung sollen vor dem Benutzer weitestgehend verborgen sein.
Middleware-Produkte stellen dem Entwickler die für die Anwendung benötigten Funktionen meist in Form eines API (Application Programming Interface) zur Verfügung. DatenbankMiddleware
Datenbank-Middleware ermöglicht den Zugriff auf unterschiedliche Datenbanksysteme, ohne das Anwendungsprogramm beim Wechsel des Datenbanksystems ändern zu müssen. Das JDBCAPI erlaubt es einem Java-Programm. SQL-Anweisungen an beliebige SQL-Datenbanken zu schicken (siehe Kapitel 2). Andere Middleware-Produkte verwenden eine synchrone Verbindung zwischen Client und Server, um Prozeduren nach dem RPC-Modell aufzurufen.
Bild 1.11.·
Server
Client
Entfernter Prozeduraufruf
f(x,y)
z
~
f (5, 101
r:">
y
~
: Middleware
1.5
Middleware und Transparenz
15
Der Remote Procedure Calt (RPC) versteckt den Aufruf einer auf RPC einem anderen Rechner im Netz implementierten Prozedur hinter einem lokalen Prozeduraufruf und bietet damit ein sehr vertrautes Programmiermodell an. Das Programm, das die Prozedur aufruft, agiert als Client, das Programm, das die aufgerufene Prozedur ausführt, als Server.
RMI (Remote Method Invocation) ist die objektorientierte Umsetzung des RPC-Modells für Java-Programme (siehe Kapitel 7), Die folgenden Programmbeispiele zeigen, wie einfach eine Anwendung mit lokalem Methodenaufruf in eine Client/ServerAnwendung mit entferntem Methodenaufruf (RMl) umgewandelt werden kann. Die im Beispiel benutze Methode berechnet die Summe zweier Zahlen, Zunächst die lokale Anwendung, public class AddServer ( public double add(double x, double y) {
return x
Programm 1.1
+ y;
public class AddClient ( public static void main(String[] args) (
AddServer serVlce
=
new AddServer();
System.out.println(service.add(2.3. 5.7));
Die beiden Klassen werden nun mittels eines Interface entkoppelt. Um ein Server-Objekt zu erzeugen, wird eine statische Methode benutzt, die den konkreten Namen der Implementierungsklasse (hier, AddServer) gegenüber dem Client verbirgt (Factory-Methode). public interface Add ( double add(double x. double y);
public class AddServer implements Add ( public double add(double x. double y) return x + y;
Programm 1.2
16
1
Einleitung und Grundlagen
public class AddFactory ( public static Add getAdd() return new AddServer();
public class AddClient ( public static void main(String[] args) ( Add service ~ AddFactory.getAdd(); System.out.println(service.add(2.3. 5.7));
Das Klassendiagramm in Bild 1.12 verdeutlicht die Zusammenhänge. Bild 1.12.·
«i nterface»
Klassendiagramm
Add
zu Programm 1.2
r------------<
! «use» ! !
add ()
i
AddClient
main ()
f::r-!
!«realize» !
i
!
«use»
AddServer add ()
,
: «use»
AddFactory getAdd ()
Die RMI-Version der Anwendung entsteht nun durch leichte Änderung von Programm 1.2. Programm 13
l mpor t java rmi . Remote; lmport java rml.RemoteExceptlon;
lmport ja va.rml .Namlng; lmport java.rmi.Re mote Exception; import java.rmi.server.UnicastRe moteObject; public class AddServer extends UnicastRemoteObject imp le ments Add ( public AddServer() throws RemoteException { } public double add(double x, double y) throws Remote Exception ( return x + y;
public static void main(String[] args) throws Except i on ( AddServer ser ver ~ new AddSer ver(); Naming,rebind("add", server );
import java.rmi.Naming; public class AddFactory ( public static Add getAdd() throws Exception ( return (Add) Naming,lookup("//localhostiadd");
public class AddClient ( public static void main(String[] args) throws Except i on ( Add service ~ AddFactory,getAdd(); System,out,println(ser vice,add (2,3, 5,7));
Add, AddServer und Add Factory importieren Rlvll-Pakete. Das Interface Add ist von java . rmi .Remote abgeleitet, die Methode add enthält eine throws-Klausel mit der Ausnahme ja va. rmi . RelTDte Except ion.
Die Klasse AddServer ist von java. rmi .ser ver. Uni castRemoteObject abgeleitet und besitzt eine mai n-Methode, die das erzeugte Server-Objekt bei einem Verzeichnisdienst registriert.
Die Methode getAdd der Klasse AddFactory erhält ihr Add-Objekt mit Hilfe dieses Verzeichnisdienstes.
17
1
18
Einleitung und Grundlagen
Zur Vereinfachung sollen Client und Server auf demselben Rechner (1 oea1hast) laufen. Zunächst müssen die Sourcen compiliert werden:
mkdir bin javac -d bl n src/* .java Start des Verzeichnisdienstes:
start /0 bin rmiregistry Start des Servers:
start java -cp bin AddServer Aufruf des Client:
java -ep bin AddClient Verzeichnisdienst und Server müssen am Ende mit Strg+C abgebrochen werden.
1.6 Internet
Grundbegriffe des Internets
Das Internet ist ein weltumspannendes Rechnemetz, das aus einer Vielzahl von internationalen und nationalen öffentlichen Netzen sowie privaten lokalen Netzen besteht. Spezielle Kopplungselemente, so genannte Rauter, verbinden diese Teilnetze miteinander und ermöglichen so den Datenaustausch zwischen Rechnern, die sich in unterschiedlichen Teilnetzen befinden. Das Internet ist ein offenes Netz, zu dem Unternehmen, nichtkommerzielle Organisationen sowie Privatpersonen gleichermaßen Zugang haben.
Kommunikationsprotokolle beinhalten Sprach- und Handlungsregeln für den Datenaustausch. Sie sind das Verständigungsmittel in einem Rechnernetz. TCPIlP
TCP/IP (Transmission Control ProtocoVInternet Protocol) bezeichnet eine Familie von einzelnen, aufeinander abgestimmten Protokollen für die Kommunikation im Internet. TCP und IP sind die beiden wichtigsten Protokolle dieser Familie.
Intranet
Im Gegensatz zum offenen Internet ist ein Intranet ein privates, innerbetriebliches Rechnernetz auf Basis der Internet-Protokolle ("privates Internet").
1.6
Grundbegriffe des Internets
19 Bild 1.13' Gtundstruktur
Teilnetz
des Internets
Teilnetz
recoter
recoter
Teilnetz Teilnetz
e octer
Router
Teilnetz
Teilnetz
Zur Beschreibung der Kommunikation werden allgemein Schichtenmodelle eingesetzt. Im Internet werden vier aufeinander aufbauende Schichten unterschieden. Jede Schicht hat ihre eigene Punktionalität, die sie der jeweils höheren Schicht zur Verfügung stellt.
4
Anwendungsschicht
ili
3
Transportschicht
2
Vermittlungsschicht
1
Verbindungsschicht
1. Die Verbindungsschicht umfasst die Netzwerkhardware und Gerätetreiber. Sie ist die Basis für eine zuverlässige physische Verbindung zwischen zwei benachbarten Systemen. 2. Die Vermittlungsschicht enthält das Frotokoll IP (Internet Protocol), das die Internet-Adressen (IF-Adressen) definiert. die als Basis für die Wegwahl im Internet dienen. Einzelne Datenpakete werden vom Sender zum Empfänger über verschiedene Teilnetze des Intemets geleitet. Das hier angesiedelte Frotokoll ARP (Address Resolution Frotocol) übersetzt
Bild 1.14.' TCPIlPSchichtenmodell
1
20
Einleitung und Grundlagen
logische IP-Adressen in physische Adressen (Hardwareadressen) einer Datenstation im lokalen Netz. 3. Die Transportschicht enthält die beiden Protokolle TCP (Transmission Control Protocol) und UDP (User Datagram Protocol).
TCP stellt der Anwendung ein verlässliches . verbindungsorientiertes Protokoll zur Verfügung, Vor der eigentlichen Datenübertragung in Form von Datenpaketen wird eine virtuelle Ende-zu-Ende-Verbindung zwischen Sender und Empfänger aufgebaut. UDP ist ein so genanntes verbindungsloses Protokoll. Datenpakete werden ins Internet geschickt, ohne dass vorher eine Verbindung mit dem Empfänger hergestellt wurde. DDP und TCP werden in den Kapiteln 3 und 4 ausführlich behandelt. 4. Beispiele für die Protokolle der Anwendungsscbicbt sind, SMTP (SimpleMail Transfer Protocol), Telnet; PTP (File Transfer Protocol) und HITP (Hypertext Transfer Protocol), das die Basis für das World Wide Web (WWW) ist. Bild 1.15 zeigt den Ablauf einer Kommunikation zwischen Client und Server, die ihre Daten direkt über TCP austauschen. Bild 1.15·
Datenübertragung mittels TCP
Client
TCP IP Ethernet
Serve r
TCP-Paket
I
---------------------------- ~ I
I
Daten
f---------'
Daten
f---------'
Daten
f---------'
IP·Paket
--------------------- ~ Ethernet-Paket
----------1 Eth ernet I
IP I TCP I
TCP IP Ethernet
Der Client übergibt seine Daten an die Transportschicht. TCP sorgt für die Aufteilung der Daten in Pakete. versieht jeweils die Nutzdaten eines Pakets mit einem Datenkopf (Header) und gibt die Pakete weiter an die Vermittlungsschicht. Die Vermittlungsschicht fügt ihrerseits einen IP-Header hinzu. Schließlich packt die Netzwerkkarte einen Rahmen um die Daten. Beim Empfänger werden in umgekehrter Reihenfolge die Header Schicht für Schicht wieder entfernt und ausgewertet, bevor die Daten an die übergeordnete Schicht weitergegeben werden.
1.6
Grundbegriffe des Intemets
Eine Aufgabe des Protokolls IP ist die Bereitstellung eines Adres- IP-Adressen sierungsschemas, das von den physischen Adressen der Netzwerkhardware unabhängig ist. Jeder Rechner im Internet (genauer: der Rechner als Komponente eines Teilnetzes) hat eine eindeutige IP-Adresse, die in der zur Zeit noch vorherrschenden Version IPv4 aus 32 Bit besteht und durch eine Folge von vier durch Punkte getrennte Zahlen zwischen 0 und 255 dargestellt wird. Eine IP-Adresse besteht aus zwei Teilen: der Netzadresse, die das Netz eindeutig identifiziert, und der Rechneradresse, die den Rechner in diesem Netz eindeutig identifiziert. Offizielle Netzadressen werden zentral vergeben, Rechneradressen können frei vom Netzwerkadministrator der jeweiligen Institution vergeben werden. Drei Adressklassen werden unterschieden: A, Bund C. Adressen der Klasse A nutzen 8 Bit für die Netzadresse (beginnend mit 0). Adressen der Klasse B 16 Bit (beginnend mit 10) und Adressen der Klasse C 24 Bit (beginnend mit 110). Beispiel, Die IP-Adresse 194.94. 124.236 gehört zur Klasse C und identifiziert einen Rechner in dem durch die Netzadresse 194 .94 . 124 . 0 identifizierten Netz. Jeder Rechner im Internet kann sich selbst mit der Adresse 127 . 0 . 0 .1 adressieren.
Private IP-Adressen wie z. B. 10 . 0 . 0 . 0 bis 10 .255 .255 . 255 oder 192 . 168 . O. 0 bis 192 .1 68 .255 .255 werden nie im Intemet geroutet. Jede Organisation kann sie frei im internen Netz verwenden. Um eine Verbindung mit dem Internet zu ermöglichen, werden private Adressen in öffentliche Adressen übersetzt und umgekehrt. Die starre Klasseneinteilung der IP-Adressen wird durch das heute übliche Verfahren CIDR (Classless Inter-Domain Routing) aufgehoben. Hierbei erfolgt die Aufteilung in Netz- und Rechneradresse bitweise mit Hilfe von Subnetzmasken. Die neue Version IPv6 bietet vor allem einen größeren Adress- IPv6 raum. Es werden 128 Bit zur Darstellung der Adresse verwendet. IPv6-Adressen werden hexadezimal notiert, wobei die Adresse in 8 Blöcken zu jeweils 16 Bit unterteilt wird. Führende Nullen innerhalb eines Blockes dürren weggelassen werden. Blöcke werden durch einen Doppelpunkt getrennt. Beispiel, 2001: 0: d5c 7: a2d6: 38bb: 3cd 5: 3f57: fd9b
21
1
22
DNS
Einleitung und Grundlagen
Zur besseren Handhabbarkeit wird das Nummemsystem durch ein Namensystem, dem Domain Name System (DNS), überlagert IP-Adressen werden einem sprechenden Namen, dem DornainNamen, zugeordnet. Domain-Namen sind hierarchisch aufgebaut. Beispiel: 'vMw.hs-nl ederrheln.de
Hier handelt es sich um den WebselVer der Hochschule Niederrhein mit de (Deutschland) als Top Level Dornain. Der Hostname l ocal hast identifiziert den eigenen Rechner und entspricht der IP-Adresse 127.0.0.1 . DNS-Seruer sind spezielle Verzeichnisdienste, die die Zuordnung von IP-Adressen zu DNS-Namen und umgekehrt unterstützen. Hierüber kann z.B. die IP-Adresse zu einem Namen erfragt werden.
Zu beachten ist, dass mehrere Rechner mit jeweils eigener IPAdresse denselben DNS-Namen haben können und zu einer IPAdresse mehrere DNS-Namen gehören können. Portnummer
Um einen bestimmten Dienst (Server) im Internet zu identifizieren, reicht die IP-Adresse des Rechners, auf dem der Server läuft, nicht aus, da mehrere Server auf demselben Rechner gleichzeitig laufen können. Zur Identifizierung eines Servers dient neben der IP-Adresse des Rechners die so genannte Portnummer. Portnummern werden auch vom Server benutzt, um den anfragenden Client für die Rückantwort zu adressieren. Portnummem sind ganze Zahlen von 0 bis 65535. Sie werden von den Protokollen der Transportschicht verwendet. TCP und UDP können Portnummern unabhängig voneinander verwenden, d. h. ein TCP-Server und ein DDP-Server können auf einem Rechner unter derselben Portnummer zur gleichen Zeit laufen. Die Portnummern von 0 bis 1023 sind weltweit eindeutig definiert und für so genannte wellknown services reserviert. Die Liste der well-known Portnummern kann über
http://www. lana.org/ass lgnments/port-numbers abgefragt werden. Im Bereich von 1024 bis 49151 sind weitere Portnummem registriert, z. B. 1099 für die RMI-Registry und 3306 für den MySQLDatenbankserver.
Die Klasse InetAddress
1.7 Port
Protokoll
23
Service
Beschreibung
Echo
liefert die Eingabe als Antwort zurück
13 TCP/UDP
Daytime
liefert die aktuelle Zeit des Servers
20 TCP
FTP (Data)
überträgt Daten zum Server
21 TCP
FTP (Control)
sendet FTP-Kommandos zum Server
23 TCP
Telnet
Terminalemulation
25 TCP
SMTP
überträgt E-Mails zwischen Rechnern
53 TCP/UDP
DNS
Domain Name System
80 TCP
HTIP
Webserver
7 TCP/UDP
1.7
Die Klasse InetAddress
Die Klasse java. net. InetAddress repräsentiert eine IP-Adresse. Subklassen hiervon sind Inet4Address und Inet6Address. die IPAdressen der Version 4 bzw. 6 repräsentieren. Die folgenden drei statischen Methoden erzeugen InetAddressObjekte, static InetAddress getLocalHost() throws UnknownHostException ermittelt die IP-Adresse des Rechners. auf dem diese Methode läuft.
Die Klasse java. net . UnknownHostExcept l on ist Subklasse von ja va. to . IOExceptl on. Eine Ausnahme dieses Typs wird ausgelöst, wenn die IP-Adresse eines Rechners nicht ermittelt werden kann. static Inet Address getByName(String host)
throws UnknownHostExceptlon liefert die IP-Adresse zu einem Hostnamen. Enthält das Argument host eine IP-Adresse anstelle eines Namens, so wird nur die Gültigkeit des Adressformats geprüft. static Inet Address [] getAllByName(String host)
throws UnknownHostExceptlon liefert ein Array von IP-Adressen, die alle zum vorgegebenen Hostnamen gehören. String getHostAddress() liefert die IP-Adresse eines Inet Address-Objekts.
Bild 1.16.·
Beispiel für wellknown Services
1 Einleitung und Grundlagen
24
Stri ng getHostName() liefert den Hostnamen zu dieser IP-Adresse. Falls das Inet Address-Objekt mittels einer IP-Adresse erzeugt wurde und der zugehörige Hostname nicht ermittelt werden konnte, wird die IP-
Adresse zurückgegeben. Programm 1.4 zeigt die IP-Adresse und den Hostnamen des eigenen Rechners an.
Programm 1.4
lmport ja va net. InetAddress; lmport ja va net Unknown Host Exceptlon; public class Local Host ( public static voi d main(String[] args) throws UnknownHostException ( Inet Address addr ~ Inet Address .get LocalHost ( ) ; System.out.println(" IP-Nummer;\t" + addr.get HostAddress()); System.out.println(" Hostname;\t" + addr get HostName());
Programm 1.5 liefert die IP-Adresse zu einem Hostnamen. Programm 1.5
lmport ja va net. InetAddress; lmport ja va net Unknown Host Exceptlon; public class Lookup ( public stat ic voi d main(String [] args) throws UnknownHostException { if (args.length !~ I ) { System. err .pri ntln("java Lookup «hostname> System.exit (l);
I
Connection
------ --1
JcbcOcbcConnection
------ --1
JdxOdbcSta:emenl
jeva.utilDete
- ----- --
ResultSet
java lang Excernon
DatooaseMetEOata
ResultSetMetaData
----{>
erenos
------{>
JcbcOcbcPreparedStaement
JdbcOdbcResultSet
- ----- --
.u tc o ctc t etete seweteöa a
- ----- --
JclxO cbcResultSetMetaData
implements
Die Klasse DriverManager handhabt das Laden des JDBC-Treibers und bietet Methoden zum Aufbau einer Datenbankverbindung. Die wichtigsten Interfaces: •
Connection repräsentiert eine Datenbankverbindung.
•
Statement wird verwendet, um SQL-Anweisungen über eine
gegebene Datenbankverbindung auszuführen. •
Resu ltSet bietet Methoden, um auf das Ergebnis einer SQLAbfrage zuzugreifen.
2.2
Erste Beispiele
31
Bild 2.5 zeigt auch die Klassen der ]DBC-ODBC-Bridge, die die Interfaces implementieren.
2.2
Erste Beispiele
2.2.1
Die Beispiel-Datenbank
Um verschiedene Aspekte von JDBC in den nächsten Abschnitten demonstrieren zu können, benutzen wir eine Datenbank, die Bücher verschiedener Verlage verwaltet. Bild 2.6 zeigt die Beziehung zwischen den Entitätstypen verlag und buch. Bild 2 .6 ,
verl ag
11
n
-----
Datenmodell
buch
I
-----
Ein Verlag hat verschiedene Bücher veröffentlicht. Ein Buch ist in genau einem Verlag erschienen. In einer relationalen Datenbank werden die Entitätstypen als Tabellen aus Zeilen und Spalten implementiert. Die Spalten repräsentieren die verschiedenen Merkmale einer Entität, beim Buch also z. B. ISBN-Nurrnner, Autor, Titel usw . Jede Zeile enthält alle Angaben zu einer einzigen Entität.
1 isbn
I ~"
1, 01
I ousQobe
3-15-001308- 9
Oickens, Ch"Jles
Schwere Zeiten
K" rtoniert
3-15-00156 2-6
Dicke ns, Ch"Jles
Große Erw" rtungen
K" rtoniert
s", on, oI>
,m
'", ,e
1989
~
1993
s
3-15-010606- 0
Dick ens, Ch"rles
Der We ihn" chts" bend
Gebunden
ua
2006
3-257-20998-3
Dickens, Ch"rles
Nikol" s Nickleby
K" rtoniert
;ce
1997
3-257-21034-5
Dickens, Ch"rles
D"vid Copp erlield
K" rtoniert
3-257-21166 -1( Dicke ns, Ch"rles
Ble"kh" us
K" rtoniert
3-257-21405-7
Dick ens, Ch"rles
Die Pickwickier
K" rtoniert
ece e" es
3-257-21406-5
Dickens, Ch"rles
M"rtin Chu" lewit
K" rtoniert
en
3-351-030 44-4
Dickens, Ch"rles
W eihn" chten mit Dickens
Gebunden
3-458-3 2655-3
Dicke ns, Ch"rles
H" rte Zeiten
K" rtoniert
3-458-32733- 9
Dick ens, Ch"rles
Geschichte " us , wei Stiidten
3-458-32810-6
Dickens, Ch"rles
Ble"k House
3-458-3300 4-6
Dickens, Ch"rles
3-491-9600 7-1( Dickens, Ch"Jles
,C '
1982 1984 2002 1998
, , , , , ~
2005
..
e_' Bild 2.7:
n Ausschnitt aus der
12,6
C~ 14,9 14,9 14,9 12,9 13.9
in
200 4
C
11,5
K" rtoniert
m ,e;
1987
C
12,5
K" rtoniert
1 030
1988
C
1 011
1991
Nikol" us Nickleby
K" rtoniert
W eihn" chtserziihlungen
Gebunden
,~,
2000
3-538-0 5349-9
Dickens, Ch"rles
D"vid Copperliel d
Leinen
1 023
1955
3-538-06656-6
Dickens, Charles
Die Pickwickier
Gebunden
1 039
1997
3-538-0665 7-4
Dickens, Ch"rles
M" rtin Chu" lewit
Gebunden
1 000
1997
3-538-066 58-2
Dickens, Ch"rles
Nichol" s Nickleby
Gebunden
~%
1997
3-538-06982- 4
Dickens, Ch"rles
Nichol" s Nickleby
Gebunden
~%
2004
D" s Geheimnis des Edwin Drood
Leinen
3-7175-197 6-1( Dickens, Charles
verlOOL"
oe,
2001
C
, , , , , a
r
" "
9,95 44,9 12,9 12,9 12,9 24,9 24,9
Tabelle buch
2 Datenbankanwendungen mit JDBC
32 Primärschlüssel
Jede Tabelle enthält einen Prirnärschlüssel, der durch eine Spalte oder eine Kombination von mehreren Spalten repräsentiert wird. Der Wert des Primärschlüssels identifiziert höchstens eine Zeile der Tabelle, Die Schlüsselwerte einer Tabelle sind alle voneinander verschieden, Für die Tabelle buch bietet sich die Spalte isbn als Primärschlüssel an.
Fremdschlüssel
Um Verknüpfungen zwischen Tabellen herstellen zu können, werden so genannte Fremdschlüssel benötigt. Ein Frerndschlüssel einer Tabelle wird durch eine Spalte oder durch eine Kombination von mehreren Spalten repräsentiert. Ein Fremdschlüssel ist in einer anderen Tabelle Primärschlüssel. Durch Vergleich von Fremdschlüsselwert und Primärschlüsselwert werden Beziehungen zwischen Zeilen der beiden Tabellen hergestellt So hat die Tabelle buch den Primärschlüssel isbn, die Tabelle verlag den Primärschlüssel ierlag id. Die Tabelle buch besitzt den Fremdschlüssel uerlagid (siehe Tabellenstruktur weiter unten), Fremdschlüsselwerte innerhalb einer Tabelle sind in der Regel natürlich nicht eindeutig. Die l:n-Beziehung zwischen verlag und buch wird also durch die Fremdschlüssel-Primärschlüssel-Beziehung etabliert Im Weiteren setzen wir Grundkenntnisse der Datenbanksprache SQL voraus, Die SQL-Anweisungen zur Erstellung der zwei Tabellen für das Datenbanksystem MySQL sind,
Tabelle verlag
create table verl ag ( ver l a9_l d lnteger, verlag_na me varchar (30) , webadress e varchar (30) , primary key (ver lag i d)
Tabelle buch
create tabl e buch ( isbn varchar (17) , autor varchar(30), titel varchar(SO), ausgabe varchar(20), sei t enzahl l nte ger , jahr lnteger, ver l ag_l d lnteger, preis double, best and lnteger, stand dat etlme, primary key (isbn), for eign key (ver lag_i d) references verlag (verlag_i d)
2.2
Erste Beispiele
33
Die Programme dieses Kapitels "WUrden mit verschiedenen Datenbanksystemen getestet. Es "WUrden solche Systeme bevorzugt, die weit verbreitet bzw. ohne allzu großen Aufwand installiert werden können. Die folgende Tabelle führt die Datenbanksysteme und eingesetzten ]DBC-Treiber auf. Bezugsquellen können am Ende des Buches nachgeschlagen werden.
Access. MySQL (mit Tabellentyp InnoDB) und Apache Derby unterstützen Transaktionen (siehe Kapitel 2.4.1) und stellen die referentielle Integrität sicher.
2.2.2
Verbindungsaufbau
Alle Programme sind so geschrieben, dass die Datenbanksysteme leicht gewechselt werden können. Dazu sind die datenbankspezifischen Angaben in einer Konfigurationsdatei dbconnect. properties ausgelagert, die mit dem Texteditor bearbeitet werden kann. In diesem Abschnitt zeigen wir den grundsätzlichen Aufbau einer ]DBC-Anwendung. Wir setzen voraus, dass die Datenbank bereits eingerichtet ist und Testdaten enthält. In den folgenden Abschnitten dieses Kapitels werden dann der Aufbau und das Laden von Datenbanken mittels ]DBC-Programmen ausführlich beschrieben. SQL-Skripte und Testdaten sowie Skripte zum Starten und Stop- Online-Seroice pen des Derby-Servers können der zum Download zur Verfügung gestellten Programmsammlung (Online-Service) entnommen werden. Das erste Programmbeispiel gibt Informationen über die benutz- Programm 21 te Datenbank und das eingesetzte DBMS aus. Datenbankspezifische Angaben (Treiber, Identifikation der Datenbank, User-Id und Passwort) befinden sich in der Datei dbconnect.properties.
Hier sind die Angaben zur MySQL-Datenbank aktiviert. Das Zeichen # leitet einen Kommentar ein. Die Access-Datenbank muss mit dem Namen buecher als ODBC-Datenquelle registriert sein. Bei Windows muss zu diesem Zweck das Programm DatenquelZen (ODBC) aus der Systemsteuerung aufgerufen und der MSAccess-Treiber sowie die Access-Datenbank ausgewählt werden.
DBMetaData
import import import import
ja va ja va ja va ja va
io. File InputStream; sql .Connection; sq l . DatabaseMetaData; sq l . Dri verManager ;
impor t ja va util.Properties;
publ ic class DBMetaData ( public stat ic void main(String[] args) throws Except i on ( // OB-Parameter einlesen Fi lel nput St ream in ~ new Fi lelnput St ream( "dbconnect.prop erties"); Properties prop = new Properti es( ); prop.load(in) : in.clos e(): Stri ng Stri ng String String
11 Verb indung zur OB herstellen Connectlon con = Drl verManager.getConnectlon(url, user,
2.2
Erste Beispiele
35
password) ; DatabaseMetaData dbmd ~ con.getMetaData(); System.out. pri nt 1n(" URL; " + dbmd. getURl()); System.out. pri nt 1n(" UserNarre ; " + dbrrd. getUserNarre ()); System.out. pri nt 1n(" DatabaseProductName; " + dbrrd.getDatabaseProductName()); System.out. pr i nt 1n("OatabaseProductVerSl on: + dbrrd.getDatabaseProductVersion ()); System.out. pri nt 1n(" DriverNarre; + dbrrd getDri verNarre ()); System.out. pr i nt 1n(" DrlverVerSl on: + dbrrd.getDri verVersion()); con clos e();
Zunächst werden die Verbindungsparameter aus dbconnect. properties eingelesen und in entsprechenden Variablen abgelegt.
Die Treiberklasse (sun. jdbc. odbc. JdbcOdbcDri ver für Access, }DBC-Treiber laden com.mysql.jdbc.Driver für MySQL und org.apache.derby.jdbc. Cl i entDri ver für Apache Derby) wird mit Class. forNarre ( ... ) explizit geladen. Hierbei wird ein statischer Initialisierungsblock der Treiberklasse ausgeführt. der dafür sorgt. dass eine neue Instanz der Treiberklasse beim Treibermanager (java. sq 1. Drlver Manager) registriert wird. Verwendet man die neuesten Treiberversionen, so kann auf das Autoloading explizite Laden ab Java SE 6 verzichtet werden. Dann kann auch die Eigenschaft driver in der Datei dbconnect.properties weggelassen werden.
Sicherzustellen ist. dass die Klassen des JDBC-Treibers. zusammengefasst in einer Datei (z. B. mysql-connector-java-5. 1. 11bln.jar) vom Class-Loader gefunden werden. Dazu muss für die Programmausführung die entsprechende jar-Datei in den Klassenpfad aufgenommen werden. Die Programmsammlung des Online-Service enthält hierzu Vorschläge. Um eine Verbindung zur Datenbank aufbauen zu können, muss Verbindung zur diese Datenbank genau identifiziert werden. Die Zieldatenbank Datenbank wird in URL-Schreibweise angegeben, herstellen
jdbc:: Hierbei bezeichnet das Subprotokoll die Art des verwendeten Treibers (z. B. odbc, mysql oder derby), Subname bezeichnet die eigentliche Datenbank. Bei Access ist das der Name der ODBCDatenquelle. bei MySQL und Apache Derby ein URL (Uniform Resource Locator): / /1 oca1hostibuecher. Die Datenbank liegt hier
36
2 Datenbankanwendungen mit ]DBC auf dem eigenen Rechner. Statt 1oca1hast kann auch der Name eines im Netz verbundenen anderen Rechners angegeben sein. Die Dokumentation zum Treiber enthält, was als Subprotokoll und Subname anzugeben ist.
DriverManager
Die Klasse java.sql.Drl verManager kann unterschiedliche Treiber zur selben Zeit handhaben. Wird eine Verbindung zu einer Datenbank angefordert, so versucht der Treibermanager, den passenden Treiber zu finden und zur Herstellung der Verbindung zu verwenden. Die Drl verManager-Methode getConnectlon stellt eine Verbindung zur Datenbank her.
statlc Connectlon getConnection(Strlng url) throws SQLException static Connection getConnection(String url, Properties info) throws SQLException static Connection getConnection(String url, String user, String password) throws SQLException Alle Methoden liefern ein Connecti on-Objekt und erwarten einen URL und ggf, weitere Angaben als Parameter, Datenbank-User und Passwort können angegeben werden. Bei der zweiten Methode sind die zusätzlichen Verbindungsparameter user und password im Properties-Objekt info eingetragen. SQLException
Fehler werden in ]DBC grundsätzlich als Ausnahmen der Klasse java. sq1.SQLExcepti on ausgelöst
Connection
Ein Objekt vom Schnittstellentyp java. sq1.Connecti on präsentiert eine Verbindung zur Datenbank.
re-
Ein SQL-Anweisungsobjekt wird über die Connecti on-Schnittstelle erzeugt: Statement createStatement() throws SQLException Eine aktive Verbindung zur Datenbank wird mit der Methode close geschlossen: voi d close() throws SQLException Der Zustand einer Verbindung kann abgefragt werden, boolean isClosed() throws SQLException DatabaseMetaData
Das Interface DatabaseMetaData repräsentiert Informationen über die Datenbank und das DBMS, Die folgende ConnectiorcMethode liefert ein Metadaten-Objekt
2.2
Erste Beispiele
37
Database MetaData getMetaData() t hr ows SQLException Die Schnittstelle umfasst weit über 100 Methoden. Hier werden nur einige aufgeführt, die auch im nächsten Beispiel verwendet werden.
String getURL() throws SQL Exception liefert den URL der Datenbank. String getUserName() throws SQLException liefert den Namen des Datenbank-Users. String getDatabaseProductName() throws SQLException liefert den Produktnamen des Datenbanksystems. Str ing getDatabaseProductVersion() throws SQLException liefert die Version des Datenbanksystems. String getDriverName() throws SQLException liefert den Namen des ]DBC-Treibers. String getDriverVersion() throws SQL Exception liefert die Version des ]DBC-Treibers. mkd i r bi n javac -d bin src/DBMetaData.java java -cp bin %JDßC DR IV ER% DEMetaData
Die Umgebungsvariable JDßC_DR IVER referenziert die jar-Datei des ]DBC-Treibers. Ausgabe, URL: jdbc: mysq 1: / /1 oca1hostlbuecher
UserName: root@localhost DatabaseProductName: MySQL
Das Grundgerüst einer typischen ]DBC-Anwendung besteht aus den folgenden Schritten, 1. ]DBC-Treiber laden (entfällt bei Autoloading)
2. Verbindung zur Datenbank herstellen 3. SQL-Anweisungsobjekt erzeugen 4. SQL-Anweisung ausführen 5. Ergebnisse der SQL-Anweisung verarbeiten 6. Ressourcen freigeben
Test
2 Datenbankanwendungen mit ]DBC
38 Programm 2.2
Das zweite Programmbeispiel zeigt Informationen zu Büchern in Form einer sortierten Liste an. Die hierzu nötige SQL-Anweisung lautet select autor, t i t el . isbn from buch order by autor, titel
Buecherlistel
import ja va io.File InputStream; import ja va sql .Connection; import ja va sq l . Dri verManager; import java sql.ResultSet; import java sql.Staterrent; import java util.Properties; public class Buecherlistel ( public static void main(String [] args) throws Exception (
// OB-Parameter einlesen FilelnputStream in
~
new FilelnputStream(
"dbconnect.properties"); Properties prop = new Properties(); prop.load(in) ; in.close(); Stri ng ur 1 ~ prop. getProperty(" ur 1"); String user ~ prop.getProperty("user". ""); String password ~ prop.getProperty("password". "");
/I Autoloading // Verbindung zur OB herstellen
Connection con
=
DriverManager.getConnection(url, user,
password) ; // SQL-Abfrage ausführen
Statement stmt
=
con.createStatement();
ResultSet rs = stmt.executeQuery("select autor, t i t el . " + "i sbn from buch order by autor. ti te 1");
// Ergebnisse ausgeben whi 1e (rs. next ()) ( System.out.println(rs.getS tring(l)); System.out.println(rs.getString(2)); System.out.println("ISBN " + rs.getString(3)); System.out.println();
rs.close(); stmt.close(); con.close() ;
2.2
Erste Beispiele
Das Interface java.sql.Statement ist die Basisschnittstelle für alle Statement SQL-Anweisungsformen. Mit der Staterrent-Methode executeQuery wird eine SELECTAnweisung ausgeführt: ResultSet executeQuery(String sql) throws SQLException Die Methode liefert ein ResultSet-Objekt mit dem Abfrageergebnis. Die SQL-Anweisung sq1 enthält kein Abschlusszeichen wie z.B. ";". Solche Abschlusszeichen können von DBMS zu DBMS variieren und werden demzufolge vom Treiber gesetzt. void close() throws SQLException gibt die mit der Ausführung gebundenen Ressourcen frei. Hierdurch wird auch das evtl. vorhandene ResultSet-Objekt geschlossen. Das Ergebnis einer SQL-Abfrage (eine Relation mit Zeilen und ResultSet Spalten) wird durch ein Objekt mit der Schnittstelle Resu ltSet repräsentiert. Zum Navigieren über die Zeilen der Ergebnismenge wird die Resu ltSet-Methode next genutzt boolean next() t hr ows SQLException Ein interner Cursor zeigt auf die aktuelle Ergebniszeile. Zu Beginn ist der Cursor vor der ersten Zeile positioniert. next liefert so lange true, wie das Weitersetzen des Cursors erfolgreich war. 1st die Ergebnismenge leer oder das Tabellenende erreicht und keine weitere Zeile mehr vorhanden, so wird fa l se zurückgeliefert. void close() t hr ows SQLException gibt die genutzten Ressourcen frei. Nachdem der interne Cursor auf eine Ergebniszeile positioniert SpaltenzugriJJ "WUrde, kann auf die Spaltenwerte dieser Zeile von links nach rechts zugegriffen werden. Für den Spaltenzugriff existieren verschiedene ResultSetMethoden getXxx. Es kann über den Spaltenindex. der bei 1 beginnt. oder über den Spaltennamen zugegriffen werden. Zu beachten ist, dass der Spaltenindex sich auf die Spaltennummer in der Ergebnismenge und nicht in der Originaltabelle bezieht. getStri ng liefert den Spaltenwert als ein Stri ng-Objekt Str ing getString(int column lndex) t hrows SQL Exception String getString(String columnName) throws SQL Exception
39
2 Datenbankanwendungen mit ]DBC
40 Programmausgabe:
Dl ckens, Char les 81eak House IS8N 3-458-32810-6 Ol ckens, Charles 81 eakhaus IS8N 3-257-21166-X Dlckens, Charles Das GehelmnlS des Edwl n Drood IS8N 3-7175-1976-X
Programm 23
Im Unterschied zu Programm 2.2 wird im folgenden Programm auch der Verlag eines Buches ausgegeben:
Dlckens, Charles Bl eak House IS8N 3-458-32810 6 Insel
Die SQL-Anweisung enthält einen so genannten join zwischen den Tabellen buch und verlag. Buecherliste2 (Ausschnitt)
ResultSet rs ~ stmt .executeQuery("select autor, tt tel . r sbn. verlag narre + "fram buch inner join verlag" + "on buch. verla9_l d = verlag.v erlag~ld + "order by autor. ti te 1"); whi 1e (rs. next ()) ( System.out.pr i ntln(rs.getString( I)); System.out.pr i ntln(rs.getString(2)); System.out.println(" IS8N " + rS.g etString(3) + rS.g etString(4)); System.out.println();
Programm 2.4
+ " "
Mit dem folgenden Programm kann gezielt nach einem Titel gesucht werden. Dazu muss als Kommandozeilenparameter der Titel oder nur ein Teil des Titels beim Aufruf angegeben werden.
2.3 JDBC-Datentypen ResultSet rs
~
41
stmt
Buecherliste3 (Ausschnitt)
.executeQuery("select autor, t i t el , isbn, verlag name + + +
"fram buch inner join verlag"
"on buch.verla9_id
=
verlag.verla9_id
"where titel like '%" + titel + "%' order by autor, titel");
2.3
JDBC-Datentypen
Bei den SQL-Datentypen, die von den verschiedenen Datenbanksystemen unterstützt werden, gibt es zum Teil erhebliche Unterschiede. SQL arbeitet mit anderen Datentypen als Java. Beim Zugriff auf die Spaltenwerte und bei der Belegung von Parametern in JDBCProgrammen (siehe PreparedStatement im nächsten Abschnitt) muss daher eine Abbildung zwischen beiden Typsystemen erfolgen. Um die am meisten verwendeten SQL-Datentypen einheitlich zu ]DBC-Datentypen repräsentieren, "WUrden so genannte JDBC-Datentypen definiert, die in der Klasse java.sql.Types als Konstanten vom Typ int zusammengefasst sind: public static final int XXX Bild 2.8 gibt eine Übersicht über die ]DBC-Datentypen und ihre Entsprechungen für die Datenbanksysteme Access, MySQL und Apache Derby. Bild 2,8.'
Abbildung zwischen ]DBC-Typen und SQL-Typen SQL-Typ Access
SQL-Typ MySQL
TINYINT
BYTE
TINYINT
SMALLINT
SMALLINT
SMALLINT
SMALLINT
INTEGER
INTEGER, COUNTER
INTEGER,
INTEGER
JDBC-Typ
SQL- Typ Derby
Integerzahlen
MEDIUMINT BIGINT
BIGINT
BIGINT
REAL
FLOAT
REAL
DOUBLE, FLOAT
DOUBLE, REAL
DOUBLE, FLOAT
Gleitkommazahlen REAL FLOAT DOUBLE
2 Datenbankanwendungen mit JDBC
42
SQL-Typ Access
JDBC-Typ
SQL- Typ MySQL
SQL-Typ Derby
DECIMAL(p,s),
DECIMAL(p,s)
NUMERIC(p,s)
NUMERIC(p,s)
DATE
DATE, YEAR
DATE
TIME
TIME
TIME
DATE, TIME,
TIMESTAMP,
TIMESTAMP
DATETIME
DATETIME
CHAR
CHAR(n)
CHAR(n)
CHAR(n)
VARCHAR
VARCHAR(n), TEXT
VARCHAR(n)
VARCHAR(n)
LONGVARCHAR
LONGCHAR,
TINYTEXT, TEXT,
LONG VARCHAR
LONGTEXT
MEDIUMTEXT,
Festkommazah len DECIMAL NUMERIC
CURRENCY
Datum und Uhrzeit
TIMESTAMP
Zeichenketten
LONGTEXT
Binärdaten BIT
BIT
BINARY
BINARY(n)
BOOLEAN CHAR(n) FOR BIT DATA
VARBINARY
VARBINARY(n)
TINYBLOB
VARCHAR(n) FOR BIT DATA
LONGVARBINARY
LONGBINARY
BLOB,
LONG VARCHAR
MEDIUMBLOB,
FOR BIT DATA
LONGBLOB
Beim Lesen der Werte aus einem Resu ltSet-Objekt bzw. bei der Übergabe von Parametern an ein Pre par edSt at ement (siehe nächsten Abschnitt) werden getXxx- bzw. setXxx-Methoden verwendet Hier findet eine Konvertierung von SQL-Typen in Java-Typen bzw. umgekehrt statt Bild 2,9 zeigt die Abbildung zwischen JDBC-Typen und Java-Typen,
2.3
43
]DBC-Datentypen
saa z». Abbildung zwischen ]DBC- und]ava-
Typen JDBC-Typ
Java-Typ
Java-Objekttyp
TINYINT
byte
Int eger
SMALLINT
short
Inte ger
INTEGER
l nt
Int eger
BIGINT
l ong
Long
REAL
float
Fl oat
FLOAT
double
Doub 1e
DOUBLE
doubl e
Doub 1e
DECIMAL
java.math Bi gl:€ci ma 1
ja va.math. Big Deci mal
NUMERIC
java.math Bigl:€ci ma 1
ja va.math. BigDecimal
DATE
java sql · Date
ja va sql . Date
TIME
java sq l · Tü re
java sql .Tlme
TIMESTAMP
java sql · Tl Jn2stamp
ja va sql .Tlmestamp
CHAR
Str i ng
Stri ng
VARCHAR
Stri ng
Stri ng
LONGVARCHAR
Stri ng
Stri ng
BIT
boolean
Bool ean
BINARY
byte[]
byte[]
VARBINARY
byte[]
byte[]
LONGVARBINARY
byte[]
byte[]
Die get-Methoden der Klasse ResultSet sind dann entsprechend, byte getByte (...) short getShort(...) int getlnt(...) long getLong (...) floa t getFloat(...) doubl e getDouble(...) java. math. BigDeci ma1 getBi gDecima1 (...) java.sql.Date getDate(...) java.sql. Time getTime(...) java.sql. Timestamp getTimestamp( ..) String getString(...) boolean getBoolean(...) byte[] getBytes(...)
getXxx
2 Datenbankanwendungen mit JDBC
44
Beim Aufruf einer get-Methode ist als Parameter eine Spaltennummer (Typ i nt) oder ein Spaltenname (Typ St ri ng) anzugeben. Alle get-Methoden können die Ausnahme SQLExcepti on auslösen. Die Resu ltSet-Methoden Object getObject(int column Index) throws SQLException Object getObject(String columnName) throws SQLException liefern den Spaltenwert als ein Java-Objekt. dessen Typ dem entsprechenden JDBC-Datentyp aus der obigen Tabelle (Bild 2.9) entspricht. So wird z.B. für einen Spaltenwert vom JDBC-Typ I NTEGE R ein Objekt vom Typ Int eger geliefert.
Datum/Zeit
Die Klassen Date, Time und Timestamp des Pakets ja va.sql sind Subklassen von java.util.Date und repräsentieren die JDBCDatentypen DATE. TIME und TIMESTAMP. Objekte dieser Klassen enthalten Datumsangaben. Uhrzeitangaben bzw. Angaben zu Datum und Uhrzeit.
Date
Date(long dat e ) erzeugt ein Date-Objekt mittels des Zeitwerts date in Millisekunden (seit 01.01.1970. 00,00,00 GMT). void setTime(long date) setzt das Datum. static Date valueOf(String s) liefert ein Date-Objekt aus der Zeichenkette mm-tt".
5
im Format "jjjj-
String toString() liefert eine Zeichenkette im Format "jjjj mm-tt". Time
Time(long time) erzeugt ein Time-Objekt mittels des Zeitwerts ti me in Millisekunden (seit 01.01.1970. 00,00,00 GMT). voi d setTime(long time) setzt die Uhrzeit. static Time valueOf(String s) liefert ein Time-Objekt aus der Zeichenkette "hh :mm: 55".
String toString() liefert eine Zeichenkette im Format "hh :mm: ss".
5
im Format
2.4
Ausführung von SQL-Anweisungen
45
Timestamp(long t l me ) Timestamp erzeugt ein Tirrestamp-Objekt mittels des Zeitwerts time in Millisekunden (seit 01.01.1970. 00,00,00 GMT).
void setTirre(long ti rre) setzt Datum und Uhrzeit. long getTirre() liefert Datum und Uhrzeit in Millisekunden (seit 01.01.1970, 00,00,00 GMT). static Timestamp valueOf(String s) liefert ein Tirrestamp-Objekt aus der Zeichenkette s im Format "jjjj- rrrn-tt hh:mm:ss.f", wobei f die Nanosekunden bezeichnet. Str ing toString() liefert eine Zeichenkette im Format "jjjj -rrrn-tt hh:mm:
55.
f".
boolean after( Tirrestamp t s ) prüft, ob diese Uhrzeit später als ts ist. boolean before(Timestamp ts) prüft, ob diese Uhrzeit früher als ts ist. int compareTo(Ti rrestamp ts) vergleicht diese Uhrzeit mit ts. boolean equals( Timestamp ts) prüft, ob dieses Timestamp-Objekt inhaltlich gleich dem TirrestampObjekt ts ist.
2.4
Ausführung von SOL-Anweisungen
In diesem Abschnitt beschäftigen wir uns mit der Änderung von Datenbankstrukturen und -inhalten, der Abfrage von Metadaten über Ergebnismengen zur Laufzeit und mit der Schnittstelle
PreparedStatement.
2.4.1
Transaktionen
Im Fall von Änderungen steht der Begriff der Transaktion im Vordergrund. Eine Transaktion ist eine Folge von Datenbankoperationen Transaktion CSQL-Anweisungen), die die Datenbank von einem konsistenten Zustand in einen neuen konsistenten Zustand überführen. Die Anweisungen einer Transaktion werden entweder alle ausgeführt und abgeschlossen (committed] oder alle zurückgenommen
Crolled back). Transaktionen können automatisch oder manuell gesteuert werden. Eine ]DBC-Anwendung ist standardmäßig im Auto-CommitModus, Jede einzelne SQL-Anweisung bildet eine Transaktion, die automatisch mit Commit bestätigt wird.
46
2 Datenbankanwendungen mit ]DBC Um mehrere Anweisungen innerhalb einer einzigen Transaktion ausführen zu können, muss Auto-Commit ausgeschaltet werden. Das Interface Connectlon deklariert Methoden zur Steuerung von Transaktionen.
Auto-Commit
void setAutoCommit(boolean autoCommit) throws SQLException schaltet Auto-Commitein (true) oder aus (false). boolean getAutoCommit() throws SQLException prüft. ob Auto-Commit eingeschaltet ist.
Commitund
Rollback
void commit() throws SQLException zeigt an, dass die Transaktion abgeschlossen werden soll und alle Änderungen in der Datenbank permanent gespeichert werden sollen. void rollback() throws SQLException bricht die aktive Transaktion ab. alle bisherigen Änderungen innerhalb dieser Transaktion werden rückgängig gemacht. Transaktionen werden automatisch begonnen. Die erste SQLAnweisung nach einem Commit oder Rollback gehört schon zu einer neuen Transaktion. Eine manuell gesteuerte, noch nicht abgeschlossene Transaktion wird explizit mit der Methode commi t oder ro II back geschlossen.
2.4.2
Datenbankänderungen
SQL-Anweisungen. die die Datenbankstruktur betreffen (z. B. CREATE TABLE) und die Änderungsbefehle INSERT. UPDATE und DELETE werden mit Hilfe der folgenden StatementMethode an die Datenbank geschickt int executeUpdate(String sql) throws SQLException Im Fall von INSERT. UPDATE oder DELETE liefert die Methode die Anzahl der von der Änderung betroffenen Zeilen. Programm 2.5
Programm 2.5 richtet die im Kapitel 2.2 eingeführten Tabellen verlag und buch ein. Die Properties-Datei tables.properties enthält die Verweise auf die entsprechenden SQL-Skripte.
tables.properties
#tab1es . ./ db/Access/ create_ver 1ag. sq1 # . ./ db/Access/ create_buch. sq1 t abl es .. /db/MySQL/create_verlag.sql .. /db/MySQL/create_buch.sql #tabl es . ./db/Derby/create_verl ag .sql # . ./ db/Derby/create_buch. sq1
2.4 Ausführung von SQL-Anweisungen
47
Das Programm ist unabhängig von einem konkreten Datenmodell und somit universell einsetzbar.
lmport lmport lmport lmport lmport import lmport
java.lo BufferedReader; java.lo Fl l eI nput St ream; java.lo. FlleReader; java sql.Connectlon; java sql.Drl verManager; java sql.SQLException; java sql.Statement;
public class CreateTables ( public static voi d main(String[] args) { Connectlon con = null; try ( File lnputStream in ~ new File lnputStream( "dbconnect. propertles");
Properties dbProp dbProp.load (in); in.close() ;
~
new Properties() ;
String url ~ dbProp.getPropert y("url ") ; String user ~ dbProp .getProperty("user". ""); String password ~ dbProp.getProperty("password". ""); con
in ~ new File lnputStream("tables.propert ies"); Propertles tablesProp = new Propertles(); tab 1es Prop .load (i n) ; in.close() ; StrlngTokenlzer tables = new StrlngTokenlzer(tablesProp .getProperty( "tab 1es")); while (tables.hasMoreTokens()) ( BufferedReader reader ~ new BufferedReader( new File Reader (t able s .next Token( )) ) ; Strlng l tne:
In vielen Datenbanksystemen (wie auch bei MySQL) wird eine Transaktion durch create table automatisch abgeschlossen. Deshalb bleibt hier Auto-Commit eingeschaltet. Programm 2.6
Das folgende Programm ermöglicht das Laden von Tabellen aus externen Quellen.
Importdatei
Die Importdatei enthält pro Zeile einen in die Tabelle einzutragenden Datensatz, wobei die Spaltenwerte in der gleichen Reihenfolge auftreten, in der die Tabellenspalten mit der SQLAnweisung "CREATE TABLE" definiert "WUrden. Die Werte in der Datei sind durch ein Sonderzeichen (z. B.; oder das Tabulatorzeichen), das in keinem der Werte enthalten sein darf, getrennt (Feldtrenner).
Beispiel, Die Importdatei für die Tabelle verlag hat den Inhalt.
1;Anaconda; IN 2;Artemis &Winkler;www.patmos de 3;Aufbau;www.aufbau- verlag.de 4;Dlogenes;www.dlogenes.ch
Feldtrenner ist hier;. Evtl. nicht vorhandene Werte sind durch \N zu kennzeichnen. Diese entsprechen den NULL-Werten in der Datenbank Jede Zeile enthält drei Werte, die den Spalten uerlagid, verlag_name und webadresse entsprechen. Es folgen einige Konventionen für die Notierung der Werte in der Importdatei •
Zeichenketten werden ohne Begrenzer wie z. B. " eingegeben.
•
Numerische Werte werden wie Literale in Java notiert.
usw.
2.4
Ausführung von SQL-Anweisungen
•
Nicht vorhandene Werte (NULL-Werte) werden durch IN gekennzeichnet.
•
Formate für Datum, Uhrzeit und Zeitstempel sind "jjjj- rrrntt", "hh: rrrn:ss" bzw. "jjjj- rrrn-tt hh:mm:ss".
49
Das Programm ist allgemeingültig geschrieben und kann zum Laden beliebiger Tabellen genutzt werden. Es wird ntit drei Parametern aufgerufen: 1. Name des Verzeichnisses, in dem sich die Importdateien be-
finden C". " kennzeichnet das aktuelle Verzeichnis) 2. Feldtrenner, z. B. ; oder It (falls das Tabulatorzeichen benutzt wurde) 3. Name der Tabelle Der Name der Importdatei für die Tabelle xxx muss dann xxx.txt lauten. Das Programm generiert für jede eingelesene Zeile der Importdatei eine INSERT-Anweisung. Da die Tabellenstruktur (insbesondere die verwendeten SQL-Datentypen) dem Programm zur übersetzungszeit nicht bekannt ist, müssen Informationen hierüber zur Laufzeit abgefragt werden. Hierzu wird die ResultSet-Methode get l'etaData verwendet. die ein Resu ltSetl'etaData-Objekt bereitstellt. das die gewünschten Informationen enthält ResultS etMetaData getMetaData() throws SQL Exception Das Interface Resul tSet rvetaData deklariert u. a. Methoden, um Resu!tSetMetaData den ]DBC-Datentyp einer Spalte. die Anzahl der Spalten und einen Spaltennamen der Ergebnisrelation abzufragen. int getColumnType(int column) throws SQL Exception int getColumnCount() throws SQLException String getColumnLabel (int column) throws SQLException
finally { try { if (con !~ null) con.close(); if (tab !~ null) tab.close(); catch (Exception e) ( System.err.println(e);
// typgerechte Aufbereitung der Spaltenwerte für INSERT private static String getValue(int type. String s) { / / aus IN wi rd null if (s.equals("IIN")) re turn "null"; else if (type ~~ Types.CHAR 11 type ~~ Types.VARCHAR 11 type ~~ Types LDNGVARCHAR) { // der einfache Anführungsstrich ' wird verdoppelt s ~ s. replaceAll (" "'. """); return "'" + 5 + "'''; else if (type ~~ Types.DATE) return "{d '" + s + "')"; else if (type ~~ Types.TIME) return "{t '" + s + "')"; else if (type ~~ Types.TIMESTAMP) { // bei Access entsprechen die SQL-Typen DATE. TIME. // DATET IME alle dem JDBC-Typ TIMESTAMP
2 Datenbankanwendungen mit ]DBC
52
if (s.indexOf(' ') !~ -1) return "( ts '" + s + "'}"; else if (s.indexOf('-') !~ -1) return "{d + s + "'}"; else return "{t + s + "'}"; else
return
5;
Um Informationen über die ]DBC-Datentypen der Tabellenspalten zu erhalten, wird zunächst eine SQL-Abfrage der Form select
*
from tabelle where 0
~
1
ausgeführt, die eine leere Ergebnismenge liefert. Die Resu ltSet-Methode getMetaData liefert dann ein Resu ltSetMetaData-Objekt. Mit Hilfe von StrlngTokenlzer wird nun jede Zeile in ihre Werte zerlegt und die INSERT-Anweisung aufgebaut. lnsert into tabelle values (wertl, ... )
Die Hilfsmethode getValue(int type, String s) sorgt für die SQL-konforme Aufbereitung des eingelesenen Wertes. Hier liegen die folgenden Regeln zugrunde, Ist
5
gleich "\N", so liefert die Methode "null"
ZUIÜCk.
Ist der ]DBC-Typ type gleich CHAR, VARCHAR oder LONGVARCHAR, wird der Wert in einfache Hochkommata eingeschlossen. Ein einfaches Hochkomma • im Wert selbst wird verdoppelt. Ist type gleich DATE, TIME oder TIMESTAMP, so werden EscapeKlauseln der Form {d 'xxx'}, {t 'xxx'} bzw. {ts 'xxx'} zurückgeliefert, wobei xxx für das Literal für Datum, Zeit bzw. Zeitstempel steht (siehe obige Beschreibung der Importdatei). Test
Wegen der bestehenden Fremdschlüssel-Primärschlüssel-Beziehung muss zuerst die Tabelle verlag, dann die Tabelle buch geladen werden.
Batch-llpdates
Mehrere INSERT- oder UPDATE-Anweisungen können in einem Staterrent-Objekt mit addBatch gesammelt werden und dann in einem Zug mit executeBatch ausgeführt werden. Diese Vorgehensweise ist in der Regel effizienter als die einzelne Ausführung von SQL-Anweisungen.
2.4 Ausführung von SQL-Anweisungen
53
Hier die beiden Staterrent-Methoden, void addBatch(String sql) throws SOLException int[J executeBatch() throws SOLException Das i nt-Array enthält für jede SQL-Anweisung die Angabe darüber, wie viele Datensätze eingefügt bzw. verändert "WUrden, den
Wert Statement.SUCCESS NO INFO oder Staterrent.EXECUTE FAILED im Fehlerfall. Das nächste Programm exportiert die Daten einer Tabelle im Programm2.7 oben beschriebenen Import-Format. Damit können die Daten der Datenbank in einem Textformat gesichert werden.
public class Export ( public static void main(String[J args) { String dir ~ args[DJ; String delimiter ~ args[IJ; String table ~ args[2J; if (delimiter.equals("llt")) delimiter ~ "It"; Pri ntWrHer out = null; Connection con = null; try ( out = new PrintWrHer(new FlleWrlter(dir + ".txt"). true);
FileInputStream in
~
+ "I" +
table
new FileInputStream(
"dbconnect. properties");
Propertles prop prop.load (i n) ; in.close() ;
=
new Propertles();
String url ~ prop.getProperty("url"); String user ~ prop.getProperty("user". ""); String password ~ prop.getProperty("password". ""); con
=
DrlverManager.getConnectlon(url, user, password);
2
54 Statement stmt
=
Datenbankanwendungen mit ]DBC
con.createStatement();
ResultSet rs = stmt. executeQuery( "se1ect * from " +table); ResultSet MetaData rsmd ~ rS.getMetaData(); int n ~ rsmd.getColumnCount();
public class UpdateBestand ( public static void main(String[] args) { String datei ~ "bestand.txt"; BufferedReader input ~ null; Connection con = null; try ( input ~ new BufferedReader(new FileReader(datei)); FilelnputStream in
~
new FilelnputStream (
"dbconnect. properties");
Properties prop prop.load(in) ; in.close() ;
=
new Properties();
String url ~ prop.getProperty("url"); String user ~ prop.getProperty("user". ""); String password ~ prop.getProperty("password". ""); con = DriverManager.getConnection(url, user, password); con setAutoCommit(false); PreparedStatement stmt = con.prepareStatement( "update buch set bestand ~ bestand + ? + "stand = ? where isbn = ?"); String isbn; i nt bestand; i nt count;
StringTokenizer st; String line;
while ((line ~ input.readLine()) !~ null) ( st = new StringTokenizer(line); isbn ~ st.nextToken(); bestand ~ Integer.parselnt(st.nextToken()); stmt.setlnt(l. bestand); stmt.setTi mestamp(2. new Timestamp(System .currentTi me Hi 11 i s ())) ; stmt setString(3. isbn);
57 UpdateBestand
2 Datenbankanwendungen mit ]DBC
58 count
~
stmt executeUpdate();
i f (count > 0) System.out.println(isbn + "; Bestand aktualisi ert"); con .commlt ( ) ; stmt.close(); catch (Exception e) ( System.err.pr i ntln(e); try { if (con !~ null) con. rollback(); catch (SQL Exception ex) System. err .println(ex); } finally { try { if (con !~ null) con.close() ; if (i nput !~ null) i nput. cl ose () ; catch (Exception e) ( System.err.println(e);
2.5
Ein einfaches Frontend für SOL-Datenbanken
In Kapitel 2.2 wurden bereits Metadaten über die Datenbank (OatabaseMetaData) genutzt. Hier folgen zwei weitere Methoden: ResultSet getTables(String catalog . String schemaPattern. String tableNamePattern. String[] types) throws SQLException liefert eine Beschreibung der Tabellen der Datenbank. Im Beispiel rufen wir die Methode mit den Parametern null , null, "%", null auf. Das ResultSet-Objekt enthält u. a. die Spalten TABLE- NAME und TABL E- TYP E. ResultS et getColumns(String catalog. String schemaPattern. Str ing tableNamePattern. String col umnNamePattern) throws SQLException liefert eine Beschreibung der Tabellenspalten. Im Beispiel rufen wir die Methode mit den Parametern null, null, Tabellenname, "Z" auf. Das ResultSe t-Objekt enthält u. a. die Spalten
2.5
Ein einfaches Frontend für SQL-Datenbanken
COLUMN_NAME, DATA_TYPE TYPE_NAME, COLUMN_SIZE.
ODBC-Datentyp
aus
java sql. Types),
Das folgende Programm bietet ein Frontend für SQL-Daten- Programm2.9 banken, mit dem beliebige SQL-Anweisungen (SELECT, INSERT, UPDATE, DELETE) an eine Datenbank geschickt werden können.
Die Abfrageergebnisse können auf Wunsch in der Datei log.txt protokolliert werden. Das Programm bietet neben den SQLAnweisungen noch die folgenden Eingabemöglichkeiten show tables
alle Tabellennamen anzeigen
show table xxx
Metadaten zur Tabelle xxx anzeigen
log on
Protokollierung einschalten
log off
Protokollierung ausschalten
q
Programm beenden
In diesem Programm nutzen wir die Staterrent-Methode Unbekannte execute, um beliebige, zur Compilierungszeit unbekannte SQL- Anweisungen Anweisungen an die Datenbank zu schicken: ausführen
boolean execute(String sql) throws SOLException Der Rückgabewert ist true, wenn ein Abfrageergebnis vorliegt, und false, wenn es sich um eine Änderung handelt.
Mit den Statement-Methoden getResultSet und getUpdateCount kann die Ergebnismenge bzw. die Anzahl der geänderten Zeilen ermittelt werden: ResultSet getResultSet() throws SOLException int getUpdateCount() throws SOLException Auch die Schnittstelle PreparedStatement enthält eine executeMethode, boolean execute() throws SOLException Bei schwerwiegenden Fehlern Methoden einen Fehler durch java. sq1 .SOLExcepti on (Subklasse lermeldungen können in einem verpackt sein.
melden die meisten ]DBC- SQLException eine Ausnahme der Klasse von Excepti on). Mehrere FehObjekt vom Typ SOLException
SOLExcepti on enthält folgende Methoden,
59
2 Datenbankanwendungen mit ]DBC
60
String getSQLState() liefert eine Fehlerbeschreibung nach X!OPEN- bzw. SQL,2003Konventionen.
int getErrorCode() liefert einen herstellerspezifischen Fehlercode. SQLException getNextException() liefert das nächste Ausnahme-Objekt, falls mehrere Fehlermeldungen vorliegen. Sql
lmport lmport lmport lmport lmport lmport lmport l mport l mpor t import import import lmport lmport
java java java java java java ja va ja va java java java java java java
public static void main(String[] args) try ( FilelnputStream in ~ new File lnputStream( "dbconnect.properties"); Properties prop = new Properties(); prop.load(in) ; in.close(); Stri ng ur1 ~ prop. getProperty(" ur1"); String user ~ prop.getProperty("user". ""); String password ~ prop.getProperty("password". "");
con
=
DriverManager.getConnection(url, user, password);
input ~ new BufferedReader(new InputStreamReader( System. in)); String query;
2.5
Ein einfaches Frontend für SQL-Datenbanken whi 1e (true) ( System.out.print("> "); query ~ input.readLine(); if (query.equals("q")) break; if (query.equals("log on")) if (output ~~ null) (
output
=
new PrlntWrlter(
new FileWriter("log.txt"). true);
} log
~
true;
contlnue;
if (query.equals("log off")) log ~ false;
contlnue;
i f (log) output.println ("Query; " + query); try ( process(query) ; catch (SQLException e) printSQLException(e);
} catch (Exception e) ( System.err.println(e); finally { try { if (con !~ null) con.close(); if (input !~ null) input.close(); if (output !~ null) output. cl ose () ; catch (SQLException e) printSQLException(e); catch (IQException e) System.err.println(e);
61
62
2 Datenbankanwendungen mit ]DBC private static void printSQLException(SQLException e) ( System. err. pri nt1n( "SQLExcepti on: ") : while (e !~ null) ( System. err. pri nt1n( "SQLState: " + e. getSQLState ()): System.err.println("Message: + e.getMessage()): System. err. pri nt1n( "ErrorCode: " + e. getErrorCode ()): e ~ e.getNextException():
if (isResultSet) ( ResultSet rs ~ stmt.getResultSet(): ResultSet MetaData rsmd ~ rs.get MetaData(): int numCols ~ rsmd.getColumnCount(): l nt count = 0; whi 1e (rs. next () ) count++ ;
System.out.println("#" + count ): if (log) output prin tln("#" + count ): for (int i ~ 1: i log on > show tables
TABLE: buch TABLE: verl ag > show table buch isbn VARC HAR( 17) autor VARCHAR(30 ) titel VARCHAR(80) ausgabe VARCHAR(20) sei tenzah I INT( 10) jahr INT( 10) ver l ag_id I NT(10) preis DOUBL E(22) bestand I NT(l O) stand DATET IME(19) > select * from buch where titel like '%Twist%' # 1 i sbn: 3-7466-2200-X autor: Dlckens, Charles titel: Oliver Twist ausgabe: Kartonlert sei t enzahI: 564 jahr: 2005 verlag_id : 3 preis: 9.95 bestand: 50 stand: 2010 02-07 00:00:00.0 > log off > q
2.6
Speicherung großer Objekte
Wir wollen nun die Struktur der Tabelle buch um eine zusätzliche Spalte, die ein Bild des Covers aufnehmen kann, ergänzen. Die hierfür geeignete SQL-Anweisung lautet ""ccess:
alter tabl e buch add bild longbinary
MySQL:
alter table buch add bild longblob
Apache Derby: alter table buch add bild blob Große Binärobjekte schreiben und lesen
Mit Streams können die Daten blockweise geschrieben bzw.
gelesen werden.
PreparedState rrent-Methode: vOl d setBinaryStream(lnt parameterIndex, Input St ream x, int length) throws SQLException
2.6 Speicherung großer Objekte
65
1ength ist die Größe der Datei in Byte. Resu1tSet-Methoden, InputStream getBinaryStream(int columnlndex) throws SQLException InputStream getBinaryStream(String columnName) throws SQLException ImportImage speichert ein jpeg-Bild für eine bestimmte ISBN-
Programm2.10
Nummer. Die Bilder sind in einem vorgegebenen Verzeichnis zu finden und tragen jeweils den Dateinamen: . jpeg.
Aufrufbeispiel
java -cp bin %JDBC DRIVER% ImportImage bilder 3-15-001308-9
public class Import Image ( public static void main(String[J args) ( String dir ~ args[OJ; String isbn ~ args[IJ; Input St ream fi n ~ null; Connection con = null; try ( FilelnputStream in ~ new FilelnputStream( "dbconnect. properties");
Properties prop prop.load (i n) ; in.close() ;
=
new Properties();
String url ~ prop.getProperty("url"); String user ~ prop.getProperty("user". ""); String password ~ prop.ge tProper ty("password". ""); con
=
DriverManager.getConnection(url, user, password);
String image
=
dir + "I" + isbn +
File file ~ new File(image); int length ~ (int) file.length(); fin ~ new FilelnputStream(file);
Jpeg
66
2 Datenbankanwendungen mit ]DBC PreparedSt at ement ps = con.prepareStatement( "update buch set bild ~ ? where isbn ~ ?"); ps setBinaryStream( l, fi n, length ); ps setString(2, isbn); int count ~ ps executeUpdate(); i f (count> 0) System.out.prl ntln(lmage + " wurde lmportlert"); ps.cl osct ):
catch (Exception e) ( System,err,println(e); f inally { try { if (con !~ null) con.close t ) ;
i f (fin
!~
null)
f iu.cl ose O ;
catch (Except i on e) ( System, err ,println(e);
Programm 2.11
Mit ExportImage kann ein solches gespeichertes jpeg-Bild wieder in ein Dateiverzeichnis exportiert werden.
public class ExportImage ( public stat ic void main(String[J args) { Str ing dir ~ args[OJ; Str ing isbn ~ args[ IJ ; Input St ream fin ~ null; Fi l eOut put St ream fout ~ null; Connectl on con = null; try { Fi l el nput St ream in ~ new Fi l el nput St ream(
ResultSet rs ~ stmt .executeQuery( "sel ect bi l d fram buch where i sbn + isbn + '" and bi Idis not null");
=
'"
i f (rs. next ()) ( fin ~ rS.getBinaryStream(l); Strlng lmage = di r + "I" + i sbn + Jpeg fout ~ new FileOutputStream(image); byte[] buffer ~ new byte[1024]; int si ze : while ((size ~ fin.read(buffer)) !~ -1) { fout. wri te (buffer. O. si ze); ) fout. fl ush () ; System. out. pr i nt ln (" Bll d wurde exportl ert: " + l mage);
else System. out. pri nt In (" Kei n BiI d vorhanden"); rs .close(); stmt. cl ose () ; catch (Exception e) ( System.out.println(e); finally { try { if (con !~ null) con.close(); if (fin !~ null) fin.close(); if (fout !~ null) fout. cl ose () ; catch (Exception e) ( System.err.println(e);
2 Datenbankanwendungen mit ]DBC
68
2.7
Navigation und Änderungen inder Ergebnismenge
Bei der Erzeugung von SELECT-Anweisungen kann auf die Art der Verwendung der Ergebnismenge Einfluss genommen werden. Hierzu existieren die beiden Connect l on-Methoden
Statement createStatement (int resultSetType, lnt resultSetConcurrency) throws SQLException PreparedSt at ement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException Der Parameter resultSetType bestimmt, wie in der Ergebnismenge mit Hilfe eines internen Cursors navigiert werden kann. Der Parameter resu l tSetConcurrency legt fest, ob Datensätze der Ergebnismenge direkt geändert werden können. Navigierbarkeit
Für resultSetType können folgende Konstanten verwendet wer-
den, ResultSet, TYP E- FORWARD- ONLY
Der Cursor kann nur zum nächsten Satz bewegt werden. Dies ist die Standardeinstellung für die parameterlose Connectlon-Methode createStatement.
ResultSet, TYP E- SCROLL - INSENSITI VE
Der Cursor kann frei bewegt werden.
ResultSet,TYP E- SCROLL - SENS ITIVE
Der Cursor kann frei bewegt werden. Änderungen durch andere Transaktionen sind sichtbar. Änderbarkeit
Für resu ltSetConcurrency können folgende Konstanten verwendet werden:
ResultSet,CONCUR READ ONLY Änderungen können nicht vorgenommen werden. Dies ist die
Standardeinstellung für die parameterlose Connect ion-Methode createStatement .
ResultSet,CONCUR UPDATABL E Datensätze können in der Ergebnismenge geändert werden. Die freie Navigation kann bei entsprechender Einstellung mit
den folgenden ResultSet-Methoden erfolgen : boo1ean next() boolean prevlous()
boolean relative(lnt rows) void beforeFirst() void beforeLast() Der Cursor wird zur nächsten, vorherigen, ersten, letzten Zeile bewegt, auf die Zeile mit der Nummer row positioniert (die Zählung beginnt bei 1), um rows Zeilen vorwärts bzw, rückwärts (bei negativer Zahl) bewegt, vor die erste Zeile, hinter die letzte Zeile bewegt Ferner existieren die Prüfmethoden:
boolean boolean boolean boolean
isFirst() isLast() isBeforeFirst() isAfterLast()
int getRow() liefert die Nummer des aktuellen Datensatzes Cl für den ersten Satz), Das folgende Programm demonstriert die verschiedenen Mög- Programm2.12 lichkeiten der Navigation durch die Ergebnismenge.
java.lo BufferedReader; java.lo Fll eI nput St ream; j ava. jo . InputSt reamReader ; java sql.Connectlon ; java sql.Drl verManager; java sql.ResultSet; java sql.SQLExcep tion; java sql.Statement; java.utll.Propertles;
public class Na vigation ( public static void main(String[] args) {
Connectlon con
=
null;
Statement stmt ~ null; ResultSet rs ~ null; try ( Fi l elnput St r eam in ~ new File lnputStream(
"dbconnect.propertles"); Propert les prop = new Propertles (); prop . load (i n) ; in.close() ; String url ~ prop. get Propert y( "ur l "); String user ~ prop.getProperty("us er". "" ); String password ~ prop.getProperty("password". "");
con
=
DrlverManager.getConnectlon(url, user, password);
69
2 Datenbankanwendungen mit ]DBC
70
stmt
= con.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE. ResultSet.CONCUR_READ_ONLY); rs = stmt.executeQuery( "select isbn. titel from buch order by isbn");
if (rs.last()) ( System.out.println("Anzahl Zeilen; " + rs.getRow()); else ( System. out. printl n( "Ergebnl smenge i st leer"); return;
} rs. beforeFi rst(); System.out.println("Ihre Eingabe bitte; " + "next , prevl aus, fl rst . l ast oder qult"); BufferedReader br ~ new BufferedReader( new InputStreamReader(System. in)); Strlng I i ne ; whi 1e (( 1i ne ~ br readLi ne ()) ! ~ null) try { if (line.equals("next")) ( if (rs.next()) System.out.println(rs.getRow() + " " + rs.getString(1) + " " + rs.getString(2)); else if (line.equals("previous")) ( if (rs.previous()) System.out.println(rs.getRow() + " + rs.getString(1) + " " + rs.getString(2)); else if (line.equals("first")) ( if (rs.first()) System.out.println(rs.getRow() + " " + rs.getString(1) +"" + rs getString(2)); else if (line.equals("last")) ( if (rs .last()) System.out.println(rs.getRow() + " " + rs.ge tString(1) +"" + rs getString(2)); else if (line.equals("quit")) { break; } catch (SOLException e) System.err.println(e); } catch (Exception e) ( System.err.println(e);
2.7
Navigation und Änderungen in der Ergebnismenge
71
finally { try { if (rs !~ null) rs .close(); if (stmt !~ null) stmt. cl ose () ; if (con !~ null) con.close(); catch (SQLException e) {
Ist die Ergebnismenge als änderbar eingestellt, können Datensätze direkt in einem ResultSet-Objekt eingefügt, geändert und gelöscht werden. In der Regel darf die SELECT-Abfrage nur eine Tabelle umfassen (keine JOINs) und keine GROUP-Klausel enthalten. Die Abfrage muss den Primärschlüssel mit einschließen. Die genauen Regeln hängen vom DBMS ab. Für das Folgende setzen wir voraus, dass der Cursor auf einem bestimmten Datensatz der Ergebnismenge positioniert ist.
Aufruf der Resu ltSet-Methoden Ändern void updateXxx(int idx. typ x) throws SQLException analog zu den setXxx-Methoden aus Kapitel 2.4.3, also z. B. rs.updatelnt(3. 100); Aufruf der Resu ltSet-Methode void updateRow() throws SQLException Aufruf der Resu ltSet-Methode void moveTolnsertRow() throws SQLException
Einfügen
Aufruf der update Xxx-Methoden. Aufruf der Resu ltSet-Methode void insertRow() throws SQLException Ggf. Aufruf der ResultSet-Methode vOld moveToCurrentRow() throws SQLExceptlon um zum aktuellen Datensatz zurückzukehren, Aufruf der Resu ltSet-Methode void deleteRow() throws SQLException
Löschen
2 Datenbankanwendungen mit ]DBC
72 Programm 2.13
Das folgende Programm demonstriert die drei Änderungsarten. Zu einem Buch kann die Bestandszahl geändert werden, ein neues Buch mit ISBN-Nummer kann eingefügt werden, ein Datensatz kann gelöscht werden.
l mport lmport lmport lmport l mpor t import import
lmport ja va sql.Tlmestamp ; lmport ja va util.Properties;
publ ic class Update ( public static voi d main(String[] args) ( Connection con = null; Staterrent stmt ~ null; ResultSet rs ~ null; try ( Fi l el nput St ream in ~ new Fi l elnput St r eam( "dbconnect.properties"); Properti es prop = new Proper ties(); prop.load( in) ; in.clos e(); Stri ng ur1 ~ prop. getProperty(" ur1"); String user ~ prop.getProperty("user". ""); String password ~ prop.getProperty("password". ""); con
= con.createStatement( ResultSet. TYP E_SCROLL_INS ENS ITIVE. ResultSet.CONCUR_UPDATA ßL E); rs = stmt.executeQuery( "select isbn. titel. bestand. stand from buch");
if (rs.last()) ( System. out pri nt 1n( "Anzah1 Zei1en; " + rs. getRow()) ; else ( System. out pri nt l n( "Ergebnl smenge l st leer"); return; } rs. fi rst(); System. out println(); System. out. pri nt 1n( rs. getRow() + " " + rs. getStri ng (l) + " " + rs .ge tString(2) + " " + rs.getString(3)
2.7
Navigation und Änderungen in der Ergebnismenge + " " + rs .getString(4)); System.out.println ();
System.out println (" Ihre Ei ngabe bitte; ");
System.out.println(", insert , del ete, + "nur RETURN oder quit");
BufferedReader br ~ new BufferedReader( new Input St r eamReade r (Sys t em. i n ) ) ;
String line; wh ile ((line ~ br.readLine()) i f (line equals("q uit")) br eak;
rs. i nser tRow(); rs .moveToCurrentRow(); System. out. pri nt 1n (" insert OK"); el s e i f (line.equals("del ete")) rs .de1eteRow () ; System.out.println("del ete OK");
rs .previous(); el s e { try ( int bestand ~ Int ege r . par s el nt (l i ne ) ; rs.update lnt(3. bestand); rs.updateTimestamp(4. new Timestamp(System .currentTi me Hi 11 i s ())) ; rs. updateRow(); System.out.println(rs.getRow() + " " + rs.getString(l) + " " + rs.getString(2) + " "+ rs.getStr ing(3)); catch (NumberFormatException e) ( System. err. pri nt 1n ( "Keine Int ege r z ahl " ) ;
73
2 Datenbankanwendungen mit ]DBC
74
catch (SQL Exception e) System.err.println( e); } catch (Exception e) ( System. err println(e); finally { t ry { if (rs !~ null) rs.close(); if (stmt !~ nul l ) stmt.close(); if (con !~ null) con.close() ; catch (SQL Exception e ) (
2.8
CachedRowSet - eine verbindungslose Ergebnismenge
Ein Resu ltSet bietet den Zugriff auf das Abfrageergebnis mit Hilfe von internen Zeigern. Allerdings handelt es sich hierbei nicht um einen Container, der Daten speichert. Spätestens mit dem Schließen der Datenbankverbindung sind alle Daten verloren. Implementierungen des Interfaces j avax. s q1 . RowSet speichern die Zeilen des Abfrageergebnisses und ermöglichen auch die Navigation und Änderungen. RowSet ist ein Subinterface von ResultSet. CachedRowSet
Wir behandeln hier ein Subinterface von RowSet: j avax. sq1. rowset. CachedRowSet
Seit Java SE 5 wird eine Implementierung dieses Interfaces mit ausgeliefert:
com.sun.rowset.CachedRowSet Impl CachedRowSet Imp l-Instanzen sind serialisierbar.
Die CachedRowSet-Methode voi d populate(Resul tSet rs) t hr ows SQLException
übernimmt das übergebene Abfrageergebnis rs.
2.8
CachedRowSet - eine verbindungslose Ergebnismenge
75
Ohne eine Datenbankverbindung können die Daten mit den entsprechenden Resu ltSet-Methoden geändert werden (siehe Kapitel 2.7). Diese Änderungen können dann später in der Datenbank nachvollzogen werden.
Hierzu dient die folgende Methode,
vOld acceptChanges(Connectlon con) throws SyncProvlderExceptlon javax. sq l . rowset. spi .SyncPro vi derExcepti on ist Subklasse von SQLException.
Ein CachedRowSet eignet sich vor allem in verteilten Umgebungen. Daten können z. B. auf ein mobiles Endgerät übertragen werden und dort ohne Datenbankverbindung geändert werden. Später kann eine Übertragung zurück zur Datenbank erfolgen und die Daten können eingespielt werden.
Folgendes Szenario soll die Verwendungsmöglichkeit demonst- Programm2.14 rieren:
1. Buchdaten werden abgefragt und in einem CachedRowSet ge-
speichert. Anschließend wird dieses serialisiert (RowSetTestl). 2. Das CachedRowSet wird rekonstruiert (Deserialisierung) und der erste Datensatz wird geändert. Die Instanz wird wiederum serialisiert (RowSetTest2). 3. Das CachedRowSet wird rekonstruiert (Deserialisierung) und die Änderungen in der Datenbank eingespielt (RowSetTest3). Bild2.11.·
Cached RowSet
Testszenario
OB buch.dat
CachedRowSet
CachedRowSet
®
Update
Zur Vereinfachung der Prozesse dient die Klasse RowSetUti 1i ty. Sie enthält den Konstruktor RowSetUtility(String url. String user. String passwordl zur übergabe der Verbindungsparameter, die Methode
boolean executeQuery(String query)
76
2 Datenbankanwendungen mit ]DBC zur Erzeugung des Abfrageergebnisses mit Speicherung in einem
CachedRowSet-Objekt, die Methode boolean propagate() zur übernahme der Änderungen in die Datenbank, die get/setMethoden CachedRowSet get RowSe t() und void setRowSet(CachedRowSet rowSet) sowie die Methoden zur Serialisierung und Deserialisierung
statlc vOld serlallze(CachedRowSet crs, Str lng fll ename) throws IOExcept l on und static CachedRowSet deser ialize(String fil ename) throws IOExcept i on, ClassNot FoundException Row5etUtility
lmport ja vax.sql .rowset.CachedRowSet; lmport com.sun.rowset.CachedRowSet Impl; public class RowSetUtility ( prlvate CachedRowSet rowSet; pr i vat e Strl ng ur 1 ;
prlv at e Strlng user; prlvate Strlng password; public RowSetUtility(String ur}, String user. String password) ( this.url -vurl : thls.user = user; thlS password = password; public boolean executeOuery(String query) { Connect l on con = null; Statement stmt ~ null; ResultSet rs ~ null;
rowSet ~ new CachedRowSetlmpl(); rowSet.popu1ate( rs); return true;
catch (SQLException e) return false;
finally { try { if (rs !~ null) rs .close(); if (stmt !~ null) stmt. cl ose () ; if (con !~ null) con.close(); catch (SQLException e)
public void setRowSet(CachedRowSet rowSet) ( this.rowSet = rowSet; public CachedRowSet getRowSet() return rowSet;
public boolean propagate() Connection con = null; try ( con = DriverManager.getConnection(url, user, password); con.setAutoCommit(false); rowSet.acceptChanges(con); re turn true;
publlC statlc vOl d serlallze (CachedRowSet crs, String filename) throws IOExcept i on ( ObjectOutputStream out ~ new ObjectOutputStream( new Fi le Out put St ream(fi l ename )) ; out writeObject(crs); out fl ush() ; out close(); public stat ic CachedRowSet deserialize(String filename) throws IOExcept i on, ClassNotFound Except ion ( ObjectlnputStream in ~ new ObjectlnputStream( new Fi l el nput St ream(f i l ename) ) ; CachedRowSet crs ~ (CachedRowSet) in,readObj ect(); in,clos e(); r eturn crs;
RowSetTestl
lmport java l o.Fl l eI nput St ream; lmport java utll.Propertles;
l mport javax.sql .rowset.CachedRowSet; public class RowSetTest l ( public static voi d main(String[] args) { Fi lel nput St re am in; try ( j n = new Fl l eI nput St re am( "dbconnect propertles" ); Propertles prop = new Propertl es(); prcp. load(in); in,close(); Stri ng ur1 ~ prcp .getProperty(" ur1"); String user ~ prop,getProperty("user", ""); String password ~ prop,getProperty("password", ""); RowSetUtility util password) ;
~
new RowSetUtility(url, user,
= "select i sbr , t i tel . bestand, stand" "from buch order by isbn"; i f (uti 1, executeOuery(query)) ( CachedRowSet crs ~ util ,getRowSet();
Strlng query
while (crs,next()) ( System,out,println( crs getRow() +" " + crs,getString(l) + " " + crs getString(2) + " " + c rs .get St r l ng (3) + " " + crs,getString(4));
+
2.8
CachedRowSet - eine verbindungslose Ergebnismenge
79
RowSetUti 1i ty. seri al i ze(crs. "buch.dat") ; } catch (Exception e) ( System.err.println(e);
lmport java.sql .Tl mestamp;
RowSetTest2
lmport javax.sql.rowset.CachedRowSet;
public class RowSetTest2 ( public static void main(String[] args) { try ( CachedRowSet crs ~ RowSetUtility.deserialize( "buch.dat") ; crs.absolute(I); int bestand ~ crs.getlnt(3) + 10; crs.updatelnt(3. bestand); crs.updateTl mestamp(4, new Tlmestamp(System .currentTi rreMi 11 i s ())) ; crs. updateRow(); RowSetUti 1i ty. seri al i ze(crs. "buch.dat") ; catch (Exception e) ( System.err.println(e);
public class RowSetTest3 ( public static void main(String[] args) { FilelnputStream in; try { tn = new FII er nputSt ream ("dbconnect propertl es" ) ; Propertles prop = new Propertles(); prop.load(in) ; in.close() ; String url ~ prop.getProperty("url"); String user ~ prop.getProperty("user". ""); String password ~ prop.getProperty("password". "");
RowSetTest3
2 Datenbankanwendungen mit ]DBC
80 RowSetUtility util password) ;
~
CachedRowSet crs
RowSetUtility,deserialize(
~
new RowSetUtility(url, user,
"buch.dat"»:
uti 1, setRowSet(crs); i f (uti 1, propagate()) ( System,out,println("OB-Update erfolgreich"); else ( Systeri.out println("Fehler bei OB-Update"); } catch (Exception e) ( System,err,println(e);
2.9
Exkurs: XML-Dokumente aus SOL-Abfragen erzeugen
Thema dieses Kapitels ist die Entwicklung eines Programms, das das Ergebnis einer beliebigen SQL-Abfrage in eine XML-Struktur überführt und diese dann in ein anderes Format (z. B, HTML) mittels XSLT (Extensible Stylesheet Language Transformation) transformiert.
XMI
Zunächst einige Bemerkungen zu XML: Mit so genannten Auszeichnungssprachen (wie z. B, HTML) können beliebigen Textelementen Eigenschaften zugewiesen werden, wodurch deren Formatierung oder Bedeutung festgelegt wird, Die Textelemente werden durch Markierungen (tags) gekennzeichnet. Auf eine Startmarkierung folgt das Textelement, das in der Regel durch eine Endemarkierung abgeschlossen wird, Dabei sind die Markierungen im Allgemeinen geschachtelt, d.h. im markierten Text können weitere Markierungen enthalten sein. Das ermöglicht eine hierarchische Strukturierung des Inhalts, XML (Extensible Markup Language) ermöglicht mit selbst definierten Tags eine genaue Beschreibung strukturierter Informationen, die dann maschinell ausgewertet werden können. Im Unterschied zur Sprache HTML, deren Tags fest vorgegeben sind, ist XML eine Metasprache, mit der anwendungsspezifische Auszeichnungssprachen definiert werden können.
2.9
Exkurs, XML-Dokumente aus SQL-Abfragen erzeugen
81
Das XML-Dokument param.xml enthält Angaben zur Herstellung Programm 2.15 einer Datenbankverbindung, eine SQL-Abfrage und einige Dateinamen. Der allgemeine Aufbau dieser Datei sieht wie folgt aus:
Parametername Parameterwert Das zu entwickelnde Programm 2.15 (Db2Xm1) liest diese Parameter mit Hilfe der Klasse XMLPararreters zu Beginn ein.
url jdbc:mysql ://localhost/buecher
user root
password root SQL-Abfrage --> sql select autor, tl tel, lsbn, prels, bestand, stand from buch order by autor. titel
xmlFlle result/result.xml
param.xml
2 Datenbankanwendungen mit ]DBC
82
xs lFlle template.xsl
outputFlle result/result.html
Die Leistung der Klasse XMLParameters besteht im Parsen des XML-Dokuments mit Hilfe eines SAX-Parsers (Simple API for XML) und der Bereitstellung der Parameternamen und -werte in Form eines Propertl es-Objekts. Ein SAX-Parser bietet ein ereignisorientiertes API. Der Parser liest das XML-Dokument sequentiell durch und ruft spezielle Callback-Methoden immer dann auf, wenn er im Eingabestrom ein Tag oder ein Textelement entdeckt.
XMLParameters
package xmlutils; l mport l mport l mport l mport
java. i c. FileNotFoundException; java. i c. FileReader; java. i c. IOExceptlon;
java .uti l . Propertl es;
lmport javax.xml .parsers.ParserConflguratlonExceptlon; lmport javax.xml .parsers.SAXParser; lmport javax.xml .parsers.SAXParserFactory; l mport l mpor t l mpor t l mpor t l mpor t
public class XMLParameters extends DefaultHandler prlvate Strlng element; pr i vate Strl ng narre = " " : pr i vate Strl ng val ue = " " ; prlvate Propertles propertles = new Propertles(); public XMLParameters(String filename) throws FileNotFoundException. IOException. ParserConflguratlonExceptlon, SAXExceptlon
2.9
Exkurs, XML-Dokumente aus SQL-Abfragen erzeugen
SAXParserFaetory f aet ory ~ SAXParser Faetory.newlnstanee(); SAXParser parser ~ faetory.newSAXParser(); XMLReader reader ~ parser.getXMLReader(); reader.setContentHandler (this); reader.parse(new Input Souree (new Fi l eReader (f i l ename ) ) ) ; publie voi d startElement(String namespaeeURI. String loealName. String qName. Attributes attrs) throws SAXExeeption el ement
=
qName ;
publie void eharaeters(ehar[] eh. int start. int length) throws SAXExeeption // Textlnhalte können slch über me hrere Zell en ers trecken. String text ~ new String (eh. start. length).trim(); i f (text.length() ~~ 0) return ; if (element.equals("param-name")) if (name.length() > 0) name += " ": name += t ext ; else if (element.equals("param val ue") ) ( if (value.l ength() > 0) value += " '": value += text;
publie void endEl ement(String namespaeeURI. String loealName. String qName) throws SAXExeeption { if (qName.equals("param")) ( propertles.setProperty (name, value); name = uo. val ue = "";
XML Pararreters ist von der Klasse l:ef aultH andl er abgeleitet, die Default-Implementierungen für alle Callback-Methoden enthält Die Callback-Methoden startElerrent, endEl ement und characters werden in der Subklasse XMLParameters überschrieben. Im Konstruktor von XMLPar amet er s wird ein SAXParserFactoryObjekt erzeugt, mit dessen Hilfe dann der eigentliche Parser erzeugt wird, Die Methode parse des Interface XMLReader liest das XML-Dokument und ruft Callback-Methoden auf Die Callback-Methoden in XMLPar amet er s sind so implementiert, dass führende und nachfolgende Leerzeichen beim Parameternamen und -wert ignoriert werden . Insbesondere kann sich der Parameterwert über mehrere Zeilen erstrecken. Die Methode con vert der Klasse XMLCon verter erzeugt aus dem Ergebnis einer SQL-Abfrage (ResultSet) ein XML-Dokument der Form:
Spaltennamel
Wertl
Die kritischen Zeichen &, , ' und" werden durch so genannte Entity-Rejerenzen ersetzt.
XMLConverter
package xmlutils; import ja va sql.ResultSet; import ja va sql.ResultSetM2taData; import ja va sql .SQL Exception; publ ic class XMLConverter
prlvate ResultSet rs; publ i c XMLCon verter(Res ultSet rs) {
thls.rs
=
rs;
2.9
Exkurs, XML-Dokumente aus SQL-Abfragen erzeugen
public String conver t() throws SQLException ( StringBuilder sb ~ new StringBuilder(); String sep ~ System.getProperty("line.separator"); sb append( "" + sep); sb append( "" + sep); sb append("lt" + sep); ResultSetMetaOata rs md ~ rS.getMetaOata(); int n ~ rs md.getColumnCount(); for (int i ~ 1; i ":
sb. append( ">"); break;
85
86
2
Datenbankanwendungen mit ]DBC
case '\": sb.append("'"); break; case'\"':
sb. append("""); break; defau l t: sb. append(c);
return sb.toString();
Die Methode trans form der Klasse XML Transforrrer wandelt ein XML-Dokument in eine andere Struktur (z. B. ein HTMLDokument) mit Hilfe eines Stylesheets um. Die Extensible Stylesbeet Language Transformation (XSLT) beschreibt, wie XMLDokumente in andere Formate transformiert werden können. In unserem Beispiel wird das Stylesheet template.xsl verwendet. template.xsl
SOL Ergebnis
2.9
Exkurs, XML-Dokumente aus SQL-Abfragen erzeugen
87
template-Elemente legen fest, wie die Ausgabe aussehen soll. Ihr Inhalt definiert eine Vorlage, die mit den Daten des XMLDokuments gefüllt wird. Das Attribut match des Tags xs1 :t emp1ate gibt an, für welches Element des XML-Dokuments diese Vorlage gilt.
public class XMLTr ans f orme r ( prlvate Transfor mer transformer; public XMLTransformer(Reader xsl lnputl throws TransformerConflQuratlonExceptlon
TransformerFactory factory = TransformerFactory .new Instance () ; transformer = factory.newTransformer(new StreamSource( xslInputll: public Str ing trans form(Read er xml lnputl throws TransformerExceptlon { StrlngWrlter out = new StrlngWrlter(); transformer.transform(new StreamSource(xml Input ) , new StreamResult(outll: return out.toString():
XMLTransfonner
88
2 Dat enbankan wendungen mit JDBe Im Konstruktor von XMl Tr ansf ormer wird ein TransformerFacto ryO bjekt erzeugt , mi t dessen Hilfe dann der eigentliche Transfor mer auf der Basis eines Sryle sheets erzeugt wird Die Methode t ransfo rm der Klasse Transformer wand elt d as XML-Dokument um.
Die ma i n-Methode der Kla sse Db2Xm 1 führt di e folgende n Schri tte
aus: •
Einle sen der Paramet er au s param.xml mit Hilfe von XML -
Paramete rs
•
Aufb au der Datenbankverbindung
•
Au sfüh rung der &2L-Abfrage
•
Erzeug ung des XML-Dokumenrs mit Hilfe von X""-.Con ver ter
•
XSL-Transfo rma tion auf Basis de s Srylesheers ternplate .xsl mit Hilfe von XMLTr ansformer
Benutze r "H ugo " sitzt a m Rechner A OP-Adresse 10.108.105.95) , SZenario Benutzer "Emil" a m Rechner B (IP-Adresse 10.108.105.96) . Benutze r "H ugo" ruft das Programm wie folgt auf: java -c p bi n Talk Hugo 50000 10 .108 .105 .96 50000
Benutzer "Emil" gibt das folgende Kommando ein: java -cp bin Talk Emi 1 50000 10 .108 .105 .95 50000
Der UDP-Socket wird hier jeweils an die explizit vorg egebene lokal e Polt nummer 50000 (zwe ite r Aufrufparameter) ge bunden. Zum Test kann das Programm auch zweimal auf demselben Rec hner (localhost) gestartet we rden. Alle rdings mü ssen da nn die Po rtnumme rn unte rschiedlich sei n: j ava -cp bi n Talk Hugo 40000 localh os t 50000
jeve -cp bin Talk Emi 1 50000 localhost
4ססoo
3
110 Programm 33
lmport lmport import import import import import import import import import i mport i mport
ja va ja va ja va ja va ja va ja va ja va ja va ja va java java java java
import import import import import import
ja vax.swing ja vax.swing javax.swing javax.swing javax.swing ja vax.swing
Verbindungslose Kommunikation mit UDP
awt Borderlayout; awt Container; awt Event Queue ; awt Fl owLayout ; awt Fant ; awt event.ÄctionEvent; awt event .Act i onLi ste ner ; awt event. WindowAdapter; awt.event. WindowEvent; i o.IOExcept i on; net.DatagramPacket; net.OatagramSocket; net. InetAddress; JButton: JFrame; JPanel; JScrollPane; JTextArea; JTextField;
public class Talk imp le rrents Actionlistener, Runnable ( private static f inal int BUFS IZ E ~ 508; private DatagramSocket socket; prlvate DatagramPacket packetOut; private JTextField input; prl vate JTextArea output; public static voi d main(String [J args) { if (args,length !~ 4) ( System.err.prl ntln("java Talk " " ");
+
Systeniex i t.C l ):
JFrame frame
=
new JFrame( );
try ( new Talk(args, frame); catch (Exception e) ( System,err,println(e); Systemex i t.C l ).
fra rre pack(); fra me setVisible(true); public Talk(String[J args, JFrame fra me) throws Exception ( String user ~ args[OJ; int localPort ~ Int eger parse Int(args[IJ);
3.5 OnIine-Unterhaltung
111
String remoteHost ~ args[2J; int remotePort ~ Int eger .parse lnt (args [3J ) ; InetAddress.getByName(
InetAddress remoteAddr remoteHost) ;
~
frame.setTitle("Talk
"+
user);
socket ~ new DatagramSocket(localPort); packetOut ~ new DatagramPacket(new byte[BUFSIZEJ. BUFSIZE. remoteAddr. remotePort); frame.addWindowListener(new WindowAdapter() ( public void windowClosing(WindowEvent e) { i f (socket ! ~ null) socket. cl ose () ; System. exi t( 0) ; } }) ; Contalner c JPanel panel
frame.getContentPane();
=
~
new JPanel(new FlowLayout(FlowLayout.LEFT.
2. 2));
input ~ new JTextField(40); input. setF ont (new Font("11mospaced". Font. PLAI N. 18)); pane1. add(input) ; JButton send ~ new JButton("Senden"); send.addActionListener(this); pane1. add(send); c.add(panel. 8orderLayout.NORTH); output ~ new JTextArea(IO. 45); output.setFont(new Font("Monospaced". Font.PLAIN. 18)); output.setLineWrap(true); output.setWrapStyleWord(true); output.setEdi table(false); c.add(new JScrollPane(output). BorderLayout.CENTER); // Thread zum Empfangen von Nachrichten starten Thread t ~ new Thread(this); t.start(); public void run() ( OatagramPacket packetIn ~ new OatagramPacket( new byte[BUFSIZEJ. BUFSIZE); whi 1e (true) { try ( receive(packetIn);
112
3
Verbindungslose Kommunikation mit UDP
catch (I OExcept i on e) ( break;
prl vate vOl d recel ve(DatagramPacket packet ln) t hr ows IOExcept i on ( socket.recel ve(packet In); final String text ~ new String(packet ln getData(), 0, packet ln,ge tlength()); try ( Event Queue , i nvokel at er (new Runnable() ( public void run() ( output,append(text); output . append("ln"); } }) ; catch (Exception e) (
public void actionPerformed(ActionEvent e) { try ( byte[] data ~ input,getText(),getBytes(); packetOut.setData(data) ; packetOut,setlength(data,l ength); socket,send(packetOut); catch ( IOException ex) System,err,pr i ntln(ex);
Im Konstruktor werden zunächst IP-Adressen bestimmt, der UDP-Socket an die vorgegebene lokale Portnummer gebunden und ein Paket zum Senden der eingegebenen Daten erstellt. Beim Schließen des Fensters wird der Socket geschlossen. Textfeld, Button und Textfläche werden erzeugt und platziert sowie das aktuelle Objekt als Acti onl i stener beim Button registriert. Schließlich wird ein Thread, der Datagramme empfängt (siehe run-Methode), erzeugt und gestartet. Innerhalb der run-Methode werden in einer Endlos-Schleife die Daten empfangen und in der Textfläche angezeigt. Die DatagramSocket-Methode recel ve blockiert so lange, bis ein Datagramm empfangen wurde, Die Ausgabe des Textes erfolgt in der runMethode eines Runnab1e-Objekts r, das mittels Event Queue ,
3.6
Punkt-zu-Mehrpunkt-Verbindungen
113
lnvokeLater(r) an den Ereignis-Dispatcher, der für die Aktualisierung der Benutzungsoberfläche verantwortlich ist, zur Ausführung weitergeleitet wird.
Beim Drücken des Buttons "Senden" werden die Daten des Eingabefeldes in einem Datagramm an die Zieladresse gesendet.
3.6
Punkt-zu-Mehrpunkt-Verbindungen
Bisher haben wir in diesem Kapitel ausschließlich Punkt-zuPunkt-Verbindungen (Unicast) betrachtet. Unter Multicast versteht man eine übertragung von einem Teilnehmer zu einer Gruppe von Teilnehmern (Punkt-zu-Mebrpunkt-Verbindung). Der Vorteil dieses Verfahrens liegt darin. dass gleichzeitig Pakete über eine einzige Adresse an mehrere Teilnehmer gesendet werden. Die Vervielfältigung der Pakete findet nicht bereits beim Sender statt, sondern erst an jedem Verteiler auf dem übertragungsweg. So wird Multicasting insbesondere zur übertragung von Audio- und Videosträmen eingesetzt, um die Netzbelastung zu reduzieren.
on e-to-many
=
Dalagramm
,ro
~
Multicast-Gruppe 228.5.6.7
Q~
Im Internet werden für das Multicasting die IP-Adressen der Klasse D verwendet 224.0.0.0 - 239.255.255.255 Eine Multicast-Gruppe wird durch eine Adresse dieses Bereichs (Multicast-Adresse) und eine UDP-Portnummer repräsentiert. Die Adresse 224.0.0.0 ist reserviert und sollte nicht benutzt werden. Teilnehmer können jederzeit einer Multicast-Gruppe beitreten oder diese wieder verlassen. Wird eine Nachricht an eine Multicast-Gruppe gesendet, erhalten alle Gruppenteilnehmer diese Nachricht, sofern sie sich im Time-to-Live-Bereich des Pakets befinden. Das Feld Time-ta-Live (TTL) im Header eines IP-Pakets gibt an, wie lange ein Paket im Internet verbleiben darf. Es handelt sich um einen Zähler, der von jedem Router, den das Paket passiert, um 1 dekrementiert wird. Bei einem Wert von 0 wird
Bild 3,6.' Mu!ticasting
3 Verbindungslose Kommunikation mit UDP
114
das Paket zerstört. Die Reichweite der gesendeten Pakete kann
also durch die Angabe des Time-to-Live-Wertes eingeschränkt werden.
MulticastSocket
Die Klasse ja va.net. Multi castSocket (Subklasse von DatagramSocket) wird zum Senden und Empfangen von MulitcastDatagrammen verwendet.
void setTimeToLive( i nt ttl) throws IOExcept i on setzt den Time-To-Live-Wert. Zulässiger Wert, 0 findClass(Strlng name) throws ClassNotFoundExceptlon in geeigneter Weise überschrieben werden . Programm 4.4
Im Folgenden entwickeln wir einen Server (ClassServ er), der auf Anfrage eine Klasse in seinem Serververzeichnis sucht und den Inhalt als Byte-Array liefert. Der Client (NetworkCl assloader) überschreibt als Subklasse von ClassLoader die Methode fi ndC l ass. Dazu nutzt er die Cl assLoader-Methode defl neCl ass, um aus dem vom Server gelieferten Byte-Array ein Cl ass-Objekt zu erzeugen,
protected flnal Cl ass defineClass( Str ing narre. byt e[] b , int off. int l en) Achtung
Beim Einsatz eines Programms mit eigenem Klassenlader ist generell zu beachten,
Es wird versucht, weitere in der soeben geladenen Klasse referenzierte Klassen mit dem gleichen Klassenlader zu laden, mit dem auch die erste Klasse geladen "WUrde. Befinden sich das aufgerufene Programm (die Start-Klasse) und die zu ladende Klasse im gleichen Klassenpfad. so wird diese Klasse vom Standard-Klassenlader des Systems und nicht vom eigenen Klassenlader geladen. ClassServer
Der Server wird mit Portnummer und Suchpfad aufgerufen. Da der Name der angeforderten Klasse auch den Paketnamen enthalten kann. werden Punkte durch Schrägstriche ersetzt und somit die einzelnen Teile des Paketnamens auf Verzeichnisnamen abgebildet.
publlC class ClassSer ver extends Thread prlvate lnt port; prlvate Strlng searchPath; public ClassSer ver(int port. String searchPath) ( this.port ~ port; thls.searchPath = searchPath; public voi d startServer() { try ( ServerSocket ser ver = new ServerSocket(port); Inet Address addr ~ Inet Address .get Local Host ( ) ; System.out.println("ClassServer auf" + addr. getHostNarre () + "/" + addr. getHostAddress () + ".;" + port + " gestartet ... "); whi 1e (true) ( Socket cllent = server accept(); new Cl assThread(client. searchPath).start() ; } catch (IOException e) System.err.println (e);
pri vate class ClassThread extends Thread ( prlvate Socket ellent; prl vate Strlng searchPath; public ClassThread(Socket cli ent. String searchPath) ( thls.cllent = ellent; thls.searchPath = searchPath; public void run() ( String cli entAddr ~ client.get lnetAddress() .get HostAddress ( ) ; int clientPort ~ client.getPort(); System.out.println("Verbindung zu " + clientAddr
+ '";"
+ cl i ent Port + " aufgebaut");
try ( BufferedReader in ~ new BufferedReader( new Input St reamReader (cl i ent .getl nput St ream( )) ) ; ObjectOutputStream out ~ new ObjectOutputStream(client .getOutputStream()) ; out. fl ush(); String name
in.readLine();
4
146
Client/Server-Anwendungen mit TCP
name = narre. rep 1ace (' . " '/'); Strl ng cl assPath = searchPath + name + ". cl ass" ;
File file ~ new File(classPath); int length ~ (int) file.length(); FilelnputStream fis ~ new FilelnputStream(file); byte data[J ~ new byte[lengthJ; l nt cnt
=
0;
while (cnt < length) ( int result ~ fis.read(data. cnt , length if (result ~~-1) break; cnt += resu l t : } fis .close();
public static void main(String[J args) { if (args.length !~ 2) (
System.err .prlntln("java ClassServer "); System.exit(l); int port ~ Integer.parselnt(args[OJ); String searchPath ~ args[lJ; searchPath
~
searchPath. rep 1ace(' \ \', '/');
4.6
Klassen über das Netz laden
147
i f (!searchPath.ends With ("j")) searchPath += "I"; new ClassServer(port, searchPath).startServer();
lmport lmport lmport lmport import import
java. io EOFException; java. to . IOExcepti cn: java. io ObjectlnputStream; java. io. Pri nt WrHer; java net. ConnectExcepti on; java net.Socket;
NetworkClassLoader
public class NetworkClassLoader extends ClassLoader ( private String hast; private int port; public NetworkClassLoader(String host. int port) ( this.host ~ host; this.port ~ port; public Cl ass findClass(String name) ( byte[] b ~ 10adClassData(name); return defineClass(name. b. D. b.l engt h) ; pri vate byte[] 10adClassData(String name) { Socket socket ~ null; try ( socket ~ new Socket(host. port); ObjectlnputStream in ~ new ObjectlnputStream(socket .get lnputStream()); PrintWriter out = new PrintWriter(socket .getOutputStream(). true); out.pr intln(name); Object obj ~ in.readObject(); in.close() ; out.close(); return (byte[]) obj; catch (Connect Exception e) { throw new RuntimeExcepti on("Verbi ndung zum Server " "konnte ni cht hergeste11 t werden."); catch (EOFException e) ( t hrow new RuntimeException( "Kl asse wurde ni cht gefunden.");
+
4 Client/Server-Anwendungen mit TCP
148
catch (Exception e) ( throw new Runt l me Exception(e); finally { try { if (socket !- null) socket.close(); catch ( IOException e)
Statt
Das Programm Sta rt wird mit den folgenden Parametern aufgerufen: •
Name bzw. IP-Adresse des entfernten Rechners
•
Portnummer des entfernten Rechners
•
Name der zu ladenden Klasse, die die ma in-Methode enthält
•
evtl, Aufrufparameter für die mai n-Methode
Das Reflection-APlwird genutzt, um die main-Mefhode der geladenen Klasse mit evtl. Parametern aufzurufen. public class Start ( publ i c static void ma in(String [J args) { if (args.l ength < 3) ( System.err.println( "java Start [ ... ]"); System.exit(l); String host - args[OJ ; int port - Int eger .parsel nt (args [l J ) ; String className - args[2J; try ( NetworkClassLoader loader - new NetworkClassLoader(host. port) ; Cl ass c = loader.loadClass(className); // Bereitstellung der main-Methode java.lang.reflect.Method m - c.getMethod("main". new Class[J ( Str ing[J.class j); Str ing[J params - new String[args.length for (int i
params[i
=
3; i < args.l ength; i ++ )
3J - args[i J;
3J;
4.6
Klassen über das Netz laden
149
// Aufruf der main-Methode mit evtl. Parametern m.invoke(null. new Objecte] ( params j); catch (Runt imeExcept i on e) ( System.err.println (e.getMessage()); catch (Exception e) ( System.err.println(e);
Zur Demonstration des Verfahrens kann z. B. das Programm 4.3 (ChatCli ent) genutzt werden. ClassServer ~~
ChatClient
~~
Bild 4.7'
Dynamisches Laden über das Netz
I Start I
b yt e ] ] I
I
Rechner A
Rechner B 10.108.105.96:40000
Das Laden unbekannter Klassen aus dem Netz stellt ein Sicherheitsrisiko dar. Eine Möglichkeit. sich zu schützen. besteht darin. den Security Manager zu nutzen und nur so viele Zugriffsrechte wie nötig für die Ausführung der Anwendung zuzulassen. Die Zugriffsrechte. passend zum obigen Beispiel. werden in einer Policy-Datei beschrieben: grant {
permi ssi on ja va.1 ang.Runti mePermi ssi on "createCl assloader" ; permission ja va.lang.RuntimePermission "exitVM"; permi ssi on ja va. awt .AWTPermi ssi on "shol'Mi ndol'Mi thoutWa rni ngBanner" ;
11 Cl assSer ver permission ja va.net.SocketPermission "10.108.105.96: 40000" , "connect"; 11 ChatServer permission ja va.net.SocketPermission "10. 108.105.96:50000", "connect"; {:
Aufruf des Start-Programms (Kommando in einer Zeile} ja va -Djava.security.manage r -Djava.secu rity.policy= policy.txt -cp bin Start 10 .108 .105 .96 40000 ChatCli ent 10.108.105.96 50000
policy.txt
4
150
4.7
Client/Server-Anwendungen mit TCP
Remote Procedure Call
In diesem Abschnitt entwickeln wir ein allgemeingültiges einfaches Verfahren zum Aufruf von Methoden, die auf einem entfernten Rechner implementiert sind (RPC = Remote Procedure Call). Programm 4.5
Einzige Voraussetzung ist: Die Parameterwerte einer entfernten Methode müssen Objekte sein. Die Klassen aller Objekte (Rückgabewert und Parameterwerte) müssen das Interface ja va.l o. Serl al i zabl e implementieren. Der Client ruft die gewünschte Methode mit Hilfe von Object call (String narre. Objecte] params) der Klasse RPCCl i ent auf. narre ist der Methodenname, params enthält die Parameterwerte.
Beispiel, Lautet die Deklaration der entfernten Methode public int getSumrre(lnteger x , Integer
s>.
so sieht der Codeabschnitt des Clients für den Aufruf beispielsweise so aus: Objecte] params ~ (ID. 33); i nt sumrre ~ ( Integer) rpcCl i ent ca11 ("getSumme". params);
RPCClient
Die Klasse RPCCl i ent nutzt die Methoden writeObject und readObject der Klassen ObjectOutputStream bzw. ObjecUnputStream. um den Methodennamen, das Parameter-Array und den Rückgabewert über das Netz zu transferieren. Bei einem Rückgabewert vom Typ Exceptlon wird eine Ausnahme dieses Typs ausgelöst. l mport l mport l mport l mport
java java java java
i c. IOException;
i 0 ObjectInputStream; iO.ObjectOutputStream; net.Socket;
public class RPCClient prlvate Strlng host; prlvate lnt port;
public RPCClient(String host. int port) { th is.host ~ host; th is port ~ port;
4.7
Remote Procedure Call
pub1i c Object ca11 (Stri ng narre. Objecte] params) throws Exception { Socket socket ~ null; try ( socket ~ new Socket(host. port); ObjectOutputStream out ~ new ObjectOutputStream(socket .getOutputStream()) ; out writeObject(narre); out writeObject(params); out. flush(); ObjectlnputStream in ~ new ObjectlnputStream(socket .getlnputStream()); Object ret ~ in.readObject(); in.close() ; out.close();
lf (ret lnstanceof Exceptlon) throw (Exception) ret; return ret; fina11y { try { i f (socket ! ~ null) socket. cl ose () ; catch (IOException e)
Die Klasse RPCServer nutzt das Reflection-Al'I. Sie lädt diejenige RPCSeroer Klasse (hier als Service bezeichnet). die die Implementierung der entfernten Methode enthält, und erzeugt eine Instanz dieser Klasse. Dazu muss letztere Klasse den parameterlosen Konstruktor zur Verfügung stellen. Anhand des Methodennamens und der Parameterwerte, die vom Client als Objekte übermittelt wurden. wird mit Hilfe der ClassMethode getMethod die gewünschte Methode des Service als Method-Objekt zur Verfügung gestellt.
Anschließend wird mit i nvoke die Service-Methode aufgerufen. Wenn die Service-Methode eine Ausnahme auslöst, so löst l nvoke die Ausnahme
java lang.reflect.InvocatlonTargetExceptlon aus.
151
4
152
Client/Server-Anwendungen mit TCP
Die Invocatl onTargetExceptl on-Methode
Throwable getTargetException() gibt die von der aufgerufenen Service-Methode ausgelöste Ausnahme ZUlÜCk.
Der RPCServer liefert Ausnahmen (Typ Exception), die beim Suchen bzw, bei der Ausführung der Service-Methode auftreten können, als "normales" Ergebnisobjekt des Aufrufs zurück. l mport l mpor t l mpor t l mpor t l mpor t l mpor t l mport l mport
java java java java java java java java
i c. IOException;
i 0 ObjectlnputStream; io.ObjectOutputStream; lang.reflect.InvocatlonTargetExceptlon; lang.reflect.Method; net.lnetAddress; net.ServerSocket; net. Socket;
public class RPCServer extends Thread ( prlvate int port; private String service; public RPCServer(int port. String service) ( this port ~ port; this service = service; public void startServer() throws Exception ( ServerSocket server = new ServerSocket(port); InetAddress addr ~ InetAddress .getLocalHost(); System. out. printl n( "RPCServer auf" + addr. getHostName() + "/" + addr. getHostAddress() + ":" + port + " gestartet ... "); System.out.println("Service: " + service);
Class servlceClass = Class.forName(service); Object serviceObject = servlceClass.newInstance(); whi le (true) ( Socket cllent = server.accept(); new RPCThread(client. serviceObject).start();
public RPCThread(Socket client. Object serviceObject) thls.cllent = client; this.serviceObject = serviceObject; public void run() { try ( ObjectlnputStream in ~ new ObjectlnputStream(client .getlnputStream()); String name ~ (String) in.readObject(); Object[] params ~ (Object[]) in.readObject(); Class[] types ~ new Class[params.length] : for (int i = 0; i < params.length; i++) types[i] ~ params[i].getClass(); Object ret ~ null; try ( Method m ~ serviceObject.getClass().getMethod(name. types) ; ret = m.invoke(serviceObject, params); } // Eine Ausnahme wird als Ergebnisobjekt / / zurückgel i efert catch (InvocationTargetException e) ret ~ e.getTargetException(); catch (Exception e) ( ret = e; ObjectOutputStream out ~ new ObjectOutputStream(client .getOutputStream()) ; out.writeObject(ret); out. fl ush(); in.close() ; out.close(); catch (Exception e) ( System.err.println(e); finally { try { if (cl ient !~ null) cl ient.close(); catch (IOException e)
153
4
154
Client/Server-Anwendungen mit TCP
public static void main(String[] args) throws Exception { if (args.length !- 2) ( System.err.prlntln("java RPCServer "); System.exit(l); int port - Integer.parseInt(args[D]); String service = args[l]; new RPCServer(port, servlce).startServer();
Test
Einige beispielhafte Service-Methoden sind in der Klasse DemoService implementiert. Test.Cl ient enthält den Aufruf dieser Methoden.
pub 1i c cl ass I:€ITDSer vi ce ( public String getEcho(String text) ( return text; public int getSumrre(lnteger x , Integer y) ( return x + y; pub 1i c Date getOate () return new Date(); public void sendMessage(String msg) System.out.println(msg); public Vector getMessages(String file) throws IDException ( Vector lines = new Vector(); BufferedReader in - new BufferedReader( new FileReader(file)); String line;
4.7
Remote Procedure Call
155
while ( (l i ne - in.read Line()) !- null) ( 1i nes. add(l i ne); )
in.close() ; return 1i nes;
import java.utll.Date; import java.utll.Vector;
TestClient
public class TestClient ( public static void main(String[J args) throws Exception ( String host - args[OJ; int port - Int eger .parseI nt (args [I J ) ; RPCClient rpcClien t - new RPCClien t(host. port); Object[J params l - ( "Hallo" l: String s - (St ri ng) rpcClient.call ("get Echo" . paramsl); System. out. pr i nt 1n("getEcho; + s); Object[J params2 - ( 10. 33 ); int summe - (Integer) rpcClient call("getSumrre". params2); System. out. pr i nt 1n("getSumrre; + surrrne); Object[J params3 - (); Date date - (Date) rpcClient ca11 ("getOate". params3); System. out. pr i nt 1n("getOate; + date); Object[J params4 - ( "Dies ist ein Test." ); rpcClient.call ("sendl'essage". params4); Object[J params5 - ( "rnsg. txt" ); @SuppressWarnings("unchecked") Vector v = (Vector
faultCode O
faultStrlng No such handler: test testFault Ausgabe, No such handler: test.testFault Das Programm MyReporter (Kapitel 5.6) kann genutzt werden, um die zwischen Client und Server ausgetauschten Daten aufzuzeichnen.
Die Verwendung von HTIP als Transportprotokoll zur Übertragung der SOAP-Nachrichten ist am weitesten verbreitet. Gründe hierfür sind, •
HTTP-Server sind ausgereift und weit verbreitet,
•
HTIP ermöglicht Verbindungen über Firewalls (Port 80),
•
SOAP-Nachrichten können einfach in HTTP-Aufrufe eingebunden werden,
•
Sicherheitsmechanismen für HTIP Nachrichten angewendet werden.
können
auf
SOAP-
SOAP-Nachrichten können auch mit anderen Protokollen (z. B. SMTP, FTP, JMS) übertragen werden. Das ermöglicht einen zeitversetzten (asynchronen) Datenaustausch zwischen Client und Web Service. Aufgrund der hier eingesetzten Technologien sind Web Services unabhängig vom Betriebssystem und von der für die Implementierung gewählten Programmiersprache. Mit Hilfe der XML-basierten Sprache WSDL (Web Service Descrip- WSDL tion Language) werden Web Services beschrieben. WSDLDokumente enthalten eine Spezifikation der Funktionsschnittstellen sowie technische Details zum Web Service, z. B. das verwendete Transportprotokoll und die Adresse, unter der der Web Service erreicht werden kann. Nachfolgend ist ein Ausschnitt des WSDL-Dokuments des hier benutzten Beispiels aufgeführt. WSDL-Dokumente können von Client-Anwendungen automatisch ausgewertet werden, um die genauen Aufrufmodalitäten eines Web Service zu erfahren, Auf dieser Basis können Clients dann zur Laufzeit den Programmcode zum Aufruf des gerade benötigten Web Service dynamisch einbinden. Damit können die Abhängigkeiten zwischen Service-Anbietem und Service-Nutzem auf ein notwendiges Minimum reduziert werden (lose Kopplung).
Die Datei WEB- INFlsun jaxws-xml enthält die Endpoint-Beschreibung des Web Service,
Hier können mehrere -Tags auftreten. Die Bytecodes des Web Service aus dem letzten Abschnitt können in einer jar-Datei zusammengefasst werden. Im Verzeichnis Prog09021bi n ausführen, jar cf artikel.jar delTD/artikel/* artikel.jar dann nach WEB-INFIlib kopieren. Die Context-Datei ws.xm1 mit dem Inhalt
muss anschließend nach \conf\Catalina\localhost kopiert werden. Nun kann Tomcat gestartet werden:
\bin\startup.bat Test mit Web Browser: http://loca lhost:8080/ws/artikel
9.6
Oneway-Operationen
Mit der Annotation @Oneway kann für eine voi d-Methode festgelegt werden, ob eine Operation eine leere Nachricht als Antwort erhalten soll. Beim Aufruf einer solchen Methode wird die Kontrolle an den Aufrufer zurückgegeben, bevor die Methode ausgeführt wird.
9.6
Oneway-Operationen
345
Das folgende Beispiel zeigt den Unterschied zwischen dem Auf- Programm 9.4 ruf einer normalen VOl d-Methode und einer Oneway-Operation.
Ruft man das Client-Programm auf, sieht man deutlich den Unterschied: send! kehrt erst nach 5 Sekunden zurück, send2 praktisch sofort. SOAP-Antwort von sendl :
Bei der Ausführung von send2 wird lediglich der HTIP-Header HTTP/! .! 202 Accepted als Antwort übertragen, keine SOAP-Nachricht.
9.7
Asynchrone Aufrufe
Der Client kann eine Methode so aufrufen, dass der Aufruf nicht so lange blockiert, bis das Ergebnis vorliegt. Vielmehr kann der Client weiter arbeiten. Er wartet asynchron auf das Ergebnis. Polling oder Callback
Programm 9.5
Hierzu gibt es zwei Möglichkeiten, •
Der Client fragt von Zeit zu Zeit nach, ob die Antwort da ist (polling), oder
•
er stellt eine Callbac~Methode bereit, die dann automatisch aufgerufen wird, wenn die Antwort vorliegt.
Wir demonstrieren diese beiden Möglichkeiten anhand eines einfachen Beispiels. Der Web Service ermittelt, ob eine bestimmte Menge eines Produkts verfügbar ist. Die Bearbeitung kann unterschiedlich lange dauern (3 bis 8 Sekunden).
public interface Product ( @Web Method boolean isAvailable(@WebParam(name ~ "id") int id. @WebParam(naJn2 = "amount") i nt anount.) ;
package demo.product;
lmport java.utll.Random;
Implementierung Productlmpl
lmport javax.jws.WebServlce; @WebService(endpoi ntln terface ~ "demo.product. Product") pub1i c cl ass Productlmp 1 i mp 1ements Product ( public boolean isAvailable(int id. int amount) ( Random r ~ new Random(); int delay ~ 3 + r.next lnt(6); try ( Thread.sleep(delay * 1000); ) catch ( InterruptedException e) ( )
return r.nextBoolean();
wsimport erzeugt aus dem WSDL-Dokument die für die Entwicklung des Clients nötigen Artefakte. Damit Code für den asynchronen Aufruf (Polling, Callback) ge-
neriert wird, muss folgendes XML-Dokument erstellt werden und beim Aufruf von WSl mport berücksichtigt werden: true
bindings-Dokument custom.xml für tösimport
9 Web Services mit JAX-WS
348 Ant Task
Es folgt der vollständige Quelleode des Client-Programms, der im Anschluss erläutert wird. ProductClient
import ja va.util .concurrent. Execut i onException; import javax.xml .ws .AsyncHandl er ; import javax.xml .ws .Response ; import client. IsAvailableResponse; import client Product; import client Product ImplService; public class ProductClient private Product port; public ProductClient() ( Product ImplService servic e = new ProductImplService(); port ~ service.getProduct lmplPort(): public stat ic void main(String[] args) ( ProductCli ent client ~ new ProductClient(): client. invokeBlocking(); client. invokeNonblockingPoll() ; client. invokeNonblockingCallback() ; System.out.println(" Warte auf Antwort"): try ( Thread.sl eep(IOOOO): catch ( Int erruptedExc eption e) (
private voi d invokeBlocking( ) ( blocking ---"): Syst em.out.prin tln("boolean isAvai labl e ~ port.isAvailable( 47I I, 10) : System.out.println ("isAvailable: " + isAvailable):
9.7 Asynchrone Aufrufe
pri vate void invokeNonblockingPoll() ( System.out.println ("nonblocking. polling ---"); Response res = port.lsAvallableAsync( 4711. 10) ; while (!res.is [);me()) ( System.out.println ("Warte auf Antwort"); try ( Thread.sleep( 1000); catch (InterruptedException e) {
try ( IsAval l abl eResponse response = res.get(); boolean lsAvailabl e = response.isRet urn(); System.out.println("isAvailabl e; " + isAvailabl e ); catch (Interrupted Exception e) ( System.err.println (e.g et Message( )); catch (Execut i onExcept i on e) ( System.err.println (e .get Message( ));
pri vate void invokeNonblockingCallback() ( System.out.println ("nonblocking. callback ---"); port.isAvailableAsync( 4711. 10. new MyHandler()); private class MyHandler implements AsyncHandler public voi d handleResponse( Response res) IsAvai l abl eResponse response; try ( response = res.get(); bool ean isAvailable = response.isReturn(); System.out.println("isAvailable; " + isAvailable); catch (Interrupted Exception e) ( System. err.println(e.getMessage()); catch (ExecutionException e) ( System.err.println(e.get Message());
349
9 Web Services mit JAX-WS
350 invokeNonblockingPoll
cl i ent. Pr oduct enthält die Methode
Response< IsAvallableResponse> isAvailableAsync( i nt i d. i nt anount )
Das Interface j avax. xm1 .ws. Response repräsentiert die Antwort des Aufrufs. Es enthält u. a. die beiden folgenden Methoden,
boolean isDone()
T get() throws Int er r upt edE xcept i on . java. uti l .concurrent. Execut l onExceptl on i sDone liefert true, sobald die Antwort vorliegt, sonst fa 1se. get liefert die Antwort, hier ein Objekt vom Typ IsAvai 1ab1eRes pons e. Diese Klasse enthält die boo1ean-Property re turn . Innerhalb einer Schleife wird im Abstand von einer Sekunde nachgefragt, ob die Antwort vorliegt (polling). Ist die Schleife beendet, so kann die Antwort mit get geholt werden. invokeNonblockingCallback
cl i ent. Product enthält ebenfalls die Methode
Fut ure isAvailableAsync(lnt ld, lnt amount, Async Handler< IsAvallableResponse> asyncHandl er) Respons e ist ein Subinterface von Fut ur e. Das Interface ja vax.xml.ws.Async Handler deklariert die Methode
vOl d handleResponse(Response res), die automatisch aufgerufen wird, sobald die Antwort vorliegt. Die innere Klasse MyHand ler implementiert diese Methode für den Typparameter IsAvai 1ab1eResponse .
Nach dem Aufruf von i nvokeNonb1ocki ngea11 back wartet das Programm 10 Sekunden, damit die ma in-Methode nicht endet, bevor der Rückruf per Callback erfolgt ist. Programmausgabe (Beispiel)
nonblocking. polling
Warte Warte Warte Warte Warte
auf auf auf auf auf
Antwort Antwort Antwort Antwort Antwort
isAvailable: false nonblocking. callback
Warte auf Antwort lsAvallable: true
9.8
Web Services und binäre Daten
9.8
351
Web Services und binäre Daten
Bei SOAP-Nachrichten handelt es sich um Xlvll-Dokumente, die ausschließlich Textzeichen enthalten dürfen. Will man nun Nachrichten mit binärem Inhalt übertragen, so kann man die binären Daten vor der Übertragung in eine Texrform überführen (Codierung) und beim Empfänger wieder dekodieren. Wir demonstrieren diesen Sachverhalt anhand eines Web Service , Programm 9.6 der Upload und Download von Dateien anbietet.
public i nt erface Tr ans fe r ( 8'W ebMe t hod void upload( 8'WebParam(narre ~ "narre") String narre. 8'WebParam(narre ~ "data") byte[] data): 8'W ebMe t hod byte[] downl oad(8'WebPar am( narre "narre") String narre) throws Fi 1eNotFoundE xcept i on:
Die byte-Arrays enthalten den Inhalt der Datei. name enthält den Dateinamen.
352 public class TransferImpl ( private final static String DIR
~
"C:/temp":
public void upload(String name, byte[] data) try ( Stri ng path ~ DIR + "/" + name: BufferedOutputStream out ~ new BufferedOutputStream( new FileOutputStream(path)): out write(data): out flush(): out.close() : catch (IOException e) ( throw new WebServiceException("Upload failed"):
public byte[] download(String name) throws FileNotFoundException Stri ng path ~ DIR + "/" + name: File file ~ new File(path): if (!file,exists()) throw new Fi l eNotFoundExcepti on(" Fi l e does not exi st"); try ( ByteArrayOutputStream out ~ new ByteArrayOutputStream(): BufferedInputStream in ~ new BufferedInputStream( new FileInputStream(path)): byte[] buffer ~ new byte[2048]: intn~O:
while ((n ~ in,read(buffer)) out,write(buffer, 0, n): } in,close():
Im generierten WSDL-Dokument ist für den Parameter data der
XML-Schema-Datentyp base64Bi nary eingetragen, Der Up 1oadCl i ent überträgt die Datei test .pdf an den Web Service, der sie dann im Verzeichnis C:\ temp speichert.
9.8
Web Services und binäre Daten
353
lmport java. ia Buf feredInput St ream; import java. la ByteArrayOutputStream; import java. la Fi l eI nput St ream;
UploadClient
import client Transfer; import client Transfer ImplService; public class UploadClient ( public static voi d main(String[] args) ( Transfer ImplService service = new Transfer ImplService(); Transfer port = service.getTransfer ImplPort(); String name ~ "test.pdf"; try ( ByteArrayOutputStream out ~ new ByteArrayOutputStream(); Buffered lnputStream in ~ new BufferedlnputStream( new Fi l el nput St ream(name )) ; byt e[] buffer ~ new byte[2048]; i nt n
=
0;
while ((n ~ in.read (buffer)) out.write(buffer. O. n); } in.close() ;
Die Binärdaten werden nach dem Base64-Verfabren codiert Base64 (siehe Kapitel 6.2). Dabei werden Bytes in eine Folge von ASCIIZeichen umgewandelt.
test.pdf JVBERiOxL ... RU9GDQo:::x::/data>
Die Codierung führt zu einer Vergrößerung des Datenvolumens um ca. ein Drittel.
SOAP-Anfrage
9 Web Services mit JAX-WS
354 DownloadClient
lmport ja va lo.BufferedOutputStream; lmport ja va lo. FlleOutputStream; lmport cllent Trans fer ; lmport cllent Transfer ImplServlce; public class Downl oadCl i ent ( public static voi d main(String [] args) ( Transfer ImplServlce ser Vlce = new Transfer ImplSer vlce(); Transfer port = servlce.getTransfer ImplPort(); Str ing narre ~ "test.pdf"; try ( byte[] data ~ port.download(name); BufferedOutputStream out ~ new BufferedOutputStream( new Fi l eOut putSt re am(narre )) ; out write(data); out fl ush() ; out.close() ; catch (Exception e) ( System.err.println(e.getMessage());
MTOM
SOAP Message Transmission Optimization Mechanism (MTOMy ist ein Verfahren zur Optimierung der übertragung binärer Daten. Der in der SOAP-Nachricht im Base64-Format enthaltene Inhalt wird aus der Nachricht entfernt und in den Anhang verschoben. In der Nachricht selbst befindet sich dann eine Referenz auf die Originaldaten (raw bytes) im Anhang.
optimierte
SOAP-Nachricht
test .pdf
9.8
Web Services und binäre Daten
355
Mit der Annotation @javax.xml.ws.soap.MTOM in der Implementie- Programm9.7 rung des Web Service wird die Optimierung eingeschaltet.
0WebServlce(endpolnt Interface Ii\'1TOM public class Transfer lmpl (
"demo.transfer. Transfer")
MTOM einschalten
Das generierte WSDL-Dokument enthält einen Policy-Eintrag. der aussagt. dass MTOM verwendet wird.