3.9. Erfüllung der Anforderungen

Werbung
Bachelorarbeit
A Generic Database Web Service for the
Venice Lightweight Service Grid
Michael Koch
7. Mai 2008
Betreuer:
Prof. Dr. Paul Müller
Prof. Dr.-Ing. Stefan Deÿloch
Informatik
ICSY - Integrated Communication Systems
Universität Kaiserslautern
•
Postfach 3049
•
67653 Kaiserslautern
Ich erkläre hiermit, die vorliegende Bachelorarbeit selbständig verfasst zu haben.
Die verwendeten Quellen und Hilfsmittel sind im Text kenntlich gemacht und im
Literaturverzeichnis vollständig aufgeführt.
Kaiserslautern, den 7. Mai 2008
( Michael Koch )
Abstract (English)
This paper describes the development of a generic database
service for the Venice lightweight service Grid, which has been developed at the
University of Kaiserslautern. By using Web services any SQL query can be processed
and the corresponding result sets are sent back as strongly typed XML documents.
The accurately dened interface allows an easy-to-use, platform and programming
language independent access to all applications of a Venice federation. Transactions,
prepared statements and parallel communications of split result sets for shorter
latencies are supported, too. Because of a JDBC-like programming interface, the
service can be used intuitively and existing software components can be quickly
adjusted to the new system.
Abstract (German)
Die vorliegende Arbeit beschreibt die Entwicklung eines ge-
nerischen Datenbankdienstes für das an der TU Kaiserslautern entwickelte leichtgewichtige Servicegrid Venice. Durch den Einsatz von Webservices verarbeitet das System beliebige Datenbankabfragen des SQL-Standards und versendet Ergebnismengen nach einem stark typisierten XML-Schema. Die genaue Denition der Schnittstelle und deren Datentypen, erlaubt eine einfache, plattform- und programmiersprachenunabhängige Verwendung durch Anwendungen einer Venicedienstföderation. Zudem werden Funktionen wie Transaktionen, vorkomplierte Statements und
die parallele Übertragung von aufgeteilten Ergebnismengen zur Latenzzeitverringerung unterstützt. Die JDBC-ähnliche Programmierschnittstelle erlaubt die intuitive
Verwendung des Dienstes und die einfache Anpassung vorhandener Softwarekomponenten.
Inhaltsverzeichnis
1. Einleitung
1
2. Grundlagen
3
2.1.
SOA - Serviceorientierte Architekturen
. . . . . . . . . . . . . . . . .
3
2.2.
Leichtgewichtige Servicegrids . . . . . . . . . . . . . . . . . . . . . . .
5
2.3.
Venice - A Lightweight Service-Grid . . . . . . . . . . . . . . . . . . .
5
2.4.
Webservices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6
2.4.1.
WSDL - Web Service Description Language
7
2.4.2.
SOAP
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8
2.4.3.
JAX-RPC - Java API for XML-based RPC . . . . . . . . . . .
10
2.4.4.
XML - Extensible Markup Language
. . . . . . . . . . . . . .
11
. . . . . . . . . .
2.5.
JDBC - Java Database Connectivity
. . . . . . . . . . . . . . . . . .
12
2.6.
Webservices und Connection-Pooling
. . . . . . . . . . . . . . . . . .
13
2.7.
Vergleich mit verwandten Technologien . . . . . . . . . . . . . . . . .
13
3. Architektur
3.1.
3.2.
17
Anforderungen
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
17
Serverseitiges Verbindungsmanagement . . . . . . . . . . . . . . . . .
18
3.2.1.
Problematik . . . . . . . . . . . . . . . . . . . . . . . . . . . .
19
3.2.2.
Architektur des erweiterten Poolings
. . . . . . . . . . . . . .
20
3.2.3.
Verwendung des Zugriobjektpools
. . . . . . . . . . . . . . .
21
3.2.4.
Datenbankoptimierung . . . . . . . . . . . . . . . . . . . . . .
22
3.3.
Initialisierung und Verwaltung des Dienstes . . . . . . . . . . . . . . .
23
3.4.
Rechtemanagement . . . . . . . . . . . . . . . . . . . . . . . . . . . .
23
3.4.1.
Kategorisierung von Anfragen und Benutzern
. . . . . . . . .
24
3.4.2.
Prüfungsvorgang
. . . . . . . . . . . . . . . . . . . . . . . . .
25
3.4.3.
Überprüfungskonzepte
3.4.4.
Verwaltung
3.5.
3.6.
. . . . . . . . . . . . . . . . . . . . . .
25
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
27
. . . . . . . . . . . . . . . . . . . . . . . . . .
28
3.5.1.
Datentransferkonzepte
Architekturen . . . . . . . . . . . . . . . . . . . . . . . . . . .
28
3.5.2.
Datentypumwandlung
30
3.5.3.
Neue Datentypen mit Hilfe von XML-Schemadateien
3.5.4.
Die JDBC API und Typenzwang
3.5.5.
Nachrichtengröÿe
3.5.6.
Sicherheit - Begrenzung der Datentypen
. . . . . . . . . . . . . . . . . . . . . .
. . . . .
30
. . . . . . . . . . . . . . . .
33
. . . . . . . . . . . . . . . . . . . . . . . . .
34
. . . . . . . . . . . .
35
Implementierung der Schnittstelle . . . . . . . . . . . . . . . . . . . .
35
3.6.1.
Die Operationen
3.6.2.
Die Operationen
3.6.3.
query und execute . . . . . . . . . .
getPermissions und setPermissions
. . . . .
36
. . . . .
38
Fehlerbehandlung . . . . . . . . . . . . . . . . . . . . . . . . .
38
vii
3.6.4.
3.7.
Erweiterung - Speicherung von Zustandsinformationen
. . . .
39
Nutzung des Dienstes . . . . . . . . . . . . . . . . . . . . . . . . . . .
40
3.7.1.
Einführung der SOAP-JDBC-Bridge
. . . . . . . . . . . . . .
40
3.7.2.
Ein Vergleich
. . . . . . . . . . . . . . . . . . . . . . . . . . .
40
query -Operation
3.8.
Schematische Darstellung: Ein Aufruf der
. . . . . .
42
3.9.
Erfüllung der Anforderungen . . . . . . . . . . . . . . . . . . . . . . .
43
3.10. Umsetzung der SOA-Konzepte . . . . . . . . . . . . . . . . . . . . . .
44
4. Anwendung - Ein Bibliotheksdienst
47
5. Evaluierung
51
5.1.
Ergebnisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
53
5.2.
Belastungstest . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
54
5.3.
Ergebnisvergleich
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
55
5.4.
Sonderfall: Aufteilung von Ergebnismengen . . . . . . . . . . . . . . .
55
6. Zusammenfassung und Ausblick
57
A. Datenbankdienst: Schnittstellen
59
A.1. VSC - Service-Operationen . . . . . . . . . . . . . . . . . . . . . . . .
59
A.2. VSC - Service-Endpunkt . . . . . . . . . . . . . . . . . . . . . . . . .
59
A.3. WSDL - Abstract Service Denition . . . . . . . . . . . . . . . . . . .
59
A.4. WSDL - Service Bindings
59
. . . . . . . . . . . . . . . . . . . . . . . .
A.5. WSDL - Concrete Service Denition
. . . . . . . . . . . . . . . . . .
B. Rechtemanagement: Reguläre Ausdrücke
B.1. Ausnahmen
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
C. Datentypumwandlung
59
65
66
69
C.1. XML-Schema
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
69
C.2. Codebeispiele
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
73
C.2.1. Serverseite (TypeMapper)
C.2.2. Clientseite (ResultSet)
. . . . . . . . . . . . . . . . . . . .
73
. . . . . . . . . . . . . . . . . . . . . .
74
Abbildungsverzeichnis
77
Tabellenverzeichnis
79
Literaturverzeichnis
83
1. Einleitung
In der heutigen Zeit werden vor allem im Bereich der Datagrids zuverlässige und
performante Datenbanksysteme benötigt. Hier müssen alle Arten lose gekoppelter
Applikationen zusammenarbeiten und Zugri auf verteilte Daten erhalten. Dabei
werden die meisten dieser Daten in relationalen Datenbanksystemen verwaltet. Es
gibt zwar Technologien (JDBC, ODBC, etc.) die einen uniformen Zugri auf diese
Datenstrukturen erlauben, allerdings weisen diese oft eine hohe Komplexität auf.
Zudem wird keine volle Interopberabilität geboten und dem Clienten wird durch die
Verwaltung verschiedener Treiber ein groÿer Kongurationsaufwand überlassen. Deshalb sind in Grids meistens mehrere proprietäre Ansätze im Einsatz. Des weiteren
werden diese Techniken beispielsweise auch durch Firewalls beschränkt. Es werden
also neue Konzepte benötigt, welche Wege zum Zugri auf heterogene Datenbanken
erlauben, während beliebige Clientsysteme mit Hilfe eines einzigen Standards darauf zugreifen können. Dabei sollten auch Sicherheitsaspekte beachtet werden und die
Funktionalität unabhängig von der eingesetzten Sprache und Plattform anwendbar
sein.
Das an der TU Kaiserslautern entwickelte Servicegrid
Venice
stellt ein Framework
für eine Entwicklung in diesem Bereich zur Verfügung. Es bietet einen Single-SignOn-Mechanismus, welcher eine gesicherte Authentizierung innerhalb im Internet
verteilter Domänen in einer gemeinsamen Dienstföderation ermöglicht. Ein Informationbroker ermöglicht weiterhin die performante Verteilung von Informationen
mit Hilfe von modernen P2P-Systemen über die gesamte Föderation hinweg. Dabei
werden die Prinzipien servicerorientierter Architekturen standardmäÿig unterstützt.
In diesem Umfeld benötigt
Venice
zur Unterstützung von Datagrid-Funktionalität
Zugri auf relationale Datenbanksysteme.
Auf dieser Grundlage wird in dieser Arbeit ein generischer Datenbankdienst entwickelt, welcher die Ausführung von beliebigen Datenbankabfragen des SQL-Standards erlaubt. Dabei wird durch den Einsatz von Webservices eine generische Schnittstelle zur Verfügung gestellt. Diese Technologie ermöglicht modulare, selbst-beschreibende und eigenständige Komponenten, die über das Internet abrufbar sind. Da es
sich bei diesen auf XML basierenden Techniken um oene Standards (W3C, WS*) handelt, gibt es keine die Plattform und die Programmiersprachen betreenden
Einschränkungen.
Auf dieses Ziel hinarbeitend werden in Kapitel 2 zuerst die Basistechnologien erläutert. Theoretische Grundlagen wie serviceorientierte Architekturen (SOA) und
leichtgewichtige Servicegrids werden neben Webservices, XML sowie Datenbanktechnologien und dem Connectionpooling vorgestellt.
1
2
1. Einleitung
Darauolgend wird die Architektur des neuen Dienstes in Kapitel 3 unter Beachtung von Design- und Funktionsanforderungen entwickelt. Bei diesem Kapitel handelt es sich um den Hauptteil der Arbeit, in welchem die Lösungen zu zahlreichen
Konzepten und Problemen wie beispielsweise dem erweiterten Verbindungsmanagement, Datentypumwandlungen, Übertragungskonzepten, dem erweiterten Rechtesystem und SOAP/XML-Nachrichtengröÿen erarbeitet werden. Zum Abschluss wird
hier die Entwicklung der SOAP-JDBC-Bridge erläutert, welche ein Wrappersystem
zur intuitiven und optimierten Nutzung der Webserviceschnittstelle vorstellt.
Anschlieÿend verdeutlicht Kapitel 4 die einfache Nutzung des generischen Datenbankdienstes anhand der JDBC-ähnlichen API, indem ein einfacher Bibliotheksdienst mit einer zugehörigen graschen Oberäche präsentiert wird.
Am Ende wird der Dienst in Kapitel 5 mit Hilfe von verschiedenen Testszenarien ausführlich evaluiert.
Kapitel 6 schlieÿt die Arbeit mit einer Zusammenfassung und einem Ausblick ab.
2. Grundlagen
Für die Entwicklung des Datenbankdienstes ist ein grundlegendes Verständnis der
verschiedenen Basistechnologien notwendig. In diesem Abschnitt werden zuerst die
Konzepte von serviceorientierten Architekturen (SOA) und deren Bedeutung für
Servicegrids erläutert. In diesem Zusammenhang wird anschlieÿend die Rolle von
Webservices beschrieben. Darauolgend wird die Open-Source-Datenbank PostgreSQL und deren Verwendung mit Java vorgestellt.
2.1. SOA - Serviceorientierte Architekturen
Bei serviceorientierten Architekturen (SOA) [Mel07, Erl05, BCK03] handelt es sich
um ein abstraktes Konzept einer Software-Architektur, bei der das Anbieten, Suchen und Nutzen von Diensten über ein Netzwerk im Mittelpunkt steht. Dabei
werden Applikationen als wiederverwendbare Dienste repräsentiert. Es soll eine offene, plattform- und programmiersprachenunabhängige Nutzung ermöglicht werden.
Frühzeitige Designentscheidungen sind für die Implementierung, Organisation und
die Qualitätsmerkmale eines Systems sehr wichtig. Bei der Serviceorientierung wird
die Unternehmenslogik um eine abstrakte Dienstschicht zwischen Geschäftsebene
und Applikationslogik ergänzt. Die Kapselung in Schichten bietet die eigentliche
Abstraktion und ermöglicht eine leichte Wiederverwendbarkeit. Bild 2.1 verdeutlicht
diesen Aufbau und deutet die Zusammenhänge zwischen verschiedenen Diensten an.
Dabei handelt es sich bei einem Dienst um eine Gruppierung von zusammengehörenden Operationen, welche die Logik zum Verarbeiten von Nachrichten innerhalb
einer Arbeitseinheit darstellen. Diese Schichtarchitektur wird durch die acht zentralen Merkmale einer SOA ermöglicht:
Wiederverwendbarkeit
Ein Dienst soll durch generische Implementierung mög-
lichst unverändert in einem anderen Kontext eingebettet werden können. Dies
beschleunigt eine Portierung erheblich.
Dienst-Kontrakt
Nutzer sind von einer formalen Spezikation aller Nachrichten,
Operationen und dem eigentlichen Endpunkt abhängig. Diese Informationen
werden über WSDL-Schnittstellen bereitgestellt (siehe Kapitel 2.4.1).
Lose Kopplung
Zur Laufzeit ist nicht direkt bekannt welcher Dienst aufgerufen
wird, sondern Anwendungen oder Dienste werden erst bei Bedarf gesucht, gefunden und dynamisch eingebunden. Zum Zeitpunkt der Übersetzung ist also
nicht unbedingt erkennbar wer oder was zur Laufzeit aufgerufen wird. Zudem
sollte ein Client den Dienst ohne Kenntnis des internen Aufbaus nutzen können. Änderungen in der Implementierung dürfen sich nicht auf den Nutzer
auswirken.
3
4
2. Grundlagen
Business
Process
Layer
Business
Logic
3
Service
Layer
4
2
Service
Interface
Definitions
1
Application
Logic
Application
Layer
.NET Application
1
Singel Sign On (SSO)
2
Physical
Service
J2EE Application
Database Service
3
Library Service
4
File System Service
Abbildung 2.1.: SOA - Schichtenmodell nach [Erl05]
Abstraktion
Die Schnittstellen werden als eine Art Blackbox deniert hinter der
alle Details versteckt werden. Hierdurch wird die Wiederverwendbarkeit und
lose Kopplung unterstützt. Bei dem Datenbankdienst wird dieses Verhalten
durch Entwicklung von unterstützenden Klassen auf Clientseite erreicht (siehe
Kapitel 3.7.1).
Zusammensetzbarkeit
Dienste können Logik aus anderen Quellen oder Diensten
beziehen. Auch dieses Konzept ist eine Art der Wiederverwendbarkeit, wobei
deutlich wird, dass die Granularität und Standardisierung der Operationen
eine groÿe Rolle spielt.
Autonomie
Jeder Dienst muss für die genutzte, zugrundeliegende Technologie ei-
genverantwortlich sein. Nur hierdurch wird eine einfache Weiterentwicklung
ermöglicht. Auch hier ndet sich wieder das Prinzip der losen Kopplung.
Zustandslosigkeit
Während der Bearbeitung einer Dienstanfrage werden Zustands-
informationen gespeichert. Soweit möglich sollten nur wenige Informationen
über einen kurzen Zeitraum bereitgestellt werden, um eine gute Skalierbarkeit
zu erreichen. Nach dem Aufruf einer Operation sollten im besten Fall keine
Zustandsinformationen mehr vorliegen.
Aundbarkeit
Durch Metadaten, die in einem Verzeichnis oder Repository abge-
legt werden, wird die Suche nach der richtigen Funktionalität unterstützt und
eine universelle Erreichbarkeit garantiert. Dieses Konzept wird beispielsweise
2.2. Leichtgewichtige Servicegrids
5
durch den Einsatz von UDDI implementiert. Venice setzt hier jedoch P2PTechnologien ein, um diese Informationen über eine Föderation zu verteilen
(siehe Kapitel 2.3).
Bei diesen acht zentralen Konzepten spielen auÿerdem weiterhin die Einhaltung von
Standards, die Einfachheit und Sicherheit eine wichtige Rolle. Nur durch Standards
kann eine Kommunikation zwischen Anwendung und einem unbekannten Dienst unabhängig von Programmiersprache und ausführender Plattform erreicht werden. Nur
so ist eine breite Akzeptanz gegeben. Die Einfachheit wird erreicht, indem die konkrete Implementierung von der Schnittstelle getrennt und das Prinzip der losen
Kopplung eingesetzt wird. Ein weiterer Aspekt behandelt die Sicherheit, da in immer gröÿerem Umfang sicherheitsrelevante oder kritische Daten anfallen. Vor allem
in Servicegrids sind die Vertraulichkeit, Berechtigung und die Verbindlichkeit der
Kommunikation von Bedeutung. Die nächsten zwei Kapitel beschreiben die Bedeutung dieses Konzeptes für Servicegrids und deren konkrete Umsetzung in Venice,
einem leichtgewichtigen Servicegrid.
2.2. Leichtgewichtige Servicegrids
Um den Begri Servicegrids [HGZM07] abgrenzen zu können, muss zuvor die Funktionalität eines Grids beschrieben werden. Gridcomputing [Fos03] bezeichnet die
gemeinschaftliche, koordinierte, transparente und gesicherte Nutzung von IT-Ressourcen über geograsche und institutionale Grenzen hinweg. Das Ziel dieser Umgebung ist eine zuverlässige Verteilung der Ressourcen an die jeweiligen Konsumenten
unter geringen Zusatzkosten und einer hohen Verfügbarkeit. Dabei kann zusätzlich
zwischen Computegrids, die Ressourcen mit hohen Durchsatzraten für zeitintensive
Berechnungen bereitstellen, und Datagrids, welche groÿe sichere Speicherkapazitäten
anbieten, unterschieden werden. Servicegrids wiederum setzen eine Ebene darüber an
und nutzen diese Funktionen, indem sie spezielle Dienste für den Endnutzer zur Verfügung stellen. Leichtgewichtig bedeutet in diesem Zusammenhang zusätzlich eine
leichte Installierbarkeit, Administration und Nutzbarkeit. Konsumenten sollen somit
die Möglichkeit haben, die Dienste des Servicegrids mit Hilfe von graschen Oberächen zu benutzen, ohne spezielle Software installieren zu müssen.
Venice
[HGM]
stellt eine der ersten Umsetzungen eines leichtgewichtigen Servicegrids dar.
2.3. Venice - A Lightweight Service-Grid
Das
Venice Servicegrid
[HGM, HGZM07] ist ein oenes Framework für verteilte
Applikationen, das eine leichte Erstellung, Installation, Integration und Benutzung
von Diensten erlaubt. Dies wird durch eine Virtualisierung der Ressourcen und Abstraktion von der zugrundeliegenden Technologie erreicht. Dabei ist es für Dienstanbieter möglich, eine einzelne Sicherheitsdomäne mit Diensten für alle autorisierten
Benutzer anzubieten. Des weiteren können mehrere Domänen zu einer Föderation zusammengeschlossen werden, wodurch Dienste von anderen Domänen ebenfalls
6
2. Grundlagen
verwendet werden können. Jede Domäne und deren Dienste werden vom eigenen Anbieter betreut, wobei
Venice
einen standardisierten sicheren Austausch von Daten
und Zugri auf Dienste durch den Einsatz von Webservices und P2P-Technologien
(wahlweise JXTA [Gon01] oder Bamboo [Rhe04]) erreicht. In Bild 2.2 ist erkennbar,
wie mehrere Domänen mit eigenen Diensten mittels
Bamboo kooperieren. Für
Venice
Venice
über das Internet und
wurden bereits die verschiedensten Dienste ent-
wickelt. Beispielsweise ein Single-Sign-On-Mechanismus (SSO) oder ein File System
Service. Diese Arbeit fügt dem Servicegrid nun einen generischen Datenbankdienst
hinzu.
Provider C
Provider B
WS
IB
IB
WS
Bamboo
Provider D
IB
WS
WS
Internet
Provider A
Provider N
IB
Client A1
WS
WS
Client A2
IB = Information Broker
IB
WS
Client An
WS
WS = (any other) Web Service
Client N1
Bamboo
= Bamboo Peer-to-Peer Network
Abbildung 2.2.: Venice aus Provider-Perspektive [HGM]
2.4. Webservices
Webservices [ZTP03] stellen einen sprach- und umgebungsunabhängigen
dard
[w3c] zur Verfügung wie sie bei
Dabei bietet
XML eine
SOA
und in
gemeinsame Sprache.
tenaustausch zur Verfügung und
WSDL
SOAP
Service-Grids
W3C-Stan-
benötigt werden.
stellt die Semantik für den Da-
beschreibt die Funktionalität des Webser-
vices. Bild 2.3 zeigt einen Überblick über die verwendeten Techniken, die in den folgenden Kapiteln genauer erläutert werden. Weitere Spezikationen, wie Sicherheit,
SOA wichtig sind, werden
Web Services Interoperability Organization (WS-I) [wsi], dem World Wide
Transaktionen und Management, die ebenfalls für eine
von der
Web Consortium (W3C) [w3c] und der Organization for the Advancement of Structured Information Standards (OASIS) [oas] vorgegeben. Je nach Bedarf ist es bei
Webservices möglich, eine RPC-basierte (Remote Procedure Call) Kommunikation
oder eine dialogorientierte Kommunikation zu implementieren [NSS03]. In
Venice
ist bisher nur die erste Kommunikationsart verfügbar. In Kapitel 3.5.3 wird diese
Thematik nochmals bezüglich des Datenbankdienstes diskutiert. Zur schnellen und
2.4. Webservices
7
Verzeichnisdienst
Finden
WSDL+UDDI
Registrierung
WSDL+UDDI
Binden
Client
Dienstanbieter
Kommunikation
SOAP
XML-basierter Nachrichtenaustausch
über HTTP, FTP, IIOP, etc.
Abbildung 2.3.: Webservices - Aufbau und Kommunikation
leichten Entwicklung wurde der
Venice Service Compiler (VSC)
entwickelt. In Bild
2.4 ist die Funktionsweise des VSC-Compilers nachzuvollziehen.
2.4.1. WSDL - Web Service Description Language
Die
Web Service Description Language
(WSDL) [ZTP03] repräsentiert die Schnitt-
stellen für alle verfügbaren Dienste innerhalb einer Domäne, beziehungweise innerhalb des
Service-Grids .
Applikationen, die sich automatisch mit diesen Endpunk-
ten verbinden, brauchen Informationen über den Dienst. Wie in Bild 2.4 zu sehen
ist, werden dazu drei WSDL-Dateien erzeugt. Eine
Concrete Service Denition
lie-
fert Informationen über den Endpunkt (Name, Netzwerkadresse, Port). Die Datei
mit den
Service Bindings
beschreibt das Protokoll und die Operationen (SOAP-
Encodierung und Namespace). Das Dokument mit den
Abstract Service Denitions
deniert die konkreten Denitionen der Operationen, deren Parameter und Rückgabewerte sowie abstrakte Datentypen. Unter Anhang A nden sich gekürzte Versionen der VSC-Schnittstellen und deren erzeugten WSDL-Schnittstellen. Bei WSDLDateien handelt es sich um eine Art Vertrag, deniert in XML, dem die Applikation
zustimmen muss (siehe Dienst-Kontrakt unter 2.1). Die XML-Elemente sind dabei
in einer Schemadatei mit dem Namespace
http://schemas.xml.soap.org/wsdl/
(abhängig von der Version) genau beschrieben. Es kann mehrere verschiedene Implementierungen für eine abstrakte Schnittstelle geben, wobei die für die Applikation
am besten nutzbare gewählt wird.
Des weiteren gibt es zwei Wege eine Schnittstelle zu denieren. Entweder man programmiert den Quellcode zuerst in einer beliebigen Programmiersprache und generiert daraus die entsprechende WSDL-Dateien oder man entwirft die WSDL-Datei
und erzeugt alle nötigen Stubs und Skeletons für die Anwendung automatisch. Hier
wird der zweite Fall bevorzugt, da so eine starke Typisierung möglich ist, die unabhängiger von Plattform und Programmiersprache durch den Einsatz von XMLSchemadenitionen ist [AAM06]. Der Entwurf des WSDL-Dokumentes wird hierbei
vom VSC-Compiler unterstützt. Die Syntax zum Schnittstellenentwurf wurde auf
das Nötigste reduziert, um Fehlerquellen zu vermeiden. In Listing 2.1 ist eine der
VSC-Schnittstellen für den Datenbankdienst zu sehen (weitere siehe Anhang A).
WSDL an sich bietet keine Funktionalität, um einen Dienst zu nden. Hierzu kann
8
2. Grundlagen
.vsc
Two Files:
Operations
Service Endpoint
read
vsc
generate
.wsdl
Abstract
Service
Definition
Service Endpoint
- DatabaseService.wsdl
Service
Bindings
Operations
- Database-SOAP.wsdl
Concrete
Service
Definition
Concrete Operations and Messages
- Database.wsdl
import
.wsdl
import
wsdl2
java
read
.wsdl
generate
.wsdd
.java
Binding
Impl
Binding
Skeleton
Stubs
Skeleton
Classes
pack into JAR file, deploy and use it
Axis 1.4
Abbildung 2.4.: Venice Service Compiler (VSC)
ein Verzeichnisdienst (Universal Description, Discovery and Integration - UDDI )
verwendet werden, in welchem dann die Dokumente abgelegt werden. Über diesen
Webserver können Informationen über Organisationen die Webservices anbieten, sowie Beschreibungen und technische Informationen gespeichert und abgefragt werden.
Venice steht der DomainInformationService lokal in jeder Domäne zur Verfügung
und ein Information Broker ermöglicht die Verteilung domänenübergreifend in einer
In
Föderation mit Hilfe von P2P-Technologien.
2.4.2. SOAP
Während WSDL die Schnittstelle und ihre Operationen bestimmt, wird über SOAP
[ZTP03] das eigentliche Nachrichtenformat mittels XML deniert. Dabei kann die
Nachricht mit verschiedenen Transportprotokollen wie beispielsweise HTTP oder
SMTP verwendet werden. In Bild 2.2 ist eine SOAP-Nachricht erkennbar. Sie besteht aus drei Komponenten, dem Envelope, dem optionalen Header und dem Body.
Der Envelope ist eine Art Umschlag für die anderen zwei Elemente und repräsentiert
2.4. Webservices
9
interface DBControl {
namespace
" http: // www . icsy . de / koch / wsdl / database /" ;
location
" http: // www . icsy . de /~ koch / wsdl / de / icsy / services / basic
/ database / " ;
types sql
" http: // www . icsy . de /~ koch / schema / sql . xsd " ;
types sqlFaults " http: // www . icsy . de /~ koch / schema / sqlFaults . xsd " ;
types
domain
" http: // www . icsy . de /~ venice / types / domain . xsd " ;
types
faults
" http: // www . icsy . de /~ venice / types / faults . xsd " ;
operation sql:SQLValueSet query ( domain:SSOInformation sso ,
sql:SQLQuery exec ) throws sqlFaults:SQLPermissionFault ,
sqlFaults:SQLFault , faults:BaseFault ;
}
Listing 2.1: VSC - Operationen
die Nachricht. Der Header bietet verschiedene Möglichkeiten, um die Verarbeitung
der Nachricht zu erweitern. Hierüber sind beispielsweise Einträge für Authentifzierung oder
QoS -Anforderungen
(Quality of Service) bestimmbar, welche auf dem
Weg zum Ziel gelesen werden können. Der Body enthält alle Daten für den nalen
Empfänger der Nachricht.
Für den weiteren Verlauf dieser Arbeit ist ein grundlegendes Verständnis über die
Umwandlung (Kodierung) von Datentypen in SOAP notwendig. Plattformen und
Programmiersprachen haben in der Regel unterschiedliche Repräsentationen von
Datentypen die inkompatibel zueinander sind. Deshalb muss das benutzte Transportprotokoll die Datentypen von einer Repräsentation in eine allgemeine Transportform und wieder zum Original wandeln können. Diesen Vorgang nennt man
Serialisierung und Deserialisierung. Nur hierdurch wird eine Kommunikation in einer heterogenen Umgebung zwischen unterschiedlich programmierten Applikationen
möglich. Die Umwandlung in das Transportformat übernimmt
direkt. Es wer-
Verbundtypen unterstützt. SOAP deniert diese Datentypen
nicht selbst, sondern benutzt XML-Schemadateien (XSD ). Wie diese Schemata auf-
den
einfache Typen
SOAP
und
gebaut werden, wird in Kaptiel 2.4.4 genauer beschrieben. Verbundtypen wiederum
bestehen aus einer Anzahl von untergeordneten Elementen. Hierüber ist die Denition von Strukturen und multidimensionalen Arrays möglich. In Listing 2.2 sieht
man ein Beispiel einer SOAP-Nachricht, die beim Aufruf der Funktion query des
Datenbankdienstes erzeugt wird (siehe Listing A.5 für die entsprechende WSDLSchnittstellendenition). Im folgenden Kapitel wird beschrieben auf welchem Weg
dieses Konzept für Java umgesetzt wird.
< soapenv:Envelope xmlns:soapenv = " http: // schemas . xmlsoap . org / soap /
envelope / " xmlns:xsd = " http: // www . w3 . org /2001/ XMLSchema "
xmlns:xsi = " http: // www . w3 . org /2001/ XMLSchema - instance " >
< soapenv:Header >
</ soapenv:Header >
< soapenv:Body >
< ns1:query soapenv:encodingStyle = " http: // schemas . xmlsoap .
org / soap / encoding / " xmlns:ns1 = " DBControlService " >
< ns1:arg0 href = " # id0 "/ >
</ ns1:query >
< multiRef id = " id0 " soapenc:root = " 0 " soapenv:encodingStyle = "
http: // schemas . xmlsoap . org / soap / encoding / " xsi:type = "
10
2. Grundlagen
ns2:SQLQuery " xmlns:soapenc = " http: // schemas . xmlsoap . org /
soap / encoding / " xmlns:ns2 = " http: // www . icsy . de / koch / types /
sql / " >
< dbname xsi:type = " xsd:string " > test </ dbname >
< dbtype xsi:type = " ns3:String " xmlns:ns3 = " http: // www . icsy . de /
types / basic /" > own </ dbtype >
< queryString xsi:type = " xsd:string " > select * from test </
queryString >
< valueTypes xsi:type =" ns4:IntArray " xsi:nil = " true " xmlns:ns4 =
" http: // www . icsy . de / types / basic / " / >
< values xsi:type = " ns2:PreparedValues " xsi:nil = " true " / >
</ multiRef >
</ soapenv:Body >
</ soapenv:Envelope >
Listing 2.2: SOAP-Nachricht
2.4.3. JAX-RPC - Java API for XML-based RPC
Die
Java API for XML-based RPC (JAX-RPC) [NSS03] ermöglicht die EntwickRPC -basierten Anwendungen durch Bereitstellung einer standardisierten
lung von
API
(Application Programming Interface). In diesem Fall wird die Funktionali-
tät von Apache AXIS [axi], einer Open Source
SOAP -Engine,
welche unter einem
Tomcat-Webserver [tom] läuft, angeboten. Ein Client kann somit entfernte Methoden aufrufen, die von einem Anbieter als Dienst veröentlicht wurden. Dabei bietet
JAX-RPC
die Umwandlung zwischen
WSDL-basierten
Dienstbeschreibungen und
SOAP Nachrichten versteckt, indem die Datentypen automatisch von Java nach XML über-
Javaklassen an. Des weiteren wird die Komplexität der zugrundeliegenden
setzt werden. Tabelle 2.1 zeigt die bereitgestellten Datentypen und deren Umwandlung [ZTP03]. In Kapitel 3.5.3 wird dieses Thema noch einmal in Bezug auf die Datentypen der Datenbank präzisiert.
JAX-RPC
garantiert dabei die Interoperabilität
Java Type
Type
boolean
xsd:boolean
byte
xsd:byte
short
xsd:short
int
xsd:int
long
xsd:long
oat
xsd:oat
double
xsd:double
java.lang.String
xsd:string
java.math.BigInteger
xsd:integer
java.math.BigDecimal
xsd:decimal
java.util.Calendar, java.util.Date
xsd:dateTime
byte[], Byte[]
xsd:base64Binary
JavaBean T
xsd:complexType T
Tabelle 2.1.: Java-XML-Mappings
2.4. Webservices
11
SOAP -basierten Webservices. Beispielsweise kann der Client auf diesem Weg
auch mit Microsoft .NET implementiert sein. Beim Aufruf einer Methode werden
also die Javaparameter dynamisch in XML-Datentypen umgewandelt, über das Me-
zu allen
dium übertragen und auf der Gegenseite wieder übersetzt. Das Gleiche geschieht bei
der Rückgabe des Ergebnisses. Zusätzlich werden die
Klassen (Stubs, Ties) übersetzt. In
Venice
WSDL-Schnittstellen in JavaVSC -Compiler
wird dies ebenfalls vom
übernommen (siehe Bild 2.4).
2.4.4. XML - Extensible Markup Language
Für den Datenbankdienst ist die Denition von neuen Datentypen zur Kommuni-
SOAP zwischen Dienstanbieter und Client notwendig. Hierzu werden
XML-Schemata (XSD -Dateien) [ZTP03] verwendet. In diesem Abschnitt werden
nicht alle Möglichkeiten von XML erklärt, sondern nur die in späteren Abschnitkation mittels
ten benötigten Informationen hervorgehoben. Umfangreichere Beschreibungen sind
XML-Referenz [BPSMM00] enthalten. Ein XML-Schema beschreibt Regeln,
ein XML-Dokument und somit eine SOAP -Nachricht erfüllen muss, um gültig
in der
die
(valide) zu sein. Hier werden Datentypen deniert, die in der eigentlichen Nachricht
später verwendet werden können. Dabei werden einfache Typen (simple types) wie
integer und decimal, sowie komplexe Verbundtypen, welche aus diesen einfachen und
komplexen Typen zusammengesetzt werden, unterstützt. Listing 2.3 zeigt eine solche
Schemadatei.
1
2
3
4
< schema targetNamespace = " http: // www . icsy . de / beispiel / " xmlns:own = "
http: // www . icsy . de / beispiel / "
xmlns:wsdl = " http: // schemas . xmlsoap . org / wsdl / "
xmlns = " http: // www . w3 . org /2001/ XMLSchema "
xmlns:xsi = " http: // www . w3 . org /2001/ XMLSchema - instance " >
5
6
< import namespace = " http: // www . icsy . de / types / basic / "
schemaLocation = " http: // www . icsy . de /~ venice / types / basic . xsd " / >
7
8
9
10
11
12
< simpleType name = " databaseName " >
< restriction base = " basic:String " >
< pattern value = " [a - zA - Z_ ]* " / >
</ restriction >
</ simpleType >
13
14
15
16
17
18
19
20
21
< complexType name = " SQLQuery " >
< sequence >
< element name = " dbname " type = " own:databaseName " minOccurs = " 1 "
maxOccurs = " 1" / >
< element name = " queryString " type = " basic:String " minOccurs = " 1 "
maxOccurs = " unbounded " / >
</ sequence >
</ complexType >
< element name = " SQLQuery " type = " own:SQLQuery " / >
</ schema >
Listing 2.3: XML-Schemadatei
12
2. Grundlagen
Namespace , zu welchem die Attribute in diesem Dokument
zugeordnet werden. SOAP -Nachrichten unterstützen alle XML-Standarddatentypen.
Die komplexen Datentypen hingegen unterscheiden sich und werden in separaten Namespaces deniert (siehe Zeile 5). In Zeile 8 wird ein Namespace importiert, in dem
bereits einige Datentypen für Venice deniert wurden. Darauolgend wird ein einZeile 3 beschreibt den
facher Typ basierend auf dem Venice-Datentyp basic:String, der eine Zeichenkette
mit einer maximalen Länge repräsentiert, mit einer zusätzlichen Restriktion angelegt. Dieser Typ wird nun wiederum in einer Sequenz eines komplexen Datentyps
verwendet. Mittels sequence wird bestimmt, dass alle Elemente in dieser Reihenfolge
vorkommen müssen. Die Anzahl kann hierbei mit minOccurs und maxOccurs beeinusst werden. Auf dem gleichen Weg können auch neue
SOAP-Fault -Nachrichten
bestimmt werden.
In
VSC/WSDL-Schnittstellen kann das fertige, valide Schema nun eingebunden werVSC -Compiler erzeugt daraufhin die nötigen Klassen (Stubs/Skeletons).
den. Der
Die Datentypen stehen somit in Java zur Verfügung, wobei komplexe Datentypen
als
Java Beans
[Ham] implementiert werden. Diese speziellen Komponenten besitzen
einen Standardkonstruktor und Setter/Getter-Methoden, um auf deren Variablen
leicht zugreifen zu können und sind grundsätzlich serialisierbar.
nimmt die Übersetzung der
XSD -Datei
XSD/SOAP -Datentypen
JAX-RPC
über-
wie zuvor beschrieben. Die
des Datenbankdienstes wird in Kapitel 3.5.3 vorgestellt. In den folgen-
den Abschnitten wird die Kommunikation mit der eigentlichen Datenbank erläutert.
2.5. JDBC - Java Database Connectivity
Die
JDBC API
[WFC
+ 99] ermöglicht Programmierern die standardisierte Nutzung
der Structured Query Language (SQL) durch Bereitstellung einer Reihe von Javaklassen und -schnittstellen. Dabei kann über einen bestimmten Treiber direkt
auf verschiedenste Datenquellen zugegrien werden. Hier wird PostgreSQL [SYCFa]
als Database Management System (DBMS ) verwendet. PostgreSQL ist eine OpenSource-Datenbank, die einen groÿen Teil des SQL-Standards implementiert. Somit
ist eine leichte Benutzung von SQL-Abfragen mit Unterstützung aller Sprachelemente wie Verbindungsaufbau, Abfragen, Aktualisierung und Ergebnisverarbeitung
möglich. Dies funktioniert auf allen Plattformen, auf welchen eine
chine
Java Virtual Ma-
läuft. Bild 2.5 beschreibt die generelle Zwei-Tier-Architektur (Zwei-Schichten-
Architektur). Dabei kann das DBMS auf einem entfernten Server laufen, während
die Kommunikation über ein proprietäres Protokoll funktioniert. Hierbei stehen verschiedene Treiber (ODBC, Native, etc.) zur Verfügung. Da in diesem Fall keine
heterogene Umgebung vorliegt und nur ein DBMS und somit nur ein Treiber benötigt wird, ist eine genauere Beschreibung nicht notwendig. Diese Thematik wird
in Kapitel 3.1, das die Anforderungen an den Datenbankdienst diskutiert, nochmals
aufgegrien.
2.6. Webservices und Connection-Pooling
13
h
Java-Anwendung
Client
JDBC
DBMS
PostgreSQLServer
Abbildung 2.5.: JDBC - 2-Schichten-Architektur nach [Des03]
2.6. Webservices und Connection-Pooling
Bei der Benutzung von Webdiensten werden in den meisten Fällen nur kurze Anfragen gestellt, die schnell verarbeitet sind. Speziell für einen Datenbankdienst bedeutet
dies, dass bei jedem Aufruf eine Verbindung zur Datenbank mittels JDBC aufgebaut
und nach Abarbeitung auch wieder geschlossen werden müsste, um das Konzept der
Zustandslosigkeit zu erfüllen (siehe Zustandlosigkeit in Kapitel 2.1). Dieser Vorgang ist mit Abstand der langsamste Teil einer SQL-Abfrage. Um dieses Szenario
zu vermeiden wurde das Connection-Pooling eingeführt. Dabei wird beim Start des
Dienstes eine vorgegebene Anzahl von Verbindungsobjekten erstellt. Kommt nun eine Abfrage, so wird das vorhandene Objekt aus dem Pool zur Verfügung gestellt. Es
muss keine neue Verbindung hergestellt werden. Nach Abschluss der Anfrage wird
das Objekt wieder freigegeben und steht wiederum zur Verfügung. Bei der Entwicklung des Datenbankdienstes wurde noch eine weitere Abstraktionsebene hinzugefügt
(siehe Kapitel 3.2).
2.7. Vergleich mit verwandten Technologien
Da die Entwicklung von neuen Konzepten zum Zugri auf entfernte Daten Gegenstand der aktuellen Forschung ist und groÿer Bedarf für neue Ideen und Systeme
seitens der Wirschaft besteht, gibt es viele Arbeiten, die sich mit diesem Thema
beschäftigen. In diesem Abschnitt werden einige Entwicklungen kurz vorgestellt und
von dem hier entwickelten System abgegrenzt.
Die Umwandlung der Inhalte von relationalen Datenbanken zu XML-Dokumenten
und -Schemata wird in der Regel auch von dem jeweiligen DBMS unterstützt. Auch
PostrgreSQL bietet hierfür Funktionen an [SYCFc]. Zum einen müssen diese allerdings explizit in den SQL-Abfragen mit den richtigen Parametern aufgerufen werden, was einer transparenten Nutzung widerspricht, auf der anderen Seite müsste
das Ergebnis mit Hilfe der dokumentbasierten Kommunikation oder einem generischen Datentyp übertragen werden. Diese Technik wird bisher allerdings nicht von
14
2. Grundlagen
Axis und somit auch nicht von Venice unterstützt. Auÿerdem besteht wenig Kontrolle über den Aufbau des Ergebnisdokuments, was die Aufteilung von Nachrichten
bei zu groÿen Dateigröÿen und die Verarbeitung auf Clientseite erschwert. Die hier
entwickelte System bietet durch die genaue Denition der Datentypen eine generisch
direkt verständliche Schnittstelle, während die Ergebnismenge (was die Gröÿe, Anzahl und Länge betrit) genau beobachtet und beeinusst werden kann. Es besteht
lediglich ein Nachteil darin, dass für jeden Datentyp explizit Algorithmen programmiert werden müssen, wobei für Venice nur die Grundtypen relevant sind.
Der JDBC Webservice (JDBC-WS) [DWD06] erlaubt den Zugri über eine URL,
ohne einen speziellen Treiber verwenden zu müssen. Dabei werden ebenfalls Webservices verwendet um Anfragen und Ergebnismengen zu transportieren, die allerdings
nicht gegen ein XML-Schema validiert werden und somit keine starke Typisierung
vorliegt. Wie auch in dem hier entwickelten System, wird aus Sicht des Clienten
vollständig von der zugrundeliegenden Datenbanktechnologie abstrahiert. Allerdings
werden die SOA-Konzepte wie beispielsweise Zustandslosigkeit und Aundbarkeit
nicht beachtet. Dieses Konzept kann als grundlegende Idee des hier entwickelten
Systems gesehen werden, allerdings lassen sich alleine beim Performancevergleich
bereits Nachteile erkennen (siehe Kapitel 5).
Einen weiteren Ansatz bietet das Spitre-Projekt [HM01] innerhalb des
Data Grid
European
Projektes. Dabei handelt es sich speziell um die einfache, performante
Benutzung bei der Beachtung von Interoperabilität, indem eine Middleware zwischen Client und RDBMS zur Verfügung gestellt wird. Der Zugri dieses Dienstes
erfolgt ebenfalls per JDBC, während die Daten als XML-Repräsentation des relationalen Systems übermittelt werden. Dabei werden Tools wie
jwget, wget, curl
aber
auch herkömmliche Browser bei Nutzung von HTTP-Verbindungen unterstützt. Allerdings ergeben sich durch das XML-Format ähnliche Nachteile wie in den beinden
vorherigen Fällen. Das System ist auÿerdem bei weitem nicht so intuitiv zu verwenden wie die hier vorgestellten SOAP-JDBC-Bridge.
Aufbauend auf dem WSRF-Standard bietet OGSA-DAI [AKA
+ 05] verschiedenen
Porttypen für zustandsbehaftete Webservices von verschiedenen Datenquellen. Mit
der Speicherung von Zustandsinformationen handelt es sich hierbei bereits um ein
grundlegend anderes Konzept. Der
Grid Data Service
(GDS) bietet Zugri auf re-
lationale Datenbanken und ist vergleichbar mit dieser Arbeit. Allerdings werden
Anfragen mit speziellen
GDS-Perform documents
gestellt, die verschiedene Abfra-
gesprachen (SQL, XPATH, etc.) unterstützen. Diese Dokumente müssen vom Dienst
geparsed und mit Hilfe des Sicherheitssystems evaluiert werden. Damit geht die Nutzungstransparenz verloren und die Verarbeitung dauert länger. Dieses System wird
im Globus Toolkit [glo] verwendet und ist somit auf eine andere Umgebung eingerichtet, trotzdem weiÿt das System eine ähnliche Performance auf [ogs08]. Letztendlich
wird der generische Datenbankdienst für kurz Anfragen von verschiedenen Anwendungen der Veniceföderation entwickelt, während Sicherheitsaspekte, groÿe Datenmengen, verschiedene Datenquellen und viele weitere Anforderungen bei OGSA-DAI
2.7. Vergleich mit verwandten Technologien
15
betrachtet werden müssen.
Vor allem im Bereich der Gridtechnologien lassen sich weitere Ansätze für den standardmäÿigen Zugri auf relationale Datenbanksysteme nden. In diesem Kapitel
wird lediglich ein Einblick in dieses Gebiet gegeben. Im nächsten Kapitel wird die
Architektur des hier entwickelten Systems genau beschrieben.
16
3. Architektur
Wie bereits in den vorangehenden Kapiteln beschrieben, soll ein generischer Datenbankwebservice für das leichtgewichtige
Servicegrids Venice
entwickelt werden. In
diesem Abschnitt werden die Anforderungen und die sich daraus ergebende Architektur beschrieben. Dabei werden verschiedene Problemlösungen verglichen, bewertet und mit Beispielen aus der Implementierung veranschaulicht. Abschlieÿend wird
das System unter Bezugnahme der Grundkonzepte serviceorientierter Architekturen
evaluiert.
3.1. Anforderungen
Bei der Entwicklung des Dienstes müssen zwei Aspekte genauer betrachtet werden.
Zuerst werden die Anforderungen bezüglich der Funktionalität dargestellt. Anschlieÿend werden weitere Designanforderungen aufgelistet.
Funktionsumfang
•
Möglichst vollständige Abdeckung des SQL-Standards und Unterstützung des
Post-greSQL-DBMS
•
Abdeckung der JDBC API durch Operationen des Webservice
•
Möglichkeiten der Performence-Optimierung von Datenbankabfragen
•
Zugriskontrolle und -begrenzung der einzelnen Datenbanken und Tabellen
mit Hilfe des Venice-SSO-Mechanismus
•
Intuitive Nutzung des Dienstes auf Clientseite durch den Einsatz einer JDBCähnlichen API
Designanforderungen
•
Heterogenen Clienten den Zugri auf die Datenbank ermöglichen
•
Leichte clientseitige Implementierung: Logik auf den Server auslagern
•
Abstraktion von der Datenbank des Servers (keine Verwendung von JDBCTreibern auf Clientseite)
•
Sprachunabhängigkeit
17
18
3. Architektur
•
Eziente Typumwandlung und Datentransfer
•
Nachteile von Webdiensten ausgleichen: Langsamkeit, groÿe Nachrichten, Datentyprestriktionen
In Bild 3.1 wird eine schematische Übersicht des Datenbankdienstes gezeigt, die
den Ablauf einer einfachen SQL-Abfrage verdeutlicht. Hier muss beachtet werden,
dass es sich bei der SOAP-JDBC-Bridge um Klassen handelt, welche die JDBC
API clientseitig überschreiben um einen leichteren Zugri auf den Datenbankdienst
zu ermöglichen, in dem sie als Wrapper für dessen Operationen funktionieren (siehe Kapitel 3.7.1). In den folgenden Kapiteln werden die einzelnen Komponenten
unter Beachtung der oben genannten Anforderungen analysiert und schlieÿlich deren Umsetzung erklärt. Dabei werden die einzelnen Komponenten in umgekehrter
Reihenfolge einzelnd erläutert. Das nächste Kapitel beginnt mit dem serverseitigen
Poolingmechanismus.
Server
Client
Web Service
Operationen
SQL-Abfrage
SELECT * FROM test;
SOAP-JDBC Bridge
SizeCalculator
Connection
Permissions
PreparedStatement
Typemapper
ResultSet
SOAP
DBAccessPool
JAX-RPC
(Umwandlung)
JDBC API
PostgreSQL
Abbildung 3.1.: Datenbank Webservice: Schematische Übersicht
3.2. Serverseitiges Verbindungsmanagement
Im folgenden Codebeispiel kann nachvollzogen werden, welche Schritte für die Verarbeitung einer SQL-Anfrage unter Verwendung der JDBC API notwendig sind.
Allerdings handelt es sich hierbei um die Verwendung eines Treibers ohne den Poolingmechanismus. Da dieser Vorgang bereits von Venice bereitgestellt wird, reicht
es aus eine Verbindung mit dem Aufruf von DBUtils.getConnection(connectionString)
zu erhalten. Das Ziel des Datenbankdienstes ist es, diese Vorgehensweise mit möglichst wenig Nebeneekten und Änderungen der API beizubehalten und eventuell
Optimierungen der Clientabfragen automatisch vorzunehmen.
3.2. Serverseitiges Verbindungsmanagement
1
2
3
4
5
6
7
19
Class . forName ( " org . postgresql . Driver " ) . newInstance () ;
Connection conn = DriverManager . getConnection ( url , " mylogin " , "
mypass " ) ;
// conn = DBUtils . getConnection (" jdbc : apache : commons : dbcp : venice ") ;
conn . setTransactionIsolation ( Connection . TRANSACTION_SERIALIZABLE ) ;
final PreparedStatement pstmt = conn . prepareStatement ( " INSERT INTO
testTable ( id ) VALUES (?) " ) ;
pstmt . setString (1 , " identification " ) ;
pstmt . executeUpdate () ;
Listing 3.1: JDBC API - Beispiel
3.2.1. Problematik
Wie bereits in Kapitel 2.6 beschrieben, werden bei
Webservices
unter normalen Um-
ständen immer sehr kurze Anfragen gestellt. Um einen langsamen Verbindungsaufbau zu vermeiden werden also immer einige Verbindungen in einem Pool aufrechterhalten, die dann für eine Aufgabe ausgeliehen werden können. Daneben gibt es
allerdings noch weitere Aspekte, welche die Erweiterung des Systems empfehlenswert machen:
Nebenläugkeit
Diese Thematik befasst sich mit der Bearbeitung eines Programms
mit mehreren Prozessen beziehungsweise
Threads
gleichzeitig. Wird eine Ope-
ration des Datenbankdienstes aufgerufen, so wird ein neuer
Thread
erzeugt,
der sich um die Abarbeitung der Anfrage kümmert. Werden in dieser Operation nur lokale Variablen geändert, sind keine weiteren Umstände zu beachten,
Thread -Wechsel (Dispatch ) alle Werte mitgesichert werden und
Threads ohne Probleme weitergerechnet werden
kann. Des weiteren ist eine Blockierung eines Objekts für andere Threads mit
Hilfe von synchronized möglich, um beispielsweise Schreib- und Leseanomalien zu vermeiden. Allerdings bremst der Einsatz von Locks ein System aus, da
da bei einem
somit bei der Rückkehr des
sich eine Art Flaschenhals bildet. Während diese Systematik für einige Komponenten wie beispielsweise das Rechtesystem verwendet wird, ist diese für den
Datenbankzugri in mehreren Fällen problematisch. Aus diesem Grund verfügt jeder Thread des Dienstes über ein eigenes Objekt zum Datenbankzugri
um Nebenläugkeitseekte zu umgehen und Zustände besser organisieren und
in Ausnahmefällen über mehrere Aufrufe hinweg speichern zu können.
Zustandslosigkeit
Dieses Grundkonzept der SOA fordert, dass nach einer Anfra-
ge möglichst keine Zustandsinformationen im Server gespeichert werden. Dies
bedeutet ebenfalls, dass die Datenbankverbindung keine anfragespezischen
Werte mehr enthält und an den Pool zurückgegeben wurde. Während das in
den meisten Fällen kein Problem ist, kann es bei gröÿeren Datenmengen von
Vorteil sein, durch Nutzung von
Prepared Statements
oder
Batchupdates
In-
formationen über eine Anfrage hinweg zu halten. Diese Techniken sind an eine
bestehende Verbindung gekoppelt und erfordern, dass das Zugrisobjekt eine
Verbindung über mehrere Aufrufe hinweg hält und somit nicht an den Pool
zurückgegeben wird. Unter anderem lassen sich dadurch ein Performancegewinn und eine Ressourceneinsparung erreichen. Zudem wurde darauf geachtet
20
3. Architektur
diese Objekte nicht dynamisch zur Laufzeit zu erstellen. Stattdessen werden
diese auch in einem Zugrispool organisiert.
Mehrere Datenbanken
Die JDBC API unterstützt nur Verbindungen zu einzelnen
Datenbanken eines DBMS. In einem Pool benden sich also immer eine bestimmte Anzahl (normalerweise acht) an Verbindungen zu einer Datenbank.
Der Datenbankdienst verwaltet hingegen ein komplettes DBMS. Deshalb muss
für jede Datenbank ein eigener Pool zur Verfügung stehen. Des weiteren müssen
Überlegungen die Gültigkeitsdauer eines Pools betreend angestellt werden.
Aus den hier genannten Gründen wurde eine zweite Abstraktionsebene über dem
vorhandenen Poolingmechanismus von Venice eingeführt. Woraus sich folgende neue
Eigenschaften ergeben:
•
Komplexere Logik und bessere Kapselung durch die Verwendung von jeweils
einem Objekt pro Thread und Anfrage
•
Keine Nebenläugkeitseekte, sowie keine Bildung von Flaschenhälsen durch
die Verwendung von
Locks
•
Optimierung des Verbindungsmanagements
•
Vereinfachte Speicherung von Zustandsinformationen und somit der Nutzung
von
Prepared Statement
(Zugrisobjekte über mehrere Anfragen hinweg halten
- siehe Kapitel 3.6.4)
In den folgenden Kapitel wird die Umsetzung dieser Systematik beschrieben.
3.2.2. Architektur des erweiterten Poolings
Im folgenden Bild 3.2 werden die Zusammenhänge zwischen dem neuen System und
dem vorhanden JDBC-Pooling-Mechanismus von
Venice
verdeutlicht.
Venice
bietet
also einen Poolingmechanismus, der für jede abgefragte Datenbank einen Verbindungspool aufrecht erhält. Normalerweise wird in
Venice
nur ein Pool bereitgehal-
ten der beim Systemstart initiert wird. Der generische Datenbankdienst hingegen
erfordert dynamische Verbindungen zu verschiedenen Datenbanken, da er von vielen
Diensten genutzt werden kann und nicht im voraus bestimmbar ist, welche Datenbanken verwendet werden. Zudem können vom Dienst neue Datenbanken angelegt
werden. Die zentrale Klasse AccessPool kontrolliert dabei alle Pools, wobei jede Datenbank durch einen
Connectionstring
identiziert wird. Beim Zugri auf die Da-
tenbank wird die zentrale Verwaltungsklasse nach einem Zugrisobjekt gefragt. Im
Erstfall initiiert diese Klasse daher einen Verbindungspool (Verwendung der Hilfsklasse DBUtils von Venice) und legt gleichzeitig einen neuen Pool für Zugrisobjekte
an, welche die einzelen
Connections
des Verbindungspools verwenden können. Dann
wird ein neues Zugrisobjekt an die Anwendung zurückgegeben. Dabei werden im
Objektpool je nach Bedarf neue Objekte erzeugt bis eine maximale Anzahl erreicht
wurde. Nach erreichen des Maximums wird der nächste Thread für ein bestimmtes Intervall blockiert bis wieder ein Objekt verfügbar ist oder nach einem Timeout
3.2. Serverseitiges Verbindungsmanagement
Erweiterterung
DBAccessObjekt
Venice
Connectionpool
21
JDBCVerbindungen
Datenbank 3
ObjektPool
Datenbank 2
VerwaltungsKlasse
Datenbank 1
AccessPool
Objekt in
Verwendung
PostgreSQL
Verbindungspool
einer
Datenbank
Abbildung 3.2.: Erweiterter Pooling-Mechanismus
ein Fehler ausgelöst wird. Zudem wird jede Benutzung zeitlich gespeichert, so dass
Informationen über die jeweilige letzte Nutzung bekannt sind. So werden Objekte,
die für eine bestimmte Zeit blockiert beziehungsweise verwendet wurden, verworfen
oder durch neue ersetzt. Auÿerdem wird auch ein Pool nach einer bestimmten Zeit
aufgelöst.
3.2.3. Verwendung des Zugriobjektpools
Eine Besonderheit, die aus dem Bild nicht direkt hervorgeht, ist, dass das Zugrisobjekt zwar eine Verbindung eines Pools verwenden kann, diese aber nicht direkt
beim Ausleihen blockiert. Bei einem Abfragevorgang wird die Verbindung von dem
Zugrisobjekt erst angefordert wenn sie wirklich benötigt wird und danach sofort
wieder freigegeben um Ressourcen zu sparen. Dieser Vorgang geschieht ohne Einuss der Anwendung. In Listing 3.2 ist dieser Vorgang zu erkennen. In Zeile 3 wird
ein Zugrisobjekt angefordert. Hier ist bereits zu erkennen, dass es keinen groÿen
Unterschied zur Nutzung von DBUtils gibt. In den folgenden Zeilen werden datenbanktypische Aufgaben verrichtet, wobei die Verbindung erst ab Zeile 4 blockiert
und beim letzten Aufruf von db.next() in Zeile 7 automatisch wieder freigegeben
wird. Letztendlich wird das Zugrisobjekt in Zeile 15 wieder für den Zugrispool
freigegeben und kann erneut verwendet werden. Somit hat jeder Thread sein eigenes Datenbankobjekt zur Verfügung, in dem keine Nebenläugkeitseekte auftreten
und ebenfalls Zustandsinformationen einer Verbindung über einen längeren Zeitraum gehalten werden können. Gleichzeitig wird die Nutzungsdauer einer Ressource
automatisch verkürzt. Im nächsten Abschnitt wird erklärt, wie sich der Datenbankdienst unter Verwendung dieser Techniken initialisiert. Generell wird dieses System
in allen serverseitigen Prozessen verwendet.
22
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
3. Architektur
DBAccess db = null ;
try {
db = AccessPool . getDBObject ( " testDatenbank " ) ;
db . loadPreparedStatement ( " SELECT * FROM testTable WHERE
parameter1 = ? " ) ;
db . set (1 , " test " ) ;
db . queryPreparedStatement () ;
while ( db . next () ) {
System . out . println ( db . get (1) ) ;
}
}
catch ( SQLException e ) {
e . printStackTrace () ;
}
finally {
AccessPool . releaseDBObject ( db ) ;
}
Listing 3.2: DBAccess-Objekt - Beispiel
3.2.4. Datenbankoptimierung
Nachfolgend werden verschiedene Möglichkeiten aufgelistet Datenbankabfragen zu
optimieren. Diese Aspekte müssen bereits beim Design des Dienstes beachtet werden
und spielen auch bei der Betrachtung der Datentypumwandlungen (siehe Kapitel
3.5.2) eine groÿe Rolle.
Datenbank-Pooling
Diese Technik und deren Vorteile wurden bereits in den vor-
angehenden Abschnitten verdeutlicht.
Transaktionskontrolle
Eine Transaktion besteht aus einer oder mehreren Anfragen
die als eine Einheit ausgeführt werden. JDBC-Verbindungen sind standardmäÿig so eingestellt, dass automatisch nach jedem einzelnen Statement ein commit
durchgeführt wird. Um diesen Mehraufwand zu verhindern, kann
AutoCommit
deaktiviert werden und die Transaktion manuell mit Hilfe von commit() und
rollback() behandelt werden um unnötige Abfragen zu vermeiden.
Isolationslevel
Das Isolationslevel bestimmt, wie das DBMS die Datenintegrität un-
ter Beachtung von Fehlern, wie
reads
dirty-reads , phantom-reads
oder
non-repeatable
bewahrt. Diese können bei konkurrierenden Transaktionen auftreten. Das
DMBS verwendet
locks
um diese Probleme zu verhindern. Wenn allerdings von
vorne herein bekannt ist welche Fehler auftreten können oder das Auftreten für
die spezische Applikation nicht kritisch ist, so kann das Level heruntergesetzt
werden, wodurch ein erheblicher Performancegewinn entsteht.
Statements
Beim Datenbankdienst werden ausschlieÿlich
Prepared Statements
ver-
wendet. Der Vorteil ist hier, dass die Anfrage vorkompiliert wird und wiederverwendet werden kann. Wie diese Funktionalität realisiert ist, wird in Kapitel
3.6 genauer beschrieben. Ein weiterer Schritt ist der Einsatz von
um mehrere Anfragen gebündelt zur Datenbank zu verschicken.
Batchupdates
3.3. Initialisierung und Verwaltung des Dienstes
Datentypumwandlung
23
Die JDBC API berrscht eine sehr exible Typzuordnung
(siehe Kaptil 3.5.4). Allerdings ist die Wahl der geeigneten Zugrismethode
für die verschiedenen Datentypen sehr wichtig, um unnötige Umwandlungen
(Casts ) zu vermeiden. Die Zuordnungen sind in Tabelle 3.5.2 im genannten
Kapitel ersichtlich und werden dort nochmals detaillierter beschrieben.
Verbindungsfreigabe
Die Verbindung möglichst schnell schlieÿen beziehungsweise
an den Pool zurückgeben.
Die hier vorgestellten Techniken können in [WFC
+ 99] nachgelesen werden. Im fol-
genden Verlauf werden diese Punkte als Grundlage vorausgesetzt und nicht mehr
explizit angesprochen.
3.3. Initialisierung und Verwaltung des Dienstes
Die leichte Installation eines Dienstes gehört zu einem der Grundkonzepte eines
leichtgewichtigen Servicegrids. Um dieses Ziel zu erreichen, sollte ein Dienst bei der
ersten Ausführung oder Portierung in eine neue Domäne die Arbeitsumgebung autonom vorbereiten und sich somit selbst installieren. Der Datenbankdienst benötigt
hierfür lediglich das PostgreSQL-DBMS, dessen Host, Port, Benutzer und Passwort.
Diese Informationen werden über den zentralen Kongurationmechanimus von Venice angepasst. Daraufhin wird automatisch die benötigte Verwaltungsdatenbank
angelegt. Während des Betriebs steht für diese Verbindung beständig ein
sPool
Acces-
zur Verfügung, um beispielsweise Rechteabfragen durchführen zu können. Des
weiteren wird ein weiterer
Thread
erzeugt, der die Einhaltung von Intervallgrenzen
überwacht und Zustandsinformationen nach deren Ablauf löscht, um Ressourcen zu
sparen. Alle weiteren Abläufe werden erst beim Aufruf einer Operation von Venice
und Axis initiiert.
3.4. Rechtemanagement
Wird über den Datenbankdienst eine neue Datenbank angelegt, so ist diese über
Venice-SSO-Informationen (Domäne und Benutzername) und einen selbst vergebenen Namen eindeutig identiziert. Allerdings sollten die Datenbank oder einzelne
Tabellen nicht nur diesem einen Benutzer zugänglich sein. Aus diesem Grund ist ein
einfaches Rechtesystem notwendig, welches dem Clienten die Zuweisung von Lese/Schreibrechten auf die eigene Datenbank erlauben. Die Granularität beschränkt sich
dabei auf Datenbanken und Tabellen, während zwischen keinen, Lese- und Schreibrechten unterschieden wird. Auf die Verwendung des komplexen Rechtesystems
von PostgreSQL, beziehungsweise des SQL-Standards wurde aus mehreren Gründen
verzichtet. Es kann nicht davon ausgegangen werden, dass die Datenbank alleine
für diesen Dienst verwendet und somit zusätzlich für andere Funktionen manuell
konguriert wird. Dadurch können unvorhersehbare Zustände entstehen. Auÿerdem
ist nicht garantiert, dass der Datenbankdienst-DBMS-Benutzer über ausreichende
Rechte verfügt, um Datenbankbenutzer anzulegen und diesem weitere Rechte zu
24
3. Architektur
vererben. Zudem würde sich bei der Implementierung des Dienstes das Verbindungsmanagement erheblich verkomplizieren, da beim Verbindungsaufbau zusätzlich auf
verschiedene Benutzerdaten geachtet werden müsste. Aus diesen Gründen benutzt
der Dienst eine eigene Kontrolltabelle, um Rechteinformationen zu speichern und
verwaltet Zugrie mit Hilfe eines eigenen Systems. Dabei wird nicht zwischen verschiedene Benutzergruppen unterschieden. Die gegebenen Rechte betreen immer
einzelne Benutzer der kompletten Venice-Föderation. In den folgenden Abschnitten
werden verschiedenen Zugrisstufen ähnlich einer Mandatory Access Control (MAC)
speziziert und deren Überprüfungskonzepte analysiert.
3.4.1. Kategorisierung von Anfragen und Benutzern
Aus den zuvor geschilderten Anforderungen ergeben sich insgesamt vier verschiedene
Zugrisgruppen. Bei den ersten beiden handelt es sich um die Lese- oder Schreibberechtigungen von allen anderen Benutzern auÿer dem Besitzer (dritte Gruppe) selbst.
Auÿerdem gibt es Elemente des SQL-Standards, die von niemandem auÿer dem System (vierte Gruppe) ausgeführt werden dürfen. In welchem Bezug diese Gruppen
zueinander stehen, ist in Bild 3.3 veranschaulicht. Also hat der äuÿerste Ring am
Gruppe 4: System
Gruppe 3: Besitzer
Gruppe 2: Schreiboperationen
Gruppe 1: Leseoperationen
CREATE database
ALTER database
DROP database
CREATE table
DROP table
Datentyprestriktionen
INSERT
UPDATE
DELETE
SELECT-Statements
Gruppe 0: Keine Rechte
Datenbank
Rechteverwaltung
Abbildung 3.3.: Zusammenhang der Rechtegruppen
meisten Rechte, während es immer mehr Einschränkungen nach innen gibt. Dieses
Konzept weist Ähnlichkeiten zu einer
Mandatory Access Control
[Erl05] auf. Ein
Nutzer legt über den Datenbankdienst selbst eine neue Datenbank an und ist damit
bereits automatisch Besitzer dieser und bendet sich somit in Gruppe 3. Die Datenbank selbst kann nicht über SQL-Operationen angelegt oder geändert werden. Der
Nutzer kann jedoch alle Datenmanipulationselemente (select ,
und einige Datendenitionselemente (create/alter/drop
insert , update , delete )
table ) um Tabellen anzule-
gen, zu ändern und zu löschen, verwenden. Des weiteren wird nur eine Untermenge
der von PostgreSQL unterstützten Datentypen zugelassen. Diese Restriktion wird
aus Kapitel 3.5.2 ersichtlich. Nachdem alle Denitionen vorgenommen wurden, kann
der Nutzer die Rechte auf die eigenen Datenbankelemente verteilen, indem er Lese-
3.4. Rechtemanagement
25
oder Schreibrechte gewährt. Diese Einträge werden in entsprechenden Verwaltungstabellen gespeichert. Während dieses Schema die Organisation verdeutlicht, wird
im nächsten Abschnitt beschrieben, wie SQL-Abfragen diesen Gruppen zugeordnet
werden.
3.4.2. Prüfungsvorgang
Bei jedem Aufruf einer Datenbankdienstoperation sind verschiedene Informationen
über den Nutzer vorhanden. Der Benutzername, die Domäne, der Datenbankname
sowie die SQL-Abfrage selbst. Nachfolgend wird eine Rangfolge der durchzuführenden Prüfungen aufgelistet, wobei ein negatives Prüfungsergebnis immer zu einer
Ablehnung der Abfrage führt:
1. Es handelt sich um keine Abfrage der vierten Gruppe.
2. Über die ersten drei Elemente lässt sich bereits feststellen, ob es sich um den
Besitzer der Datenbank handelt.
•
Besitzer: Falls die Abfrage keine Tabelle mit unerlaubten Typen anlegt,
wird alles zugelassen und die Überprüfung ist beendet.
•
Fremdzugri: Es existieren Einträge für die betroene Datenbank im
Rechteverwaltungssystem.
3. Alle betroenen Tabellen haben je nach Abfragetyp die notwendigen Rechte.
Wurden alle Schritte erfolgreich durchlaufen, kann die SQL-Abfrage weiterverarbeitet werden. Um diese Prüfungen durchzuführen muss die Abfrage auf bestimmte
Schlüsselwörter und Tabellen untersucht werden.
3.4.3. Überprüfungskonzepte
Der Einsatz eines Parsers
ist eine Möglichkeit, SQL-Abfragen zu analysieren
und auf erlaubte Schlüsselwörter zu überprüfen. In Listing 3.3 ist die Syntax eines
CREATE-TABLE-Statements
[SYCFb] erkennbar. Bei der Implementierung des
Datenbankdienstes wurde Version 8.3 des PostgreSQL-DBMS verwendet. Bereits
hieran ist erkennbar, dass die Entwicklung eines neuen Parsers sehr aufwändig ist.
SELECT , INSERT , UPDATE , DELETE , CREATE/ALTER/DROP database/table und GRANT/REVOKE ebenfalls
Dabei müssen alle anderen Statements wie
einbezogen werden. Diese Schemata können unter [SYCFa] eingesehen werden.
CREATE [ [ GLOBAL | LOCAL
( [
{ column_name data_type
column_constraint [
| table_constraint
| LIKE parent_table [
[ , ... ]
] )
] { TEMPORARY | TEMP } ] TABLE table_name
[ DEFAULT default_expr ] [
... ] ]
{ INCLUDING | EXCLUDING } DEFAULTS ] }
26
3. Architektur
[ INHERITS ( parent_table [ , ... ] ) ]
[ WITH OIDS | WITHOUT OIDS ]
[ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
[ TABLESPACE tablespace ]
where column_constraint is :
[ CONSTRAINT constraint_name ]
{ NOT NULL |
NULL |
UNIQUE [ USING INDEX TABLESPACE tablespace ] |
PRIMARY KEY [ USING INDEX TABLESPACE tablespace ] |
CHECK ( expression ) |
REFERENCES reftable [ ( refcolumn ) ] [ MATCH FULL | MATCH
PARTIAL | MATCH SIMPLE ]
[ ON DELETE action ] [ ON UPDATE action ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY
IMMEDIATE ]
and table_constraint is :
[ CONSTRAINT constraint_name ]
{ UNIQUE ( column_name [ , ... ] ) [ USING INDEX TABLESPACE
tablespace ] |
PRIMARY KEY ( column_name [ , ... ] ) [ USING INDEX TABLESPACE
tablespace ] |
CHECK ( expression ) |
FOREIGN KEY ( column_name [ , ... ] ) REFERENCES reftable [ (
refcolumn [ , ... ] ) ]
[ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE
action ] [ ON UPDATE action ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY
IMMEDIATE ]
Listing 3.3: SQL-Syntax: CREATE TABLE
Die Erstellung einer neuen Parserenginge ist unter diesen Umständen viel zu aufwändig. Existierende Lösungen bieten jedoch nicht die nötige Abdeckung des SQLStandards. Auÿerdem ist die Lösung mit Hilfe eines Parsers nicht sehr performant,
vor allem wenn eigentlich das Aunden von einzelnen Schlüsselwörtern vollkommen ausreicht. Wenn eine Abfrage des Datenbankbesitzers gestellt wird, reicht beispielsweise die Überprüfung auf das Vorkommen des
Strings
create table/database.
Ist das Ergebnis negativ kann die Abfrage durchgeführt werden. Ein Parser würde
den kompletten String zerlegen, auf eine komplette, valide Syntax prüfen und so
unnötigen Overhead produzieren. Die richtige SQL-Syntax ist im Zugriskonzept
jedoch vollkommen unerheblich, da JDBC, beziehungswiese PostgreSQL, diese Fehler erkennt und entsprechende Fehlermeldungen selbst auslöst und an den Clienten
weiterreicht.
Der Einsatz regulärer Ausdrücke
bietet in diesem Fall eine hinreichende und per-
formantere Lösung für die Prüfung der SQL-Abfragen. Dabei wird jeder Schritt des
Prüfvorgangs (siehe Kapitel 3.4.2) anhand eines oder mehrerer regulärer Ausdrücke
analysiert. Der Ausdruck in Listing 3.4 erkennt einen nicht erlaubten Typ beim Erstellen einer Tabelle. Im Anhang B bendet sich eine Auistung aller verwendeten
Ausdrücke nach dem in Kapitel 3.4.2 vorgestellten Schema. Im Fall der Erweiterung
des DBMS muss die Syntax dieser Ausdrücke natürlich angepasst werden. Anhang
3.4. Rechtemanagement
27
.*(?: create | alter ) {1}[\ sa - z {}]+ table {1}[\ w \ s ]+\((?:\ s *\ w +\ s +(?:
bigint | bigserial | bit (?:\ s *\([0 -9]+\) ) ?| bit \ s + varying (?:\ s
*\([0 -9]+\) ) ?| boolean | bytea | character \ s + varying (?:\ s *\([0 -9]+\)
) ?| character (?:\ s *\([0 -9]+\) ) ?| date | double \ sprecision | integer |
numeric (?:\ s *\(\ s *[0 -9]+ ,\ s *[0 -9]+\ s *\) \ s *) ?| real | smallint |
serial | text | time (?:\ s *\([0 -9]+\) ) ?| timestamp (?:\ s *\([0 -9]+\) ) ?)
{1}[\ w \ s " " ' ]* ,?) +\ s *\) .*
Listing 3.4: Regulärer Ausdruck -
CREATE TABLE
mit Typrestriktionen
B.1 zeigt zwei Syntaxbeispiele für die das System nicht ausgelegt ist. Im schlimmsten Fall funktioniert das System für den Besitzer einer Datenbank auch ohne die
Prüfungsmechanismen. Im nächsten Kapitel wird das Rechteverwaltungssystem kurz
vorgestellt.
3.4.4. Verwaltung
Nachdem die Abfrage mit regulären Ausdrücken geltert wurde, ist ersichtlich, in
welche Kategorie die Abfrage einzuordnen ist. Handelt es sich um den Besitzer der
Datenbank, sind keine weiteren Schritte erforderlich. Versucht ein fremder Nutzer
auf eine Datenbank zuzugreifen, ist bekannt, auf welche Tabellen Schreib- oder Leserechte angefordert werden. Diese Informationen müssen zuvor von dem Besitzer
in der Rechteverwaltung angegeben und in zwei simplen Tabelle (siehe Listing 3.5)
gespeichert werden.
create table databases (
id serial ,
name text not null ,
ownerdomain text not null ,
ownername bytea not null ,
primary key ( id )
);
create table permissions (
database integer not null ,
tablename text not null ,
domain text not null ,
username bytea not null ,
action varchar (10) not null default ' read ' CHECK ( action =
' read ' OR action = ' write ') ,
primary key ( database , tablename , domain , username ) ,
foreign key ( database ) references databases ( id ) on delete
cascade on update cascade
);
Listing 3.5: Tabelle - Rechteverwaltung
Der Datenbankname besteht dabei jeweils aus der Domäne, dem Benutzernamen
und dem eigentlich vergebenen Namen. Hierauf muss bei der Benutzung des Systems geachtet werden. Aus der Kombination icsy.de, mustermann und testdb
ergibt sich beispielsweise icsy_de_467196874_testdb. Die Modikation der Rechtetabellen wird über spezielle Operationen des Datenbankdienstes angeboten und
28
3. Architektur
ist somit nicht über SQL-Abfragen möglich. So kann der Tabellenbesitzer spezische Rechte für jeden Benutzer der Föderation auf jede der Tabellen verteilen. Im
nächsten Abschnitt wird vorgestellt, wie Abfragen und Ergebnise zwischen Client
und Server transportiert werden.
3.5. Datentransferkonzepte
Nachdem eine SQL-Abfrage durch das Rechtesystem geprüft wurde und mit Hilfe
des Zugrispools, des Verbindungspools und der JDBC API verarbeitet wurde, muss
die Ergebnismenge per SOAP über das Medium zum Clienten übertragen werden.
Hierbei ergeben sich verschiedene Problembereiche:
Architekturen
Die Zusammensetzung der Ergebnismenge kann bezüglich der unter-
stützten Datentypen sehr unterschiedlich ausfallen und ist nicht vorhersehbar.
Es wird ein Lösungskonzept benötigt, um die sehr variable Ergebnismenge zu
übertragen oder abfragen zu können.
Datentypumwandlung
PostgreSQL, Java und SOAP/XML benutzen verschiedene
Datentypen. Hierbei kümmert sich die JAX-RPC um die Umwandlung von
Java nach SOAP. Eine Zuordnung von PostgreSQL nach Java ist jedoch zusätzlich notwendig. Auÿerdem muss ein neuer Datentyp mit Hilfe von XMLSchemas erstellt werden.
Nachrichtengröÿe
Bei einer groÿen Ergebnismenge kann die SOAP-Nachricht für
einen Sendevorgang zu groÿ werden. Die Gröÿe muss also im Voraus bestimmt
werden und in mehrere SOAP-Nachrichten aufgeteilt werden.
Sicherheit
Bei groÿen Ergebnismengen kann der Server an seine Belastungsgren-
zen stoÿen, weswegen ein Schutzmechanismus (Begrenzung) entworfen werden
muss. Des weiteren ergeben sich durch die Denition des Datentyps weitere
Angrimöglichkeiten, die diskutiert werden müssen.
In den nachfolgenden Abschnitten werden für jeden dieser Bereiche verschiedene
Lösungsmöglichkeiten besprochen und bewertet. Dabei liegt der Schwerpunkt auf
der hier vorgenommenen Implementierung.
3.5.1. Architekturen
Wie bereits in Kapitel 2.4.2 besprochen, besteht jede SOAP-Nachricht aus XML und
wird üblicherweise über HTTP versandt. Werden bei einer Methode nur Datentypen als Rückgabewerte oder Parameter verwendet, die von JAX-RPC standardmäÿig
unterstützt werden, geschieht eine Umwandlung von Java nach SOAP/XML automatisch. Hier wird allerdings ein eigener Typ für die Ergebnismenge deniert. Dies
geschieht über eine XML-Schemadatei. Nähere Informationen hierzu nden sich ab
Kapitel 2.4.4. Zur Gestaltung dieses Schemas bieten sich mehrere Lösungswege an,
wobei die von
Venice
verwendete Kommunikationsart einbezogen werden muss.
3.5. Datentransferkonzepte
29
In SOAP bestehen zwei grundsätzlich verschiedene Arten der Kommunikation: die
dokumentbasierte und die dialogbasierte Übermittlung. Im ersten Fall werden Standard-XML-Schemata für die Serialisierung und Instanzierung von komplexen Datentypen verwendet und im Anhang der SOAP-Nachricht mitversendet. Dieses Konzept
ist für die Darstellung einer Ergebnismenge gut geeignet. Allerdings wird diese Art
der Kommunikation momentan noch nicht von der
von
Venice
AXIS-Engine
und somit nicht
unterstützt. Eziente Lösungen in diesem Bereich müssen daher leider
von vornherein ausgeschlossen werden. Im zweiten Fall werden synchrone
Procedure Calls
Remote
zur Kommunikation verwendet. Es werden einzelne Operationen
deniert, die standardmäÿige oder eigene Datentypen als Parameter verlangen und
zurückgeben können. Unter dieser Voraussetzung ergeben sich mehrere mögliche Architekturstile zum Aufbau der Ergebnismenge.
Schwache Typisierung
Der XML-Typ xsd:any erlaubt es, einen beliebigen Da-
tentyp zu übermitteln. Im Voraus muss also nicht bekannt sein, aus welchen
Datentypen eine Ergebnismenge besteht. Dieses Konzept nennt man auch
schwache Typisierung. Grundsätzlich können generische Typen (string ,
base64kodiert , xsd:any , xsd:anyType , attachment) für die Übertragung genutzt werden. Allerdings entspricht dieses Design nicht dem grundlegenden Gedanken
von Webservices. Ein WSDL-Dokument soll möglichst genau beschreiben, welche Operationen, Parameter und Datentypen verwendet werden. Die Denition
alleine sollte Aufschluss über die Funktionsweise und deren Benutzung geben.
Des weiteren kann die Verwendung dieses Elements zu Interoperabilitäts- und
Sicherheitsproblemen führen. Der Applikation kann ein beliebiges Konstrukt
übermittelt werden und Nutzer/Anbieter müssen steuer wie diese Daten interpretiert werden. Aus diesen Bedenken heraus wird dieses Konzept nicht
verwendet.
Benutzung der PostgreSQL-Engine
Hierdurch können aus Abfragen heraus di-
rekt XML produziert werden. Die Übertragung müsste allerdings wiederum
mittels schwacher Typisierung implementiert werden.
Server-Ergebnismenge
Bei diesem Design wird die Ergebnismenge serverseitig zur
Verfügung gestellt. Als Rückgabewert wird dem Nutzer lediglich eine Identikation und einige Informationen zur Gröÿe, Anzahl und Zusammensetzung
der Ergebnisse übermittelt. Um einzelne Zeilen und deren Spalten datentypabhängig abzufragen, würde der Datenbankdienst einfach als Wrapper um eine
JDBC-ResultSet
implementiert. Das heiÿt, jeder Wert kann einzeln, beispiels-
weise mit getString(id, spalte), abgefragt werden. Während diese Architektur
sehr einfach und schnell zu implementieren wäre und ohne die Denition neuer Datentypen auskommt, da lediglich Standarddatentypen verwendet werden
können, ergeben sich durch die
RPC -basierte Kommunikation Probleme. Beim
Design von verteilten Anwendungen ist die Minimierung aller entfernten Aufrufe essenziell, da diese einen Engpass darstellen. Die
RPCs
sind normalerweise
die langsamsten Operationen des Systems und auÿerdem für eine Vielzahl von
Fehlern anfällig. Bei dem hier vorgestellten Konzept wäre ein Aufruf für jedes
einzelne Ergebnis notwendig und damit sehr inezient und langsam. Diese
Lösung kommt hier nicht in Frage.
30
3. Architektur
Starke Typisierung
Bei dieser Lösung werden die zu übertragenden Datentypen
analysiert. Für jeden Typ der Datenbank muss ein Java- und SOAP/XMLÄquivalent gefunden werden. Diese Menge von Datentypen muss dann in einem
neuen Datentyp so deniert werden, dass eine dynamische Zusammenstellung
zur Laufzeit möglich ist. Auf diesem Weg kann jede beliebige Ergebnismengenkombination erreicht werden. Dieses Konzept nennt man auch starke Typisierung, weil das Format jeder Nachricht genau deniert ist. Daraus ergibt
sich eine gute Kontrolle der Elemente was die Anzahl, Länge und Gröÿe jedes
Typs betrit. Sicherheitsaspekte können so sehr gut beachtet werden. Durch
die genaue Denition wird ebenfalls eine verbesserte Interoperabilität erreicht.
Zudem können die Datentypen durch den Einsatz von Java-Beans leicht in der
Programmierumgebung verwendet werden.
Im nächsten Kapitel wird analysiert, welche Datentypen von den verschiedenen Systemen benötigt werden und in welchem Zusammenhang diese zueinander stehen.
3.5.2. Datentypumwandlung
Da in PostgreSQL, Java und SOAP unterschiedliche Datentypen verwendet werden, wird ein einheitliches Umwandlungssystem (Mapping ) benötigt. Bei Übertra-
gung einer Ergebnismenge (ResultSet ) werden die einzelnen Spalten direkt in SOAPDatentypen umgewandelt, über das Medium verschickt und auf Clientseite in entsprechende Javadatentypen umgewandelt. In Tabelle 3.1 werden die hier unterstütz-
ten PostgreSQL-Datentypen und deren zugeordnete Typen aufgelistet. Es muss beachtet werden, dass PostgreSQL zwar den SQL-Standard gröÿtenteils umsetzt, sich
die Typenbezeichnungen jedoch unterscheiden. Auf Bezüge zum Standard wurde aufgrund der Übersichtlichkeit nicht eingegangen. Die Werte in der 2. Spalte entsprechen
den von der JDBC API vergebenen Bezeichnungen und dienen dem besseren Verständnis der darauf folgenden Codebeispiele und Tabellen. Die JDBC-Typen können
zur Laufzeit dynamisch mit der Methode resultSet.getMetaData().getColumnTypeName(colIndex) ermittelt werden. Bei dem
denierte
Venice -Datentypen,
Namespace
basic handelt es sich um bereits
während xsd JAX-Standardtypen entsprechen. Nur
die gebräuchlichsten Elemente des DBMS wurden implementiert. Da zum Zeitpunkt
der Implementierung Probleme mit Bytedatentypen bei der Bearbeitung von
red Statements
Prepa-
bestanden, wurde diese Funktionalität nicht weiterentwickelt. Diese
Mappingtabelle ist die Basis für den hier entwickelten neuen Datentyp.
3.5.3. Neue Datentypen mit Hilfe von XML-Schemadateien
In Listing 3.6 ist die Denition des Datentyps SQLValueSet erkennbar (Zeile 27).
Eine Zeile (SQLRow - Zeile 1) kann dabei alle Datentypenkombinationen aufnehmen.
Es können Datentypen ausgelassen werden (nillable=true ) oder durch Benutzung
von Arrays (basic:IntArray()) mehrfach verwendet werden.
bige
ResultSet
Während so jede belie-
unter Verwendung der unterstützten Datentypen konstruiert werden
kann, wird noch nicht ersichtlich in welcher Reihenfolge die Inhalte ausgegeben werden sollen, beziehungsweise welcher Wert zu welcher Spalte der Datenbanktabelle
3.5. Datentransferkonzepte
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
< complexType name = " SQLRow " >
< sequence >
< element name = " int8 " type = " basic:LongArray " nillable = " true "
minOccurs = " 0" / >
...
</ sequence >
</ complexType >
< element name = " SQLRow " type = " tns:SQLRow " / >
< complexType name = " SQLRows " >
< sequence >
< element name = " SQLRow " type = " tns:SQLRow " maxOccurs = " unbounded "/
>
</ sequence >
</ complexType >
< element name = " SQLRows " type = " tns:SQLRows "/
< simpleType name = " colType " >
< restriction base = " basic:String " >
< enumeration value = " int8 " / >
...
</ restriction >
</ simpleType >
< complexType name = " SQLValueSet " >
< sequence >
< element name = " colTypes " type = " tns:colType " maxOccurs = "
unbounded " / >
< element name = " SQLRows " type = " tns:SQLRows " nillable = " true " / >
< element name = " MessageAmount " type = " int " / >
< element name = " MessageNumber " type = " int " / >
< element name = " resultSetId " type = " long " nillable = " true " / >
< element name = " persistentId " type = " long " nillable = " true " / >
</ sequence >
</ complexType >
< element name = " SQLValueSet " type = " tns:SQLValueSet " / >
Listing 3.6: SOAP-Datentyp für die Ergebnismengen
32
3. Architektur
PostgreSQL
bigint
bigserial
JDBC-Types
SOAP
JAVA
int8
xsd:long
long
int4
xsd:int
int
basic:ByteArray
byte[]
xsd:boolean
boolean
integer
smallint
serial
bit varying
varbit
bit
bit
bytea
bytea
boolean
bool
character
bpchar
character varying
varchar
text
text
time
time
timetz
timetz
timestamp
timestamp
timestamptz
timestamptz
date
date
String
basic:String
Time
Timestamp
xsd:dateTime
Calendar
double precision
oat8
xsd:double
double
real
oat4
xsd:oat
oat
numeric
numeric
xsd:decimal
BigDecimal
Tabelle 3.1.: PostgreSQL-XML-Mappings
gehört. Hierzu wird ein weiteres Array für die Stringbezeichnungen der Datentypen
angelegt. Mit diesen Daten alleine, kann jeder Nutzer die Ergebnismenge clientseitig
rekonstruieren. Ein Beispiel dieses Vorgangs ndet sich unter 3.7.
1
2
3
4
5
6
7
8
SQLRow row [] = { new SQLRow () };
row [0]. set_int ( new IntArray () ) ;
row [0]. get_int () . setContents ( new int [ getTypeAmount ( types ,
DBTYPE_INT8 ) ]) ;
row [0]. get_int () . setContents ( intCount , result . getInt ( col ) ) ;
SQLRows rows = new SQLRows () ;
rows . setSQLRows ( row ) ;
rows . setColTypes ( new StringArray () ) ;
rows . getColTypes . setContents ( getTypesFromResultSetMetaData () ) ;
Listing 3.7: SQLValueSet Anwendungsbeispiel
Hierbei wird lediglich ein einzelner Integerwert übermittelt. Bereits so ist ersichtlich,
dass die Verwendung sehr umständlich ist und die Komplexität durch die Notwendigkeit der dynamischen Erstellung noch zunimmt. In Anhang C.2 nden sich Codeauschnitte bezüglich der Datentypumwandlung auf Server- und Clientseite. Da die
Erlernung der Syntax und das Verständnis der Zusammensetzung der multidimensionalen Arrays sehr verwirrend ist werden clientseitig Hilfsklassen implementiert
(näheres hierzu unter Kapitel 3.7.1).
Während es hierbei lediglich um den Transfer einer Erebnismenge geht, kann ei-
Prepared Statements
Platzhaltern (? ) erstellt. Diese
ne Übermittlung vom Client aus vorkommen, falls
verwendet
werden. Es wird eine SQL-Abfrage mit
wird dann
3.5. Datentransferkonzepte
33
vorkompiliert und kann mehrmals mit wechselnden Inhalten verwendet werden. Ein
weiterer Datentyp wird deniert um ebenfalls von Clientseite aus beliebige Datenkombinationen übertragen zu können (siehe Listing 3.8). Diese kann nun ähnlich wie
die Ergebnismenge verwendet werden.
< complexType name = " PreparedValueRow " >
< sequence >
< element name = " int " type = " basic:IntArray " nillable = " true "
minOccurs = " 0" / >
< element name = " Byte " type = " basic:ByteArray " nillable = " true "
minOccurs = " 0" maxOccurs = " unbounded " / >
...
</ sequence >
</ complexType >
Listing 3.8: Datentypen Clientseitige Übermittlung
Im nächsten Abschnitt wird die Datentypunterstützung dieser verschiedenen Systeme mit Hilfe einer Tabelle verglichen und bewertet.
3.5.4. Die JDBC API und Typenzwang
Standardmäÿig bietet die JDBC API eigene Methoden an um PostgreSQL (beziehungsweise die Datentypen des SQL-Standards) in bestimmte Javadatentypen umzuwandeln. Dabei besteht generell ein sehr exibler Typenzwang. In Tabelle 3.2
wird die Unterstützung in Bezug auf die hier verwendeten Datentypen (JDBC-
TIME
TIMESTAMP
x
x
x
x
x
x
x
x
x
x
x
TEXT
x
x
VARCHAR
x
x
CHAR
x
x
BIT
x
x
NUMERIC
x
x
FLOAT8
x
x
FLOAT4
x
x
INT8
getBoolean
getString
getInt
INT4
DATE
Typenbezeichnungen) veranschaulicht. Kleine x bedeuten hierbei, dass die Um-
x
x
x
x
x
x
x
x
x
getLong
x
x
x
x
x
x
x
x
x
getFloat
x
x
x
x
x
x
x
x
x
getDouble
x
x
x
x
x
x
x
x
x
getBigDecimal
x
x
x
x
x
x
x
x
x
getDate
getTime
x
x
x
getTimestamp
x
x
x
Tabelle 3.2.: JDBC-Mapping nach [WFC
x
x
x
x
x
x
+ 99]
wandlung generell von der JDBC API zugelassen wird, wobei die farblich markierten Werte die optimalste Lösung für das DBMS darstellen, da keine aufwändigeren
34
3. Architektur
filesize =
basis + ( typesPerRow * basisTypeValue ) + ( basisPerRow + ( typesPerRow *
typeValue ) ) * rowAmount
= 1600 + ( x *75) + (1500 + ( x *300) ) * y
Listing 3.9: SOAP-Nachrichten: Formel zur Dateigröÿenberechnung
Casts
durchgeführt werden müssen. Aufgrund dessen wird bei der Implementie-
rung der Typumwandlung nach Möglichkeit diese Methode gewählt. Weiterhin verdeutlichen die dunkel hinterlegten Felder die Umwandlung von PostgreSQL nach
SOAP/XML. Zum Zeitpunkt der Implementierung hatte das DBMS Probleme mit
den Byte-Datentypen, wodurch diese hier nicht einbezogen werden konnten. Natürlich bremst die Verwendung von XML zur Übertragung die Kommunikation und
Verarbeitung erheblich. Alle anderen Engpässe wurden bei der Implementierung
dennoch beachtet.
3.5.5. Nachrichtengröÿe
Ein groÿes Problem von XML-Daten ist, dass sie sehr viele Verwaltungsinformationen enthalten und somit sehr groÿ werden können, was wiederum die Verarbeitungsgeschwindigkeit beeinusst. Während
Axis
nur eine bestimmte Nachrichtengröÿe zu-
lässt, muss diese Problematik auch aus Gründen der Performance, Latenzzeiten und
Ressourcenverfügbarkeit beachtet werden. Auÿerdem ist es von Vorteil die Nachrichtengröÿe bereits im System anpassen zu können, um ein Optimum zu erreichen.
Bei dieser Implementierung wird aufgrund von Messungen die Gröÿe jeder Ergebnismenge vorausberechnet und nur bis zu einer bestimmten Gröÿe zugelassen. In
Tabelle 3.3 nden sich Stichproben die zur Herleitung der Faktoren beigetragen haben. Die Genauigkeit dieser Berechnung ist unbedeutend, da maximale Transfers
Datentyp
1xZeile 100xZeilen Konstant
Faktor
Abweichung
long
3293 B
195211 B
1545,64 B
1936,36 B
-17,74 kb
String
3027 B
167611 B
1528,46 B
1660,57 B
4,35 kb
double
3203 B
185516 B
1543,16 B
1839,46 B
-2,39 kb
numeric
3253 B
185667 B
1594,06 B
1840,66 B
-13,22 kb
numeric+string
3515 B
207297 B
1661,64 B
2056,27 B
13,73 kb
numeric+long
3733 B
229916 B
1675,75 B
2282,29 B
-3,75 kb
everything
7709 B
575095 B
2547,12 B
5725,06 B
-3,9 kb
Tabelle 3.3.: Dateigröÿe von SOAP-Nachrichten - Sichtproben mit Abweichung
von mindestens 10 MB von Axis zugelassen, aus Gründen der Latenzzeiten aber
wesentlich kleinere Werte bevorzugt werden. Dadurch ergibt sich bei der Aufteilung
(mit Nutzung aller Datentypen) meistens eine Zeilengröÿe von nur circa 200, wodurch nur geringfügige Abweichungen auftreten. Aber auch Extremfälle von circa 1
MB stellen kein Problem dar. Aufgrund der Stichproben wird folgende Formel verwendet: Unterschiede in der Gröÿe einzelner Variablen werden ebenfalls nicht mit
einbezogen. Auch wenn dieser Wert nicht genau ist, haben alle Stichproben (auch
3.6. Implementierung der Schnittstelle
35
mit wesentlich mehr Werten und zufälligen Variablengröÿen) zu guten Ergebnissen
geführt. Da zur Messung ein
TCP-Wrapper
zwischen Client und Dienst geschaltet
werden musste und jede Nachricht manuell bearbeitet wurde, wäre ein automatische
Berechnung nicht praktikabel.
Der erste Teil der Ergebnismenge wird übermittelt, während die Restmenge eine bestimmte Zeit zwischengespeichert und über eine weitere Operation des Datenbankdienstes abgeholt werden kann. Dazu erhält jede Anfrage dieser Art eine eindeutige
ID. Um den Server vor einer Überlastung zu schützen werden Zustandsinformationen nur einen kurzen Zeitraum zwischengespeichert. Durch dieses Konzept ist die
Behandlung von beliebig groÿen Ergebnismengen möglich, solange die entsprechenden Ressourcen zur Verfügung stehen. Eine transparente Nutzung dieser Technik ist
über die SOAP-JDBC-Bridge (siehe Kapitel 3.7.1) ohne Probleme möglich, die zudem die parallele Übertragung der verschiedenen Ergebnisteile unterstützt. Auswirkungen auf die Performance und die optimale Einstellung der maximalen Dateigröÿe
werden in Kapitel 5.4 genau diskutiert.
3.5.6. Sicherheit - Begrenzung der Datentypen
Bei der Nutzung des Dienstes muss immer auch an Arten des Missbrauchs gedacht
werden, da die Clientsoftware grundsätzlich auch nicht immer sicher sein muss. Dabei
ergeben sich verschiedene Angrisszenarien oder auch unbeabsichtigte Schwachstellen. Wie eben vorgestellt wurde, kann ein Client
Prepared Statements
übermitteln
und dabei dynamisch Paramtermengen übertragen (diese Technik wird bei
dates
ebenfalls benutzt). Damit hier keine
DoS-Attacken
Batchup-
ausgeführt werden können
wurde diese Menge künstlich im XML-Schema beschränkt. Eine weitere Schwachstelle ist die Beschränkung der Speicherressourcen auf Serverseite. Zwar werden in den
meisten Fällen Zustandsinformationen nur für die Dauer einer Abfrage zwischengespeichert, allerdings wurden im Laufe der letzten Kapitel auch Ausnahmen beschrieben. Unter anderem die Speicherung von groÿen Ergebnismengen, die Speicherung
von
Batchupdates
und das Ausschalten von
AutoCommit .
Einige dieser Techniken
werden erst in den folgenden Kapiteln verdeutlicht. Durch diese Funktionen kann
es vorkommen, dass mehr Arbeitsspeicher benötigt wird als tatsächlich zur Verfügung steht und somit die Verfügbarkeit des Servers in Gefahr ist. Dieses Szenario
wird bestmöglich durch kurze Speicherzeiten und Nutzungsbeschränkungen je Benutzer verhindert. Allerdings lässt sich dieses Problem nicht hundertprozentig lösen.
Grundsätzlich wurde bei der Implementierung auf viele dieser Szenarien geachtet
um die Risiken einzuschränken.
3.6. Implementierung der Schnittstelle
In den bisherigen Kapiteln wurden alle erforderlichen Techniken zur Abarbeitung
von SQL-Abfragen genau erläutert. In diesem Abschnitt wird die Schnittstelle des
Datenbankdienstes mit ihren spezischen Operationen vorgestellt. Letztendlich sind
nur diese Operationen (siehe Tabelle 3.4) nach auÿen hin sichtbar. In den nachfolgenden Kapiteln werden die wichtigsten Operationen noch einmal genauer erklärt.
36
3. Architektur
Operation
Beschreibung
getDatabaseList()
Gibt eine Liste aller über das System angelegten Datenbanken zurück. Rechte werden dabei ebenfalls beachtet. Das heiÿt, es werden nur Eigene oder Datenbanken mit Lese/Schreibrechten angezeigt.
databaseExists(String name)
Prüft ob die angegebene Datenbank besteht. Die entsprechenden Rechte darauf vorausgesetzt.
createDatabase(String name)
Legt eine Datenbank als Kombination von Domäne,
Benutzernamen und dem übergebenen String zurück.
Unerlaubte Zeichen werden umgewandelt und der endgültige Name wird zurückgegeben. Eine Umwandlung
ist erforderlich, um eine eindeutige Zuordnung zu erreichen, da auch Rechte auf andere Datenbanken mit
dem gleichen Namen existieren könnten.
dropDatabase(String name)
Löscht die angegebene Datenbank (vollständiger Name).
query(SQLQuery query)
(siehe Kapitel 3.6.1)
execute(SQLQuery exec)
setPermissions(SQLPermissions (siehe Kapitel 3.6.2)
p)
getPermissions()
nextResultSet(long id)
Hierüber kann die nächsten SQLValueSet abgeholt
werden, falls eine Aufteilung der Ergebnissmenge erfolgt ist.
tableExists()
Prüft ob eine Tabelle existiert. Liefert nur ein positives
Ergebnis falls man Rechte darauf hat.
commit(long id)
Ausführung einer Transaktion.
rollback(long id)
Zurücksetzen einer Transaktion.
Tabelle 3.4.: Operationen des Datenbankdienstes (DBControl)
3.6.1. Die Operationen query und execute
Wie auch bei der JDBC API sind die Funktionsweisen dieser beiden Operationen
überwiegend identisch. Die Execute-Operation ist lediglich auf die Manipulation von
Daten über
INSERT, UPDATE, DELETE - Statements ausgelegt, während bei der
SELECT-Statements verwendet werden können und ein Ergeb-
Query-Operationen
nis zurückgeliefert wird. Deshalb ist hier nur die letztgenannte Methode hervorgehoben. Zur Steuerung der Funktionsweise wird ein
SQLQuery -Objekt übergeben, dass
mittels XML-Schema zuvor deniert wurde (siehe Listing 3.10).
< complexType name = " SQLQuery " >
< sequence >
< element name = " dbname " type = " tns:databaseName " / >
< element name = " queryString " type = " basic:String " / >
< element name = " isolation " type = " tns:transactionIsolation "
nillable = " true " / >
< element name = " autoCommit " type = " boolean " nillable = " true " / >
< element name = " persistentMode " type = " boolean " nillable = " true " / >
< element name = " persistantId " type = " long " nillable = " true " / >
3.6. Implementierung der Schnittstelle
37
< element name = " valueTypes " type = " tns:valueType " nillable = " true "
minOccurs = " 0" maxOccurs = " 100 " / >
< element name = " values " type = " tns:PreparedValues " nillable = " true
" minOccurs = " 0 " / >
</ sequence >
</ complexType >
Listing 3.10: SQLQuery-Objekt
Durch die Bestimmung der verschiedenen Variablen lassen sich unterschiedliche Verhaltensweisen erzielen. Dabei muss beachtet werden, dass zwischen dem Dienst und
dem Benutzer nie eine Verbindung besteht, also im Normalfall keine Zustandsinformationen über einen Aufruf hinaus gespeichert werden. Die folgende Liste beschreibt
wie die Konguration dieses Objekts die Verhaltensweise des Datenbankdienstes beeinusst:
dbname
Aufgrund der Zustandslosigkeit ist die Angabe des vollständigen Daten-
banknamens (Domäne_Benutzername_Wahlname) bei jeder Nutzung erforderlich.
queryString
isolation
Die SQL-Abfrage als String.
Diese Variable bestimmt das Isolationslevel der Transaktion. Mögliche
Werte (serializeable,
ne )werden
repeatable_read, read_uncommitted, read_committed, no-
ebenfalls über das SQL-Schema bestimmt und vom Dienst in die
entsprechenden Konstanten der JDBC API umgewandelt.
autoCommit
Hier wird bestimmt ob die einzelne Abfrage direkt bestätigt wird oder
mehrere Abfragen hintereinander übermittelt und dann gemeinsam ausgeführt
oder abgebrochen werden. Hierzu muss der Dienst allerdings Zustandsinformationen speichern. Dieser Vorgang wird unter 3.6.4 beschrieben.
persistentMode
Diese Variable schaltet das System ebenfalls in einen erweiterten
Modus indem Zustandsinformationen gespeichert werden.
persistentId
Der Datenbankdienst ordnet der Abfrage über diese Identikation dem
entsprechend gespeicherten Zustand zu.
valueTypes
Bei der Nutzung von
Prepared Statements
zelner Parameterzeilen notwendig. Ähnlich wie die
ist die Übermittlung ein-
SQLValueSet
(siehe Kapi-
tel 3.5.3 auf Seite 30), kann hier eine beliebige Menge von Javadatentypen
übermittelt werden. Dieses Array ermöglicht die Identizierung der benutzten
Typenkonguration.
values
Hierin werden einzelne Parameterzeilen gespeichert. Somit ist die mehrma-
lige Ausführung eines vorkompilierten
Prepared Statements
möglich.
Durch diese Liste wird deutlich, dass über jene einzelnen Operationen ein groÿer
Teil der JDBC API bezüglich der Abarbeitung von SQL-Abfragen abgedeckt wird.
Hiermit wird allen Nutzern des Servicegrids unabhängig von der eingesetzten Programmiersprache ein einheitlicher Datenbankservice zur Verfügung gestellt. In Java
38
3. Architektur
ist die Nutzung dieser Schnittstelle jedoch nicht unbedingt notwendig (siehe Kapitel
3.7.1). Das nächste Kapitel beschreibt, wie man das Rechtesystem des Datenbankdienstes benutzt.
3.6.2. Die Operationen getPermissions und setPermissions
Der Besitzer einer Datenbank kann über diese Operationen die Rechte für andere
Benutzer der
Venice -Föderation
verwalten. Mit Hilfe des Objekts in Listing 3.11
können die einzelnen Tabellen einer Datenbank konguriert werden.
< simpleType name = " right " >
< restriction base = " basic:String " >
< enumeration value = " read " / >
< enumeration value = " write " / >
< enumeration value = " none " / >
</ restriction >
</ simpleType >
< complexType name = " SQLPermission " >
< sequence >
< element name = " domain " type = " basic:String "/ >
< element name = " username " type = " basic:String " / >
< element name = " name " type = " basic:String " / >
< element name = " right " type = " tns:right " / >
</ sequence >
</ complexType >
< element name = " SQLPermission " type = " tns:SQLPermission " / >
< complexType name = " SQLPermissions " >
< sequence >
< element name = " database " type = " tns:databaseName " / >
< element name = " tables " type = " tns:SQLPermission " nillable = " true "
minOccurs = " 0" maxOccurs = " unbounded " / >
</ sequence >
</ complexType >
Listing 3.11: SQLPermission-Objekt
Ein Recht besteht dabei entweder aus dem
String
read zum Lesen, write zum Schrei-
ben oder none um die Einstellung wieder zu löschen. Jede Tabelle kann bezogen auf
den Benutzer einer Domäne mit einem dieser Attribute ausgestattet werden. Greift
ein fremder Benutzer auf die Tabellen der Datenbank zu, werden die genannten
Einstellungen bei jeder SQL-Abfrage durch das zuvor beschriebene Zugrissystem
überprüft.
3.6.3. Fehlerbehandlung
Jede der zuvor beschriebenen Operationen des Dienstes löst im Fehlerfall eigens
denierte Fehlermeldungen aus. In SOAP-Nachrichten können beliebige Fehlermeldungen als Antwort innerhalb des Bodybereiches übergeben werden. Die Denition
von neuen Fehlertypen ist wiederum mit Hilfe von XML-Schemadateien möglich.
Venice bereits mehrere Fehlerarten, wie beispielsweise BaseFault oder
AuthorizationFault deniert wurden, werden auf dieser Grundlage die Fehlertypen
Während in
3.6. Implementierung der Schnittstelle
SQLFault
und
SQLPermissionFault
39
im Datenbankdienst verwendet um Fehler zur
Clientapplikation zu übermitteln. Tritt beispielsweise ein SQL-Fehler beim Ausführen eines Statements auf, wird dieser mittels einer dieser Fehlermeldungen zum Clienten übertragen.
3.6.4. Erweiterung - Speicherung von Zustandsinformationen
Wie bereits mehrfach erwähnt, zählt die Zustandslosigkeit zu einem der zentralen
Aspekte einer SOA. Die bisher beschriebenen Implementierungen beachten diese
Regel auch weitestgehend. Auf der anderen Seite wurden auch einige Konzepte vorgestellt, für welche andere Vorgehensweisen vorteilhaft sind. Im folgenden werden
die Techniken aufgelistet:
Prepared Statements
Hierbei wird eine SQL-Abfrage einmal vom DBMS kompi-
liert und kann daraufhin für mehrere verschiedene Parameterkombinationen
wiederverwendet werden. Für oft ausgeführte Abfragen ergibt sich somit ein
groÿer Performancegewinn. Allerdings ist ein solches Statement immer an eine spezische Verbindung gekoppelt. Daraus ergibt sich, dass die Verbindung
einem Nutzer des Dienstes zugeordnet und über mehrere Abrufe hinweg gespeichert werden muss. Deshalb ist es möglich, den Dienst für eine einstellbare Zeit
in einen persistenten Modus zu versetzen. In diesem Modus wird das
objekt
Zugris-
nicht wieder freigegeben, sondern unter einem eindeutigen Zeitstempel
gespeichert. Innerhalb des Zugrisobjektes wird die zugeordnete
Connection
ebenfalls nicht wieder freigegeben um Einstellungen beibehalten zu können.
Alle Anfragen werden durch Hashverfahren daraufhin überprüft ob sie bereits
existieren, damit sie wiederverwendet werden können. Ein weiterer Vorteil ist,
dass die SQL-Abfragen nur beim ersten Aufruf auf Berechtigung überprüft
werden müssen.
AutoCommit
Wurde diese Funktion aktiviert, muss eine Verbindung solange auf-
rechterhalten werden bis ein Auruf von
commit
oder
rollback
erfolgt. Auch
dieser Mechanismus wird durch die Zuweisung eines eindeutigen Zeitstempels
zu einem Zugriobjekt ermöglicht. Allerdings müssen hierbei keine zusätzlichen Informationen gespeichert werden. Bei einem Timeout wird automatisch
ein
rollback
ausgelöst.
Nachrichtenaufteilung
Überschreitet die Ergebnissmenge eine bestimmte Dateigrö-
ÿe, so muss die Nachricht in mehrere Segmente aufgeteilt werden. Das ersten
wird mit Angabe einer
ID
direkt übermittelt, während die anderen für eine
vorbestimmte Zeit unter diesem Zeitstempel nacheinander abgerufen werden
können.
Diese Features können abhängig von der Clientimplementierung je nach Bedarf aktiviert werden. Im folgenden Kapitel wird die Benutzung des Datenbankdienstes von
clientseite aus betrachtet.
40
3. Architektur
3.7. Nutzung des Dienstes
Während in den letzten Kapiteln die Schnittstellen und ihre Benutzung hervorgehoben wurden, wird hier beschrieben wie das System auf Clientseite verwendet werden
kann. Eines der Entwicklungsziele stellt die intuitive Nutzung des Datenbankdienstes dar. Es wurde bereits in Beispielen gezeigt, dass der Dienst einen groÿen Teil
der JDBC API abdeckt, die direkte Nutzung aufgrund der Kommunikation über
SOAP/XML jedoch sehr kompliziert werden kann (siehe Listing 3.6 auf Seite 31).
Deshalb wurden Java-Wrapperklassen entwickelt, welche die herkömmliche JDBC
API nachbilden, um einfacher adaptieren zu können. Im nächsten Kapitel wird dieses
System unter dem Begri SOAP-JDBC-Bridge eingeführt. Am Ende des Abschnitts
verdeutlich ein Vergleich die Vorteile des Systems.
3.7.1. Einführung der SOAP-JDBC-Bridge
Die grundlegende Idee dieser Technik beruht darauf, heterogenen Clienten den Zugri auf eine Datenquelle mit Hilfe von SOAP zu ermöglichen und die Probleme
der JDBC API so zu umgehen. So wird auf Clientseite beispielsweise kein spezischer Datenbanktreiber (bei heterogenen Systemen reicht einer nicht aus) mehr
benötigt. Die SOAP-JDBC-Bridge [Des03] wurde als sprachenunabhängiges Konzept vorgestellt und wird hier an die speziellen Bedürfnisse von Venice angepasst
und weiterentwickelt. Allerdings wurde nur eine Untermenge der JDBC API mit
den wichtigsten Funktionen entwickelt. In Tabelle 3.7.1 wird eine Übersicht über
die implementierten Funktionen in Pseudocode gegeben. Da der Datenbankdienst
ein erweitertes Rechtemanagement anbietet wurden auch einige neue Funktionen
hinzugefügt, um deren Steuerung zu ermöglichen. Standardfunktionen wurden farblich markiert und werden nicht genauer beschrieben. Dabei muss beachtet werden,
dass die Parameter nicht genau der Implementierung entsprechen. Die Klasse DriverManager wurde nicht implementiert, da kein Treiber auf Clientseite benötigt wird.
Stattdessen wurde die Klasse DatabaseManagement eingeführt, um die Datenbankerstellung zu verwalten. Durch die Tabellen wird ersichtlich, dass sich auÿer einzelnen
zusätzlichen Funktionen, die SOAP-JDBC-Bridge wie die JDBC API verwenden
lässt, in dem Besonderheiten des Datenbankdienstes wie die Ergebnismenge
luesSet ,
das
SQLQuery -Objekt,
das
SQLPermission -Objekt
SQLVa-
und das Splitten von
Nachrichten sowie persistente Verbindungen versteckt, beziehungsweise im Hintergrund verarbeitet werden. Im nächsten Kapitel wird durch ein Beispiel verdeutlicht
welche Bedeutung dieses System für die Programmierung von neuen Applikationen
hat.
3.7.2. Ein Vergleich
Nachfolgend wird die Verarbeitung einer SQL-Abfrage auf Clientseite veranschaulicht. Listing 3.12 beschreibt die Nutzung des Datenbankdienstes ohne die Wrapperklassen, während in Listing 3.13 die SOAP-JDBC-Bridge eingesetzt wird. Als SQLAbfrage wird auf beiden Seiten das Statement SELECT value FROM test WHERE
3.7. Nutzung des Dienstes
JDBC-Klasse
JDBC-Funktionen
DatabaseManager
41
Beschreibung
getConnection(dbname)
Gibt ein Connectionobjekt zur angegebenen Datenbank zurück.
createDatabase()
Die Datenbankerstellung und Löschung ist nur über diese Funk-
dropDatabase()
tionen möglich, da entsprechende SQL-Abfragen nicht vom Da-
getDatabases()
Gibt eine Liste aller vom System verwalteten Datenbanken zu-
tenbankdienst zugelassen werden.
rück. Diese müssen über createDatabase() angelegt worden sein
und der Benutzer muss entsprechende Rechte besitzen.
databaseExists()
Überprüft ob eine Datenbank existiert.
setup(wsdl, domain)
Konguration der Wrapperklassen durch Angabe der WSDLSchnittstelle (QName) des Datenbankdienstes und der momentanen Domäne. (wird intern vom DatabaseManager verwendet)
getInstance(database)
Connection
Gibt ein Datenbankobjekt zurück, dass auf den entsprechenden Datenbanknamen, die WSDL-Schnittstelle und die Domäne xiert ist. Dies bedeutet nicht das eine Verbindung zum
PostgreSQL-DBMS oder dem Datenbankdienst existiert. (wird
intern vom DatabaseManager verwendet)
setPermissions()
Rechte der eigenen Datenbank für weitere Benutzer setzen und
getPermissions()
lesen.
tableExists(tableName)
Einfache Funktion um zu überprüfen, ob bereits eine Tabelle in
dieser Datenbank mit dem angegebenen Namen angelegt wurde.
setAutoCommit(), getAutoCommit()
commit(), rollback()
getTransactionIsolation(), setTransactionIsolation()
prepareStatement()
executeQuery(sqlString)
PreparedStatement
Konguriert ein SQLQuery-Objekt und ruft die entsprechende
executeUpdate(sqlString) Operation des Datenbankdienstes auf.
setXXX()
Fügt dem SQLQuery-Object einzelne Werte hinzu und konguriert die Datentyparray.
getMetaData()
getXXX()
ResultSet
Umwandlung der verschiedenen Parameter einer Zeile aus den
entsprechenden Werten der SQLValueSet. Dabei werden die in
Tabelle 3.2 aufgezälten Datentypen unterstützt.
next()
Wechsel zur nächsten Zeile. Falls die Ergebnissmenge aufgeteilt
wurde, wird automatisch die nächste SQLValueSet abgerufen.
getMetaData()
ResultSetMetaData
SQLException
getColumnCount()
getColumnTypeName()
SQLException()
Verschiedenen Konstruktoren um die SOAP-Fehler SQLPermis-
sionFault und SQLFault umzuwandeln.
Tabelle 3.5.: Funktionsumfang der SOAP-JDBC-Bridge
value = ? verwendet, wobei die Testtabelle nur eine einzige Spalte mit Integerwerten enthält.
// SQLQuery - Objekt vorbereiten
String dbname = SAL . ws_makeCall ( wsdl , domain ,
BASIC_DATABASE_PORTTYPE , " createDatabase " , " test " ) ;
SQLQuery query = new SQLQuery () ;
query . setDbname ( dbname ) ;
query . setIsolation ( " serializeable " );
query . setAutoCommit ( true ) ;
query . setQueryString ( sqlString ) ;
String valueTypes [] = { " int " };
query . setValueTypes ( valueTypes ) ;
query . setValues ( new PreparedValues () ) ;
PreparedValueRow row [] = new PreparedValueRow [1];
row [0] = new PreparedValueRow () ;
row [0]. set_int ( new IntArray () ) ;
int value [] = { 25 };
row [0]. get_int (0) . setContents ( value ) ;
query . getValues () . setRow ( row ) ;
42
3. Architektur
// SQLAbfrage abschicken ( Unterstuetzung durch Venice )
SQLValueSet result = SAL . ws_makeCall ( wsdl , domain ,
BASIC_DATABASE_PORTTYPE , " query " , query ) ;
// Ergebnismenge bearbeiten ( vereinfacht )
SQLRow values [] = values . getSQLRows () . getSQLRow () ;
System . out . println ( values [0]. getInt () . getContents (1) ) ; // Ausgabe :
25
Listing 3.12: Nutzung des Dienstes (ohne Wrapperklassen)
// SQLQuery - Objekt vorbereiten
DatabaseManager man = new DatabaseManager ( domain , wsdl , SAL ) ;
Connection con = man . getConnection ( man . createDatabase ( " test " ) ;
PreparedStatement pstmt = con . prepareStatement ( sqlString ) ;
pstmt . setInt (1 , 25) ;
// SQLAbfrage abschicken
ResultSet result = pstmt . executeQuery () ;
// SQLValueSet bearbeiten
while ( result . next () ) {
Sytem . out . println ( result . getInt (1) ); // Ausgabe : 25
}
Listing 3.13: Nutzung des Dienstes (mit Wrapperklassen)
Die Vorteile des Systems lassen sich in dieser Gegenüberstellung sehr deutlich erkennen. Dabei wird hier die Fehlerbehandlung, die Nachrichtensplitterung und die
dynamische Erkennung der Ergebnismenge ignoriert. Dies würde zu erheblich komplexeren Strukturen führen. Durch die Wrapperklassen ist auÿerdem eine Anpassung
der bereits implementierten Dienste und Applikationen des Venice-Systems an die
neue Datenbankschnittstelle unkomplizierter, da sich die API kaum von der ursprünglichen JDBC API unterscheidet. Auÿerdem werden aufgeteilte Nachrichten
mit Hilfe mehrerer
Threads
bearbeitet, wodurch ein erheblicher Performancegewinn
erzielt wird. Dieses System wurde nur für Java implementiert, aber eine Erweiterung
in anderen Programmiersprachen ist ohne weiteres denkbar. Letztendlich kann der
Dienst auch ohne dieses System verwendet werden. Allerdings muss dem Programmierer bewusst sein, dass die Abfragen vollkommen zustandslos ablaufen, solange
keine persistente Verbindung benutzt wird. Selbst dann schlieÿt der Dienst die
Verbindung selbstständig nach einer maximalen Zeitspanne, so dass auf Clientseite
nicht darauf geachtet werden muss, wann eine Verbindung auf- oder abgebaut wird.
3.8. Schematische Darstellung: Ein Aufruf der
query -Operation
Am Anfang des Architekturabschnitts in Bild 3.1 wurde ein grober Überblick über
die Stufen, die eine SQL-Abfrage durchläuft, vorgestellt. Nachdem alle Bereiche dieser Grak im Detail anhand der Implementierung besprochen wurden, wird hier
in Bild 3.4 auf 46 anhand eines genaueren Schematas ein Ablauf mit einigen vorgestellten Sonderfällen und Optimierungen durchlaufen. Während ein Groÿteil des
dargestellten Ablaufs in der Abbildung selbsterklärend ist, wird die Benutzung von
3.9. Erfüllung der Anforderungen
43
Zugrisobjekten nocheinmal kurz hervorgehoben. Wird eine Operation (query, execute) aufgerufen, so wird überprüft, ob es sich um eine persistente Verbindung handelt.
Falls nicht, wird das System auf Seite A (siehe Abbildung) durchlaufen. Das heiÿt,
das Rechtesystem verwendet ein Zugrisobjekt des
AccessPools
auf die systemeigene
Datenbank (dbcontrol) um die Existenz der Datenbank und die benötigten Rechte
zu prüfen. Im Fall einer persistenten Verbindung wird ein Zugrisobjekt aus dem
StorageManager
verwendet und PreparedStatments können wiederverwendet wer-
den (siehe Weg B in der Abbildung). Hiernach funktioniert das System wieder auf
die gleiche Art und Weise. Im nächsten Kapitel wird die bis hierher beschriebene Implementierung anhand der zentralen Aspekte serviceorientierter Architekturen sowie
der an das System gestellten Anforderungen bewertet.
3.9. Erfüllung der Anforderungen
In Kapitel 3.1 wurden verschiedene Anforderungen bezüglich des Funktionsumfangs
und des Designs der Architektur der Datenbankschnittstelle gestellt. In der folgenden
Liste wird erläutert wie diese Anforderungen umgesetzt wurden:
Funktionsumfang
•
Der SQL-Standard des PostgreSQL-DBMS wird zu einem sehr groÿen Teil
unterstützt. Dabei wurde die Nutzung von Datentypen auf die sinnvollsten innerhalb des
Venice Servicegrids
beschränkt. Lediglich die regulären Ausdrücke
des Rechtesystems behindern die Nutzung, was beim Besitzer einer Datenbank
aber vollständig vernachlässigt werden kann.
•
Die Standardoperationen der JDBC API werden durch den Datenbankdienst
unterstützt: Ausführung von statischen und dynamischen SQL-Abfragen, Ein-
Transaktionsisolation
mit, rollback ), Wiederverwendung
stellung der
•
und des
AutoCommits
(Operationen
com-
vorkompilierter SQL-Abfragen.
Möglichkeiten der Performanceoptimierung bestehen durch die Nutzung von
persistenten Verbindungen. Generell ist der Dienst so konzipiert, dass möglichst wenige entfernte Funktionsaufrufe benötigt werden.
•
Zugriskontrolle- und begrenzung wird durch ein eigenes performantes Rechtesystem implementiert, dass den Venice-SSO-Mechanismus verwendet.
•
Die Nutzung des Datenbankdienstes ist auf Clientseite vollkommen transparent und intuitiv möglich, da die SOAP-JDBC-Bridge eingeführt wurde, welche
die JDBC API nachbildet. Der Entwickler muss keine neue Semantik erlernen,
sollte sich aber der Hintergründe rudimentär bewusst sein.
44
3. Architektur
Designanforderungen
•
Durch die Nutzung von Webservices und der starken Typisierung der Datenbankschnittstelle, kann der Dienst von heterogenen Clienten verwendet werden. Die Schnittstelle ist aufgrund des XML-Schemas leicht zu verwenden.
Allerdings ist die Nutzung von Wrapperklassen zur Entwicklung stark zu empfehlen, da die direkte Verwendung der XML-Datentypen sehr aufwendig und
fehleranfällig ist.
•
Lediglich der Server benötigt einen Datenbankttreiber. Der Client kann vom
verwendeten DBMS vollständig abstrahieren.
•
Bei der Typumwandlung wurde stark auf die Performance geachtet. Der neue
Datentyp ist in der Lage alle Ergebnismengen zu übertragen.
•
Die Ergebnismenge kann aufgeteilt werden, um zu groÿe Nachrichten zu vermeiden und die Latenzzeiten beim Clienten zu verringern. Natürlich ergibt
sich durch die Verwendung von XML eine längere Verarbeitungszeit, die sich
jedoch nicht vermeiden lässt.
Die Anforderungen wurden vollständig erfüllt und sinnvolle Erweiterungen implementiert.
3.10. Umsetzung der SOA-Konzepte
Das SOA-Konzept ist eine Grundlage zur Entwicklung von leichtgewichtigen Servicegrids und damit natürlich auch von Venice. Während bei der Implementierung
eines neuen Dienstes durch die Nutzung des
und der Nutzung von
Webservices
ServiceAbstractionLayers
von
Venice
bereits viele der SOA-Konzepte (Wiederverwend-
barkeit, Dienst-Kontrakt, lose Kopplung, Zusammensetzbarkeit, Aundbarkeit) automatisch erfüllt werden, müssen einige Punkte immer wieder beachtet werden. In
der folgenden Liste wird die Umsetzung der oenen Punkte in Bezug auf den Datenbankdienst erklärt:
Abstraktion
Die Schnittstellen werden als eine Art Blackbox deniert. Durch die
starke Typisierung bei der Entwicklung der Datentypen (XML-Schema) und
die eindeutigen Operationen kann der Dienst ohne Kenntnis der Implementierung verwendet werden. Hierdurch wird die Wiederverwendbarkeit und lose
Kopplung unterstützt. Dieses Konzept wird zusätzlich durch die Nutzung der
SOAP-JDBC-Bridge weiterentwickelt.
Autonomie
Jeder Dienst muss für die genutzte, zugrundeliegende Technologie ei-
genverantwortlich sein. Das System benötigt lediglich ein funktionsfähiges PostgreSQL-DBMS und muss initial mit den Zugangsdaten konguriert werden.
Danach verwaltet sich das System vollkommen eigenständig und kann auch in
anderen Domänen leicht eingesetzt werden.
3.10. Umsetzung der SOA-Konzepte
Zustandslosigkeit
45
Jede Nutzung des Dienstes ist zustandslos umgesetzt, allerdings
können auf Wunsch persistente Datenbankverbindungen aus Performancegründen verwendet werden. Dabei werden Zustandsinformationen über mehrere
Abfragen hinweg gespeichert. Diese werden nach einem Timeout automatisch
wieder entfernt um eine Überlastung zu verhindern. Bei einem Fehlerfall arbeitet der Dienst normal weiter und kann automatisch eine neue Verbindung
anfordern.
Somit wurden alle wichtigen Konzepte beachtet und durch Venice sind zusätzlich
die Merkmale eines leichtgewichtigen Servicegrids vorhanden. Zusätzlich werden Sicherheitsmerkmale durch einen SSO-Mechanismus und Rechtemanagement geboten. Durch die Implementierung der generischen Datenbankschnittstelle steht Venice
letztendlich ein weiterer sinnvoller Dienst zur Verfügung, der sich föderationsweise
sehr einfach verwenden lässt.
46
3. Architektur
Client/Dienst - Anwendung
Individuelle Implementierung
SOAP
JDBC
API
Ausgabe:
neue
Datenbank
anlegen
SELECt * FROM newdb
ValueSet 1
ValueSet 2
ValueSet 3
ValueSet 4
DatabaseManager
Connection
ResultSet
PreparedStatement
Kommunikation
über SOAP
Server
Datenbank
-dienst
ResultSetWorker
ResultSetWorker
ResultSetWorker
SQLQuery-Objekt
dbname
queryString
autoCommit
isolation
values
createDatabase()
SQLValueSet
valueTypes
values
amount
ids
nextResultSet()
query()
DBControl - Schnittstelle
A
B
2 3
4
AccessPool
Permissions
StorageManager
SizeCalculator
Typmapper
PreparedStatement
JDBC API
ResultSet
PostgreSQL
Abbildung 3.4.: Datenbank Webservice: Detaillierte schematische Übersicht
4. Anwendung - Ein
Bibliotheksdienst
Dieser Abschnitt verdeutlicht die einfache Benutzung des Datenbankdienstes. Hierzu wird ein neuer Dienst mit einer zugehörigen graschen Oberäche implementiert.
Nachdem ein Überblick über die Architektur gegeben wurde, werden die wichtigsten
Details beider Komponenten kurz vorgestellt.
Die Bibliotheksarchitektur besteht aus zwei Komponenten: Dem
LibraryService , der
die Datenbankschnittstelle direkt nutzt um die Einträge verschiedener Bücher zu verwalten und dem
LibraryFrame ,
welcher als eigentliche Endapplikation eine simple
grasche Oberäche anbietet. Beide Applikationen nutzen den
Layer
ServiceAbstracion-
von Venice um die verschiedenen entfernten Operationen aufzurufen. Dar-
aus ergibt sich insgesamt die in Bild 4.1 gezeigte Architektur bei Betrachtung des
Gesamtsystems. Da es hier ausschlieÿlich um eine Präsentation einer clientseitigen
Bibliotheks-GUI
Datenbankdienst
AbstractServiceLayer
JDBC
Bibliotheksdienst
SOAP-JDBC-Bridge
PostgreSQL
AbstractServiceLayer
Abbildung 4.1.: Bibliothek: Architektur
Nutzung geht, wurde die Funktionalität sehr einfach gehalten. Es werden lediglich
Büchernamen ohne weitere Metadaten und Kategorisierungen gespeichert. Dazu verfügt der Dienst über die in Listing 4.1 aufgeführten Operationen.
operation basic:StringArray getBookList () throws faults:BaseFault ;
operation void addBook ( domain:SSOInformation sso , basic:String name
) throws faults:BaseFault ;
operation void delBook ( domain:SSOInformation sso , basic:String name
) throws faults:BaseFault ;
operation void editBook ( domain:SSOInformation sso , basic:String
oldName , basic:String newName ) throws faults:BaseFault ;
operation basic:StringArray searchBook ( basic:String name ) throws
faults:BaseFault ;
Listing 4.1: Bibliotheksdienst - Auschnitt der VSC-Datei
47
48
4. Anwendung - Ein Bibliotheksdienst
Abbildung 4.2 zeigt die grasche Oberäche der Bibliotheksanwendung, weche über
fünf Buttons (von links nach rechts: Suche, Buchliste aktualisieren, editieren, hinzufügen und löschen) zum Aufrufen der verschiedenen Dienstoperationen verfügt.
In Listing 4.2 wird ein Auschnitt der Implementierung des Dienstes gezeigt, wo-
Abbildung 4.2.: Bibliothek: Grasche Oberäche
bei die SOAP-JDBC-Bridge verwendet wird um die Einträge in der Datenbank zu
verwalten.
id initDB () {
try {
DatabaseManager manager = new DatabaseManager ( DOMAIN , wsdl , SAL
);
con = manager . getConnection ( manager . createDatabase ( " testbooks " )
);
if (! con . tableExists (" books " ) ) {
con . prepareStatement ( " create table books ( id serial , name
text ) " ) . executeUpdate () ;
}
} catch ( SQLException e ) {
LOGGER . error ( " Could not initialise database - connection and
initial tables " ) ;
serviceState = ServiceState . Disabled ;
}
}
public String [] getBookList () throws RemoteException , BaseFault {
Vector < String > rows = new Vector < String >() ;
PreparedStatement pstmt = con . prepareStatement ( " select * from
books " ) ;
ResultSet result ;
try {
result = pstmt . executeQuery () ;
while ( result . next () ) {
rows . add ( result . getString (2) ) ;
}
return rows . toArray ( new String [0]) ;
49
}
} catch ( SQLException e ) {
buildBaseFault ( " Could not retrieve booklist . " ) ;
}
return null ;
Listing 4.2: Bibliotheksdienst - Javaimplementierung
Die Nutzung der Datenbank in dieser simplen Applikation unterscheidet sich kaum
von der Arbeit mit der herkömlichen JDBC API. Die intuitive Anwendung des generischen Datenbankdienstes ist direkt ersichtlich. Im nächsten Kapitel wird das
System anhand verschiedener Testszenarien evaluiert.
50
5. Evaluierung
Die Wiederverwendbarkeit des entwickelten DB-Dienstes ist eine zentrale Anforderung; daher wird in diesem Kapitel der Datenbankdienst anhand verschiedener
Testszenarien evaluiert, um die Performance des Systems zu ermitteln und das Verhalten in bestimmten Extremsituationen zu analysieren. Für Zeitmessungen existiert
innerhalb von Venice bereits der Timekeeperservice, der über Funktionen zur Speicherung der Datensätze, Berechnung von Durchschnittswerten und Erzeugung von
graschen Visualisierungen der Messwerte verfügt. Dieses System wurde für alle hier
gesammelten Testwerte und Graken verwendet. In der nachfolgenden Tabelle werden die Leistungsdaten der eingesetzten Computer aufgelistet. Alle Rechner sind
Funktion
Name
CPUs
GHz
RAM in GB
Server
bandit
4
3.20
4
Client
monster
8
2.33
16
Tabelle 5.1.: Testumgebung: Eingesetzte Ressourcen
im gleichen Netz über ein Gigabitnetzwerk verbunden. Auf dem Server werden das
PostgreSQL-DBMS bereitgestellt und der Datenbankdienst ausgeführt während auf
dem Clientsystem die Testapplikationen laufen. Bei den Tests wurden ausschlieÿlich
die beiden Operation query und execute des Dienstes und deren Kongurationseigenschaften betrachtet. Die anderen Funktionalitäten werden meistens nur bei der
Initalisierung einer Applikation benötigt und spielen daher in der Regel keine zeitkritische Rolle. Deshalb ergeben sich fünf relevante Anwendungsmöglichkeiten die
in der folgenden Auistung näher erläutert werden:
CreateTable
In diesem Fall wird die folgende Tabelle mit allen vom Datenbank-
dienst unterstützten Datentypen angelegt. Diese wird auch als Basis für alle
weiteren Tests verwendet.
create table newtable ( varBigInt bigint , varBigSerial
bigserial , varBoolean boolean , varCharVarying character
varying (150) , varCharacter character (10) , varDate date ,
varDouble double precision , varInteger integer , varNumeric
numeric , varReal real , varSerial serial , varText text ,
varTime time , varTimestamp timestamp ) ;
Inserts
Die
Execute-Operation
zeichnet sich durch drei unterschiedliche Nutzungs-
weisen aus. Damit ein guter Vergleich möglich ist, wurden die eingefügten
Werte lediglich einmal zufallsgeneriert und für jede der drei Statements wiederverwendet.
51
52
5. Evaluierung
StaticInsert
Diese Anweisung verfügt über keine dynamischen Parameter.
Dies bedeutet, dass der Erstellungprozess des
SQLQuery -Objekts
auf
Clientseite und die Verarbeitung auf Serverseite verkürzt wird. Der SQLString kann nach der Überprüfung durch das Rechtesystem direkt ausgeführt werden.
DynamicInsert
fe des
Hierbei muss die SQL-Abfrage nach der Überprüfung mit Hil-
Typemappers
PersistentInsert
erstellt werden, bevor sie ausgeführt werden kann.
Diese Funktion wurde bereits ausführlich vorgestellt 3.6.4.
Nach dem ersten Aufruf wird das
Zugrisobjekt
und somit die SQL-
Abfrage wiederverwendet. Auÿerdem wird das komplette Rechtesystem
umgangen, wodurch ein erheblicher Anteil der Verarbeitungszeit eingespart wird.
Select
Bei dieser Abfrage wird ein Teil der angelegten Daten als Ergebnismen-
ge übertragen. Bei der SQL-Abfrage
SELECT * FROM newtable LIMIT 100
wurde
darauf geachtet, dass keine Aufteilung des Ergebnisses vorgenommen wird.
SplittedSelect
Bei der SQL-Abfrage
SELECT * FROM newtable LIMIT 500 muss die Er-
gebnismenge aufgeteilt werden, da die SOAP-Nachricht in diesem Fall eine
Gröÿe von 800kb nicht überschreiten darf. Mit Hilfe der Vorausberechnung
ergibt sich daraus eine Anzahl von 140 Zeilen pro
SQLValueSet -Objekt. Nach-
dem die hier ermittelten Werte mit den anderen Fällen verglichen wurden,
beschäftigt sich Kapitel 5.4 nochmals ausführlicher mit diesem Sonderfall.
Update - und Delete -Statements wurden nicht betrachtet, da sie lediglich Auswirkungen auf die JDBC-, bzw. PostgreSQL-Komponente haben. Der Datenbankdienst behandelt diese nicht anders als normale
Insert -Statements
(ausgenommen das Rech-
tesystem). Für alle hier vorgestellten Fälle wurden mindestens 5000 Durchläufe vorgenommmen, um aussagekräftige Werte zu erhalten. Dabei wurden die Messwerte an
verschiedenen Punkten im System aufgezeichnet, wodurch beispielsweise die Übertragung von der Verarbeitung aufgeschlüsselt werden kann. In der folgenden Liste
werden diese Messpunkte beschrieben:
•
Der erste Wert zeichnet die Dauer einer kompletten Abfrage einschlieÿlich der
clientseitigen Verarbeitung durch die SOAP-JDBC-Bridge auf.
•
Im zweiten Fall wird nur der Aufruf der entsprechenden Operation vom Clienten aus betrachtet (Übertragung).
•
Auf Serverseite wird nun die Zeit zwischen Aufruf der Operation und Rückgabe des Ergebnisses gemessen. Hierin spiegelt sich die serverseitige Verar-
beitung wieder.
•
Der letzte Messwert behandelt ausschlieÿlich die Verarbeitung des PostgreSQLDBMS und entspricht dem Aufruf der JDBC-Methode, um ein
Statement
Prepared
auszuführen. Hier bietet sich also auch ein direkter Vergleich zwi-
schen dem Overhead des Datenbankdienstes und einer normalen JDBC-Datenbankverbindung.
5.1. Ergebnisse
53
Durch diese Werte lässt sich also eine strukturierte Aufteilung in Übertragung, serverseitige Verarbeitung und JDBC-Aufruf erreichen. Da die eingesetzten Computer
und das Netzwerk niemals frei von äuÿeren Einüÿen sind, können vollkommen exakte Ergebnisse nicht garantiert werden. Unterschiede im Millisekundenbereich auch
zwischen den einzelnen Tests können daher das Ergebnis geringfügig verfälschen. Im
nächsten Kapitel werden die Ergebnisse der Messpunkte detailliert beschrieben und
grasch interpretiert.
5.1. Ergebnisse
Die nachfolgenden Tests wurden auf zwei Rechnern durchgeführt. Die Serverkomponenten mit dem Dienst und dem lokalen PostgreSQL-DBMS auf der einen Seite und
der Client mit mehreren Instanzen der Testapplikation auf der anderen Seite. Hier
werden die Ergebnisse der Szenarien
DynamicInsert
beschrieben. In Abbildung 5.1 und 5.2 sind die vom
NormalSelect im Detail
Timekeeperservice errechne-
und
ten Werte eines jeweils vollständigen Aufrufs erkennbar, das heiÿt, sie beinhalten
die client- und serverseitige Verarbeitung, die Übertragung und den JDBC-Aufruf.
Die rechte Grak zeigt in einer kumulativen Verteilung jeweils wieviel Prozent der
Abbildung 5.1.: Testfall: DynamicInsert - Laufzeit eines vollständigen Aufrufs
Anfragen in welcher Zeit durchgeführt wurden (DynamicInsert :
NormalSelect :
90% unter 20 ms,
80% unter 280 ms). In der nachfolgenden Tabelle 5.2 wird ausgehend
von diesen Werten ein Überblick über die verschiedenen Verarbeitungsschritte gegeben. Dabei handelt es sich um die Durchschnittswerte, die Standardabweichung
(in Klammern) und den maximalen Wert (max) in Millisekunden (ms). Hieraus
DynamicInsert
NormalSelect
Laufzeit
15 (27) max 401 ms
287 (400) max 2096 ms
Übertragung Server-Verarbeitung JDBC
11 ms
228 ms
1 ms
40 ms
3 ms
19 ms
Tabelle 5.2.: Testwertvergleich
kann man direkt erkennen, welche Nachteile durch die Benutzung von SOAP/XML
verursacht werden. Während die Datenbankschnittstelle insgesamt lediglich circa 4
54
5. Evaluierung
Abbildung 5.2.: Testfall: NormalSelect - Laufzeit eines vollständigen Aufrufs
ms bzw. 43 ms benötigt, werden für die Übertragung 11 ms oder 188 ms benötigt
werden. Natürlich handelt es sich dabei auf Client- und Serverseite um die Typumwandlung von XML in Java und umgekehrt. Der Dienst ist also nicht für die Verwendung in zeitkritischen Applikationen gedacht. Für die Anwendungen des Venice
Servicegrids reichen diese Latenzen aber vollkommen aus. Der Dienst könnte aber
auf
Interprocesscommunication
umgestellt werden, falls ein Dienst sehr zeitkritische
Anforderungen an den Datenbankdienst stellt. Dabei müssen beide Anwendungen
auf dem gleichen Computer ausgeführt werden und die Kommunikation läuft nicht
mehr mit Hilfe von SOAP/XML wodurch viel Verarbeitungszeit eingespart wird.
5.2. Belastungstest
Da der Datenbankdienst von mehreren Clienten der kompletten Veniceföderation
verwendet werden kann, wird die Servicekomponente in diesem Abschnitt einem
gleichzeitigen Zugri von Instanzen der Testapplikation ausgesetzt. Es werden Messungen in 4 Stufen vorgenommen: vier und acht Clienten mit einer Zugrispoolgröÿe von acht Verbindungen, sowie acht Clienten mit einer Zugrispoolgröÿe von
vier Verbindungen, bzw. zwei möglichen Verbindungen. Ein Client versucht jeweils
1000 gleichwertige Anfragen durchzuführen, wobei alle die gleiche Datenbanktabelle
ansprechen. Auf einer Seite soll hiermit die Performance unter starker Belastung
ermittelt werden. Auf der anderen Seite wird beobachtet, wie der Dienst in einer
Extremsituation reagiert, da mehr Anfragen parallel verarbeitet werden müssen als
von der zugrundeliegenden Schicht angeboten werden. Hieraus wird ersichtlich, wie
der neue Poolingmechanismus mit Nebenläugkeit und Überbelastung umgeht. Die
nachfolgende Tabelle 5.3 spiegelt die Ergebnisse dieses Szenarios wieder. Nach Betrachtung der Werte wird ersichtlich, dass die Laufzeiten zwar steigen, der Dienst
aber problemlos mit dieser Extremsituation umgehen kann, auch wenn mehr Abfragen erfolgen als Verbindungsobjekte zur Verfügung gestellt werden können. Dabei
wird jede Anfrage ausgeführt, es entstehen keine Timeouts.
5.3. Ergebnisvergleich
DynamicInsert
NormalSelect
55
8er Pool
4 Clienten
1 Client
15 (27)
max 401 ms
max 2044 ms
228 (340)
24 (46)
max 501 ms
max 2227 ms
301 (438)
4er Pool
8 Clienten
38 (73)
max 650 ms
max 3280 ms
477 (692)
Tabelle 5.3.: Ergebnisse des Belastungstests
5.3. Ergebnisvergleich
In der nachfolgenden Tabelle werden alle weiteren Testfälle im Vergleich präsentiert.
Hier wurden keine weiteren Nebenläugkeitstests durchgeführt, da sich die Fälle analog zu den bereits vorgestellten Szenarien verhalten. Auällig ist, dass die Nutzung
CreateTable
StaticInsert
DynamicInsert
PersistentInsert
NormalSelect
SplittingSelect
Vollständig
max 918 ms
16 (39) max 989 ms
15 (27) max 401 ms
12 (20) max 429 ms
228 (340) max 2044 ms
1051 (1553) max 3645 ms
35 (67)
Übertragung
ms
12 ms
11 ms
10 ms
188 ms
39 ms
8
Verarbeitung
JDBC
2
21
ms
2 ms
1 ms
1 ms
40 ms
1012 ms
2
3
2
3
8
ms
ms
ms
ms
ms
ms
Tabelle 5.4.: Ergebnisvergleich
von persistenten Verbindungen kaum einen Geschwindigkeitsvorteil verursacht. Dies
liegt daran, dass die Unterschiede nur bei der serverseitigen Verarbeitung eine Rolle spielen und die Übertragung den Groÿteil der Verzögerung einbringt. Allerdings
werden im Datenbankdienst dadurch beispielsweise mindestens 2 Datenbankabfragen weniger benötigt, wodurch Ressourcen geschont werden und das DBMS entlastet
wird. Die Werte von
SplittingSelect
sind hier nur approximiert, da sich die Gesamt-
laufzeit eines Testfalls auf mehrere Operationsaufrufe verteilt. Im nächsten Kapitel
wird die Verwendung von aufgeteilten Ergebnismengen bei Select-Abfragen genauer
diskutiert, da hier noch weitere Messwerte betrachtet werden müssen, die sich nicht
mit den anderen Testfällen vergleichen lassen.
Im Vergleich zu anderen Arbeiten lassen sich unter anderem erhebliche Performancevorteile erkennen. Beispielsweise benötigt JDBC-WS [DWD06] für 500 Zeilen mit
15 Spalten (ohne Datentypdenition) 120000 ms während hier nur 1051 ms benötigt
werden (SplittingSelect ). OGSA-DAI [ogs08] bietet dagegen bei den hier vorgestellten
Ergebnissen eine ähnliche Performance. Durch die Verwendung des GDS-Dokuments
ergibt allerdings, dass alleine die Verarbeitung ohne Ausführung der Abfragen immer
circa 147 ms benötigt. Updateanweisungen und Select-Statements mit kleinen Ergebnismengen haben bei dem hier entwickelten System also einen groÿen Vorsprung.
5.4. Sonderfall: Aufteilung von Ergebnismengen
Wie man im letzten Abschnitt erkennen kann, bedeutet die Aufteilung einer Ergebnismenge immer eine erhebliche Verzögerung, falls man den Gesamtvorgang betrachtet, also die Zeit, die benötigt wird, um alle Teilmengen abzurufen. Um ein
56
5. Evaluierung
gutes Gleichgewicht zwischen Verzögerung und Gesamtlaufzeit zu erhalten, wurden
mehrere Testläufe mit unterschiedlichen Dateimaximalgröÿen vorgenommen. Auÿerdem wurden weitere Messpunkte eingeführt um das Verhalten zu analysieren. In der
nachfolgenden Tabelle 5.5 kann man die Ergebnisse dieses Szenarios betrachten. Eine
#
1
2
SOAP-Nachrichten
Laufzeit
Dateigröÿe
Anzahl
Zeilen
800kb
4
140
1051 ms
1200kb
3
211
1788 ms
Art
Erster Aufruf
Parallelaufrufe
Erster Aufruf
Parallelaufrufe
Übertragung Verarbeitung JDBC
348
380
530
400
ms
ms
ms
ms
27 ms
2 ms
26 ms
2 ms
8 ms
8 ms
-
Tabelle 5.5.: SplittingSelect - Messung bei unterschiedlichen Maximaldateigröÿen
kleine Nachrichtengröÿe wirkt sich auf die Latenzzeit und die vollständige Ausführungszeit aus. Natürlich muss beachtet werden, dass bei einer Groÿengesamtmenge
sehr viele parallele Abfragen gleichzeitig ausgelöst werden.
Letztendlich sieht man in diesem Kapitel, dass der Datenbankdienst leicht zu verwenden ist und in den meisten Fällen auch performant Ergebnisse liefert. Der Dienst
ist weitaus schneller als vergleichbare Systeme (siehe JDBC-WS [DWD06]). Im nächsten Kapitel wird diese Arbeit nocheinmal zusammengefasst und mögliche Weiterentwicklungen vorgestellt.
6. Zusammenfassung und
Ausblick
Diese Bachelorarbeit behandelt die Entwicklung eines generischen Datenbankdienstes für das an der TU Kaiserslautern entwickelte leichtgewichtige Servicegrid
nice .
Ve-
Dazu wurden zuerst die theoretischen Grundlagen, wie serviceorientierte Ar-
chitekturen und Servicegrids angesprochen, sowie darauf folgend die eingesetzten
Technologien erläutert. Dazu gehören Webservices und deren WSDL-Schnittstellen,
sowie das SOAP-Nachrichtenformat auf Basis von XML. Als Datenbankmanagementsystem wird PostgreSQL verwendet, während der Verbindungsaufbau über die
JDBC API mit einem weiterentwickelten Poolingsystem verwaltet wird.
Venice
als leichtgewichtiges Servicegrid bietet hierbei ein oenes Framework für ver-
teilte Applikation, was die Implementierung, Installation, Integration und Benutzung von Diensten betrit. Zudem werden zentrale Konzepte von serviceorientierten Strukturen, wie beispielsweise Wiederverwendbarkeit, Zusammensetzbarkeit und
Aundbarkeit automatisch angeboten. Diese Funktionalitäten können von mehreren Domänen innerhalb einer gemeinsamen Föderation mit Hilfe von grundlegenden
Diensten wie einem Single-Sign-On-Dienst und einem Informationbroker, der die
Dienstinformationen verteilt, genutzt werden.
Der generische Datenbankdienst ist eine weitere Anwendung, die sich nahtlos in diese
Struktur integriert und eine zentrale Komponente darstellt. Zugrie auf relationale Datenbanksysteme werden von sehr vielen Nutzern und Applikationen in einer
Domäne und innerhalb von Datagrids benötigt. Während die direkte Verwendung
in einer verteilten Umgebung viele Probleme, wie Sicherheitseinschränkungen durch
Firewalls oder aufwendiges Treibermanagement für verschiedene Datenbanksysteme
verursacht, bietet diese Implementierung eine mit Hilfe von Webservices genau denierte Schnittstelle, die sprach- und plattformunabhängig von jeder Anwendung der
Veniceföderation genutzt werden kann.
Neben den zentralen SOA-Konzepten unterstützt das System einen groÿen Teil des
SQL-Standards und der JDBC API. Die Datentypunterstützung beschränkt sich
auf die für
Venice
relevanten Grundtypen. Erweiterungen in diesem Bereich sind
aber jederzeit leicht möglich. Um weitere Funktionen wie Transaktionen und die
Wiederverwendung von vorkompilierten Abfragen zu gewährleisten, besitzt das System einen erweiterten
chen
Connectionpools
Pool
an Datenbankzugrisobjekten welche mit den eigentli-
der Datenbanken zusammenarbeiten. Des weiteren verfügt der
Dienst über ein vierstuges Rechtesystem (ähnlich einer
Mandatory Access Control ),
das SQL-Abfragen mittels regulärer Ausdrücke analysiert und mit dem Venice-SSOMechanismus verknüpft. Die SOAP-JDBC-Bridge erleichtert und optimiert dabei
die Benutzung des Datenbankdienstes auf Clientseite. Durch die zu JDBC fast identische Schnittstelle lässt sich der Dienst ohne weiteres in vorhandene Strukturen
57
58
6. Zusammenfassung und Ausblick
integrieren.
Gegenüber den Ansätzen verwandter Technologien werden Ergebnismengen nicht
als kanonische XML-Daten versendet, sondern nach dem Prinzip der starken Typisierung zur Verfügung gestellt. Das XML-Schema bietet Unterstütztung für die
implementierten Datentypen und beliebige Zusammensetzungen der Ergebnismenge. Die Menge der übertragenen Spalten und Zeilen eines Ergebnisses ist dabei unbeschränkt. Die Gröÿe der resultierenden XML-Datei wird vorausberechnet und in
mehrere SOAP-Nachrichten aufgeteilt. Diese können parallel übertragen werden, um
Latenzzeiten zu verringern und technologische Grenzen einzuhalten.
Des weiteren verdeutlicht die Implementierung eines Bibliotheksdienst und einer entsprechende grasche Oberäche die transparente Nutzung des Datenbankdienstes
auf Clientseite mit Hilfe der SOAP-JDBC-Bridge. Zudem bietet sie interne Mechanismen um die Kommunikation durch Nebenläugkeit zu optimieren. Ein Entwickler
muss lediglich mit der ursprünglichen JDBC API vertraut sein. Der Datenbankdienst
steht aber auch ohne dieses Wrappersystem jedem Client unabhängig von der Implementierungsprache innerhalb der Veniceföderation zur Verfügung.
Im letzten Kapitel wurde der Dienst durch ausführliche Messungen evaluiert. Mit
Ausführungszeiten von 15 ms für Insert-Statements und 288 ms für Select-Statements
(innerhalb ausgewählter Testszenarien) bietet der Dienst eine gute Performance für
die meisten Anwendungen. Ein entsprechender JDBC-Aufruf benötigt im Vergleich
dazu 3 ms. Gegenüber anderen Ansätzen ergeben sich erhebliche Performancevorteile; JDBC-WS benötigt für die Übertragung einer entsprechenden Menge circa 20000
ms.
Obwohl das System bereits viele Funktionen abdeckt, ist die Architektur des Systems
noch erweiterbar. So sind weitere Performanceoptimierungen durch Cachingstrategien oder die clientseitige Umsetzung des Batchmechanismus der JDBC API denkbar.
Weiterhin wäre eine weitere Datenbankabstraktion auf Serverseite möglich, so dass
ein beliebiges Datenbankmanagementsystem oder andere Datenquellen verwendet
werden können. In diesem sehr aktuellen Forschungsgebiet lassen sich zahlreiche
neue Features für das System nden.
Abschlieÿend betrachtet, bietet der generische Datenbankdienst eine grundlegende
und wertvolle Funktionalität für das
Servicegrid Venice
und einen guten Startpunkt
für weitere Entwicklungen im Umfeld von serviceorientierten Architekturen, Servicegrids und Datagrids.
Anhang A.
Datenbankdienst: Schnittstellen
Die folgenden Listings beschreiben die Schnittstellendenition des Datenbankdienstes im VSC-Format und deren daraus kompilierte WSDL-Dateien. In Kapitel 2.4.1
wird die Funktionsweise des VSC-Kompilers und die Bedeutung der VSC/WSDLSchnittstellen erläutert.
A.1. VSC - Service-Operationen
Das folgende Listing A.1 auf Seite 60 beschreibt die VSC-Denition der Datenbankoperationen.
A.2. VSC - Service-Endpunkt
Dieses Listing A.2 auf Seite 61 deniert den VSC-Endpunkt des Datenbankdienstes.
A.3. WSDL - Abstract Service Denition
Dieses Dokument (Listing A.3 auf Seite 61) deniert die konkreten Denitionen der
Operationen, deren Parameter und Rückgabewerte sowie abstrakte Datentypen .
A.4. WSDL - Service Bindings
Diese Datei (Listing A.4 auf Seite 62) beschreibt das Protokoll und die Operationen
(SOAP-Encodierung und Namespace) der Datenbankschnittstelle.
A.5. WSDL - Concrete Service Denition
Die
Concrete Service Denition
liefert Informationen über den Endpunkt (Name,
Netzwerkadresse, Port) des Dienstes (siehe Listing 63 auf Seite A.5).
60
Anhang A. Datenbankdienst: Schnittstellen
nterface DBControl {
namespace
" http: // www . icsy . de / koch / wsdl /
database / " ;
location
" http: // www . icsy . de /~ koch / wsdl / de /
icsy / services / basic / database / " ;
types
sql
" http: // www . icsy . de /~ koch / schema /
sql . xsd " ;
types
basic
" http: // www . icsy . de /~ venice / types /
basic . xsd " ;
types
sqlFaults
" http: // www . icsy . de /~ koch / schema /
sqlFaults . xsd " ;
types
domain
" http: // www . icsy . de /~ venice / types /
domain . xsd " ;
types
faults
" http: // www . icsy . de /~ venice / types /
faults . xsd " ;
operation xsd:string createDatabase ( domain:SSOInformation
sso , xsd:string name ) throws
sqlFaults:SQLPermissionFault , sqlFaults:SQLFault ;
operation void dropDatabase ( domain:SSOInformation sso ,
xsd:string name ) throws sqlFaults:SQLPermissionFault ,
sqlFaults:SQLFault ;
operation basic:StringArray getDatabaseList (
domain:SSOInformation sso ) throws
sqlFaults:SQLPermissionFault , sqlFaults:SQLFault ;
operation void setPermissions ( domain:SSOInformation sso ,
sql:SQLPermissions perm ) throws
sqlFaults:SQLPermissionFault , sqlFaults:SQLFault ;
operation sql:SQLPermissions getPermissions (
domain:SSOInformation sso , xsd:string dbname ) throws
sqlFaults:SQLPermissionFault , sqlFaults:SQLFault ;
operation sql:SQLValueSet query ( domain:SSOInformation sso ,
sql:SQLQuery exec ) throws sqlFaults:SQLPermissionFault ,
sqlFaults:SQLFault ;
operation sql:SQLExecuteResult execute (
domain:SSOInformation sso , sql:SQLQuery exec ) throws
sqlFaults:SQLPermissionFault , sqlFaults:SQLFault ;
operation sql:SQLValueSet nextResultSet (
domain:SSOInformation sso , xsd:long id ) throws
sqlFaults:SQLPermissionFault , sqlFaults:SQLFault ;
operation xsd:boolean tableExists ( domain:SSOInformation sso
, xsd:string database , xsd:string table ) throws
sqlFaults:SQLFault ;
operation xsd:boolean databaseExists ( domain:SSOInformation
sso , xsd:string databaseName ) throws sqlFaults:SQLFault
;
operation void commit ( domain:SSOInformation sso , xsd:long
persistentId ) throws sqlFaults:SQLFault ;
operation void rollback ( domain:SSOInformation sso , xsd:long
persitentId ) throws sqlFaults:SQLFault ;
}
Listing A.1: VSC - Operationen
A.5. WSDL - Concrete Service Denition
1
2
3
4
5
6
7
8
61
import " ../ venice / conf / vsc / basic . vsc " ;
import " DBControlService_ interface . vsc " ;
service DBControl implements DBControl , ServiceInformation ,
ServiceManagement , ServicePreferences {
namespace " http: // www . icsy . de / services / database / " ;
location " http: // bandit:8005 / axis / services / Basic_ " ;
location " https: // bandit:8005 / axis / services / Basic_ " ;
package
" de . icsy . services . basic . database " ;
}
Listing A.2: DatabaseService.vsc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
< definitions targetNamespace = " http: // www . icsy . de / services / database /
" name = " DBControl "
xmlns = " http: // schemas . xmlsoap . org / wsdl / "
xmlns:tns = " http: // www . icsy . de / services / database / "
xmlns:ns2 = " http: // www . icsy . de / koch / wsdl / database / "
xmlns:ns1 = " http: // www . icsy . de / wsdl / basic / "
xmlns:xsd = " http: // www . w3 . org /2001/ XMLSchema "
xmlns:soap = " http: // schemas . xmlsoap . org / wsdl / soap / " >
<! -- ========== Import Service Fragments ============ -- >
< import namespace = " http: // www . icsy . de / koch / wsdl / database / "
location = " http: // www . icsy . de /~ koch / wsdl / de / icsy / services /
basic / database / DBControl - SOAP . wsdl "/ >
<! -- ========== Service Endpoint ============ -- >
< service name = " DBControlService " >
< port name = " DBControlHTTPPort " binding = " ns2:DBControlBinding " >
< soap:address location = " http: // bandit:8005 / axis / services /
Basic_DBControlPort " xmlns:wsdl = " http: // schemas . xmlsoap .
org / wsdl / " / >
</ port >
< port name = " DBControlHTTPSPort " binding = " ns2:DBControlBinding " >
< soap:address location = " https: // bandit:8005 / axis / services /
Basic_DBControlPort " xmlns:wsdl = " http: // schemas . xmlsoap .
org / wsdl / " / >
</ port >
</ service >
</ definitions >
Listing A.3: DatabaseService.wsdl
62
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Anhang A. Datenbankdienst: Schnittstellen
< definitions targetNamespace = " http: // www . icsy . de / koch / wsdl / database
/ " name = " DBControl "
xmlns = " http: // schemas . xmlsoap . org / wsdl / "
xmlns:tns = " http: // www . icsy . de / koch / wsdl / database / "
xmlns:xsd = " http: // www . w3 . org /2001/ XMLSchema "
xmlns:soap = " http: // schemas . xmlsoap . org / wsdl / soap / " >
<! -- ========== Import Service Fragment ============ -- >
< import namespace = " http: // www . icsy . de / koch / wsdl / database / "
location = " http: // www . icsy . de /~ koch / wsdl / de / icsy / services /
basic / database / DBControl . wsdl " / >
<! -- ========== SOAP Binding ============ -- >
< binding name = " DBControlBinding " type = " tns:DBControlPortType " >
< soap:binding style = " rpc " transport = " http: // schemas . xmlsoap . org
/ soap / http " / >
< operation name = " query " >
< soap:operation / >
< input > < soap:body use = " encoded " encodingStyle = " http: // schemas
. xmlsoap . org / soap / encoding / " namespace = " http: // www . icsy .
de / koch / wsdl / database / "/ > </ input >
< output > < soap:body use = " encoded " encodingStyle = " http: //
schemas . xmlsoap . org / soap / encoding / " namespace = " http: // www
. icsy . de / koch / wsdl / database / " / > </ output >
< fault name = " SQLPermissionFault " >< soap:fault name = "
SQLPermissionFault " use = " encoded " encodingStyle = " http: //
schemas . xmlsoap . org / soap / encoding / " namespace = " http: // www
. icsy . de / koch / types / faults / " / > </ fault >
</ operation >
</ definitions >
Listing A.4: Database-SOAP.wsdl
A.5. WSDL - Concrete Service Denition
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
63
< definitions targetNamespace = " http: // www . icsy . de / koch / wsdl / database
/ " name = " DBControl "
xmlns = " http: // schemas . xmlsoap . org / wsdl / "
xmlns:tns = " http: // www . icsy . de / koch / wsdl / database / "
xmlns:sql = " http: // www . icsy . de / koch / types / sql / "
xmlns:sqlFaults = " http: // www . icsy . de / koch / types / faults / "
xmlns:domain = " http: // www . icsy . de / types / domain / "
xmlns:faults = " http: // www . icsy . de / types / faults / "
xmlns:xsd = " http: // www . w3 . org /2001/ XMLSchema "
xmlns:soap = " http: // schemas . xmlsoap . org / wsdl / soap / " >
<! -- ========== Types ============ -- >
< types >
< xsd:schema elementFormDefault = " qualified " >
< xsd:import namespace = " http: // www . icsy . de / koch / types / sql / "
schemaLocation = " http: // www . icsy . de /~ koch / schema / sql . xsd " /
>
</ xsd:schema >
</ types >
<! -- ========== Messages ============ -- >
< message name = " Message_DBControl_query " >
< documentation > The request message for the operation query () . </
documentation >
< part name = " sso " type = " domain:SSOInformation " / >
< part name = " exec " type = " sql:SQLQuery " / >
</ message >
< message name = " Message_DBControl_queryResponse " >
< documentation > The response message for the operation query () . <
/ documentation >
< part name = " result " type = " sql:SQLValueSet " / >
</ message >
<! -- ========== Fault Messages ============ -- >
<! -- ========== Port Types ============ -- >
< portType name = " DBControlPortType " >
< operation name = " query " parameterOrder = " sso exec " >
< input message = " tns:Message_DBControl_query "/ >
< output message = " tns:Message_DBControl_queryResponse " / >
< fault message = " tns:FaultMessage_DBControl_SQLPermissionFault
" name = " SQLPermissionFault " / >
</ operation >
</ portType >
</ definitions >
Listing A.5: Database.wsdl
64
Anhang A. Datenbankdienst: Schnittstellen
Anhang B.
Rechtemanagement: Reguläre
Ausdrücke
In diesem Kapitel werden die regulären Ausdrücke aufgelistet, die zur Einordnung
von SQL-Abfragen in die verschiedenen Berechtigungsgruppen benötigt werden. Dabei werden zusätzlich ein paar Informationen zur Bedeutung jedes Ausdrucks gegeben (siehe Kapitel 3.4).
Prüfungsvorgang
1. Handelt es sich um einen Eintrag der vierten Gruppe
.*(? :create | alter | drop ) {1}\ s +(? :database | schema ) {1}.*
.*(? :grant | revoke ) {1}.* on {1}.*
2. Der Besitzer der Datenbank möchte eine Tabelle anlegen. Es handelt sich um
gültige Datentypen.
.*(? :create | alter | drop ) {1}[\ sa - z {}]+(? :table | index ) {1}.*
.*(? :create | alter ) {1}[\ sa - z {}]+ table {1}[\ w \ s ]+\((? : \ s *\ w +\ s +(?
:bigint | bigserial | bit (? : \ s *\([0 -9]+\) ) ?| bit \ s + varying (? : \ s
*\([0 -9]+\) ) ?| boolean | bytea | character \ s + varying (? :\ s
*\([0 -9]+\) ) ?| character (? : \s *\([0 -9]+\) ) ?| date | double \
sprecision | integer | numeric (? : \ s *\(\ s *[0 -9]+ ,\ s *[0 -9]+\ s *\) \
s *) ?| real | smallint | serial | text | time (? : \ s *\([0 -9]+\) ) ?|
timestamp (? :\ s *\([0 -9]+\) ) ?) {1}[\ w \s " " ' ]* ,?) +\ s *\) .*
3. Der Nutzer besitzt die nötigen Rechte auf die verschiedenen Tabellen
•
Schreibender Zugri auf Tabellen
(? :insert \ s + into | update | delete \ s + from (? : \ w + unique ) ?) +\ s +(\ w
+)
•
Lesender Zugri auf Tabellen (schrittweise)
select [\ w \s ,\*\(\) ]+ from \ s +(\ w +)
\ s *[\ w_ ]+\ s *\(
\ s *(? :only | natural ) ?\ s *(? : (? :left | right | full ) {1}\ s *(? :outer
) ?\ s + join |(? :inner ) ?\ s * join | cross \ s+ join ) ?\ s *(? :on \ s +\
w *|(\ w +) ) {1}\ s *\*?\ s *(? : (? :as \ s +\ w +| using ) ?\ s *(? : \([\ s
\w ,]+\) ) ?) ?
65
66
Anhang B. Rechtemanagement: Reguläre Ausdrücke
B.1. Ausnahmen
Hier werden die zwei einzigen Schwächen vorgestellt, bei denen die obigen regulären Ausdrücke an ihre Grenzen stoÿen und eine SQL-Abfrage aufgrunddessen nicht
zugelassen werden würde.
Subselects
Zeile 17:Innerhalb von
from_items
ist ein
Join
momentan nicht mit einer
Subquery
kombinierbar.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
SELECT [ ALL | DISTINCT [ ON ( expression [ , ...] ) ] ]
* | expression [ AS output_name ] [ , ...]
[ FROM from_item [ , ...] ]
[ WHERE condition ]
[ GROUP BY expression [ , ...] ]
[ HAVING condition [ , ...] ]
[ { UNION | INTERSECT | EXCEPT } [ ALL ] select ]
[ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS {
FIRST | LAST } ] [ , ...] ]
[ LIMIT { count | ALL } ]
[ OFFSET start ]
[ FOR { UPDATE | SHARE } [ OF table_name [ , ...] ] [ NOWAIT ]
[...] ]
where from_item can be one of :
[ ONLY ] table_name [ * ] [ [ AS ] alias [ ( column_alias [ ,
...] ) ] ]
( select ) [ AS ] alias [ ( column_alias [ , ...] ) ]
function_name ( [ argument [ , ...] ] ) [ AS ] alias [ (
column_alias [ , ...] | column_definition [ , ...] ) ]
function_name ( [ argument [ , ...] ] ) AS ( column_definition
[ , ...] )
from_item [ NATURAL ] join_type from_item [ ON join_condition |
USING ( join_column [ , ...] ) ]
Listing B.1: SQL-Syntax: SELECT - Problems
Create table
Zeile 17 und 18: Hinter Datentypen können momentan keine
Constraints
verwendet
werden, in denen runde Klammern verwendet werden.
1
2
3
4
5
6
7
8
9
10
11
12
CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE table_name
( [
{ column_name data_type [ DEFAULT default_expr ] [
column_constraint [ ... ] ]
| table_constraint
| LIKE parent_table [ { INCLUDING | EXCLUDING } DEFAULTS ] }
[ , ... ]
] )
[ INHERITS ( parent_table [ , ... ] ) ]
[ WITH OIDS | WITHOUT OIDS ]
[ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
[ TABLESPACE tablespace ]
where column_constraint is :
[ CONSTRAINT constraint_name ]
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{ NOT NULL |
NULL |
UNIQUE [ USING INDEX TABLESPACE tablespace ] |
PRIMARY KEY [ USING INDEX TABLESPACE tablespace ] |
CHECK ( expression ) |
REFERENCES reftable [ ( refcolumn ) ] [ MATCH FULL | MATCH
PARTIAL | MATCH SIMPLE ]
[ ON DELETE action ] [ ON UPDATE action ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY
IMMEDIATE ]
and table_constraint is :
[ CONSTRAINT constraint_name ]
{ UNIQUE ( column_name [ , ... ] ) [ USING INDEX TABLESPACE
tablespace ] |
PRIMARY KEY ( column_name [ , ... ] ) [ USING INDEX TABLESPACE
tablespace ] |
CHECK ( expression ) |
FOREIGN KEY ( column_name [ , ... ] ) REFERENCES reftable [ (
refcolumn [ , ... ] ) ]
[ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE
action ] [ ON UPDATE action ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY
IMMEDIATE ]
Listing B.2: SQL-Syntax: CREATE TABLE - Problems
68
Anhang B. Rechtemanagement: Reguläre Ausdrücke
Anhang C.
Datentypumwandlung
Dieses Kapitel stellt das XML-Schema mit den neuen Datentypen für den Datenbankdienst vor. Anhand von Codebeispielen wird die Verwendung innerhalb der
Programmiersprache Java erläutert (siehe Kapitel 3.5).
C.1. XML-Schema
Das XML-Schema mit den Datentypen des Datenbankdienstes.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<? xml version = " 1.0 " encoding = " UTF -8 " ? >
< schema targetNamespace = " http: // www . icsy . de / koch / types / sql / "
xmlns:tns = " http: // www . icsy . de / koch / types / sql / " xmlns:wsdl = "
http: // schemas . xmlsoap . org / wsdl / " xmlns:xsi = " http: // www . w3 . org
/2001/ XMLSchema - instance " xmlns:basic = " http: // www . icsy . de / types
/ basic / " xmlns = " http: // www . w3 . org /2001/ XMLSchema " >
< import namespace = " http: // www . icsy . de / types / basic / "
schemaLocation = " http: // www . icsy . de /~ venice / types / basic . xsd " / >
< simpleType name = " databaseName " >
< restriction base = " basic:String " >
< pattern value = " [a - z0 -9 _ ]* " / >
</ restriction >
</ simpleType >
< simpleType name = " transactionIsolation " >
< restriction base = " basic:String " >
< enumeration value = " none " / >
< enumeration value = " read_committed " / >
< enumeration value = " read_uncommitted " / >
< enumeration value = " repeatable_read " / >
< enumeration value = " serializeable " / >
</ restriction >
</ simpleType >
< complexType name = " PreparedValueRow " >
< sequence >
< element name = " int " type = " basic:IntArray " nillable = " true "
minOccurs = " 0" / >
< element name = " Byte " type = " basic:ByteArray " nillable = " true "
minOccurs = " 0" maxOccurs = " unbounded " / >
< element name = " string " type = " basic:StringArray " nillable = "
true " minOccurs = " 0 " / >
< element name = " double " type = " basic:DoubleArray " nillable = "
true " minOccurs = " 0 " / >
69
70
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
Anhang C. Datentypumwandlung
< element name = " float " type = " basic:FloatArray " nillable = " true "
minOccurs = " 0" / >
< element name = " time " type = " basic:StringArray " nillable = " true "
minOccurs = " 0" / >
< element name = " date " type = " basic:DateArray " nillable = " true "
minOccurs = " 0" / >
< element name = " timestamp " type = " basic:StringArray " nillable = "
true " minOccurs = " 0 " / >
< element name = " boolean " type = " basic:BooleanArray " nillable = "
true " minOccurs = " 0 " / >
< element name = " short " type = " basic:ShortArray " nillable = " true "
minOccurs = " 0" / >
< element name = " decimal " type = " basic:DecimalArray " nillable = "
true " minOccurs = " 0 " / >
< element name = " long " type = " basic:LongArray " nillable = " true "
minOccurs = " 0" / >
</ sequence >
</ complexType >
< element name = " PreparedValueRow " type = " tns:PreparedValueRow " / >
< simpleType name = " valueType " >
< restriction base = " basic:String " >
< enumeration value = " int " / >
< enumeration value = " byte " / >
< enumeration value = " string " / >
< enumeration value = " double " / >
< enumeration value = " float " / >
< enumeration value = " time " / >
< enumeration value = " date " / >
< enumeration value = " timestamp " / >
< enumeration value = " boolean " / >
< enumeration value = " short " / >
< enumeration value = " decimal " / >
< enumeration value = " long " / >
</ restriction >
</ simpleType >
< complexType name = " PreparedValues " >
< sequence >
< element name = " row " type = " tns:PreparedValueRow " minOccurs = " 0 "
maxOccurs = " 100 " / >
</ sequence >
</ complexType >
< element name = " PreparedValues " type = " tns:PreparedValues " / >
< complexType name = " SQLQuery " >
< sequence >
< element name = " dbname " type = " tns:databaseName " / >
< element name = " queryString " type = " basic:String " / >
< element name = " isolation " type = " tns:transactionIsolation "
nillable = " true " / >
< element name = " autoCommit " type = " boolean " nillable = " true " / >
< element name = " persistantId " type = " long " nillable = " true " / >
< element name = " persistentMode " type = " boolean " nillable = " true "
/>
< element name = " valueTypes " type = " tns:valueType " nillable = "
true " minOccurs = " 0 " maxOccurs = " 100 " / >
< element name = " values " type = " tns:PreparedValues " nillable = "
true " minOccurs = " 0 " / >
C.1. XML-Schema
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
71
</ sequence >
</ complexType >
< element name = " SQLQuery " type = " tns:SQLQuery " / >
< complexType name = " SQLRow " >
< sequence >
< element name = " int8 " type = " basic:LongArray " nillable = " true "
minOccurs = " 0" / >
< element name = " bit " type = " basic:ByteArray " nillable = " true "
minOccurs = " 0" / >
< element name = " varbit " type = " basic:ByteArray " nillable = " true "
minOccurs = " 0" maxOccurs = " unbounded " / >
< element name = " bool " type = " basic:BooleanArray " nillable = " true
" minOccurs = " 0 " / >
< element name = " bytea " type = " basic:ByteArray " nillable = " true "
minOccurs = " 0" maxOccurs = " unbounded " / >
< element name = " varchar " type = " basic:StringArray " nillable = "
true " minOccurs = " 0 " / >
< element name = " bpchar " type = " basic:StringArray " nillable = "
true " minOccurs = " 0 " / >
< element name = " date " type = " basic:DateArray " nillable = " true "
minOccurs = " 0" / >
< element name = " float8 " type = " basic:DoubleArray " nillable = "
true " minOccurs = " 0 " / >
< element name = " int4 " type = " basic:IntArray " nillable = " true "
minOccurs = " 0" / >
< element name = " numeric " type = " basic:DecimalArray " nillable = "
true " minOccurs = " 0 " / >
< element name = " float4 " type = " basic:FloatArray " nillable = " true
" minOccurs = " 0 " / >
< element name = " text " type = " basic:StringArray " nillable = " true "
minOccurs = " 0" / >
< element name = " time " type = " basic:StringArray " nillable = " true "
minOccurs = " 0" / >
< element name = " timestamp " type = " basic:StringArray " nillable = "
true " minOccurs = " 0 " / >
</ sequence >
</ complexType >
< element name = " SQLRow " type = " tns:SQLRow " / >
< complexType name = " SQLRows " >
< sequence >
< element name = " SQLRow " type = " tns:SQLRow " maxOccurs = " unbounded
"/>
</ sequence >
</ complexType >
< element name = " SQLRows " type = " tns:SQLRows "/ >
< simpleType name = " colType " >
< restriction base = " basic:String " >
< enumeration value = " int8 " / >
< enumeration value = " bit " / >
< enumeration value = " varbit " / >
< enumeration value = " bool " / >
< enumeration value = " bytea " / >
< enumeration value = " varchar " / >
< enumeration value = " bpchar " / >
< enumeration value = " date " / >
< enumeration value = " float8 " / >
72
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
Anhang C. Datentypumwandlung
< enumeration value = " int4 " / >
< enumeration value = " numeric " / >
< enumeration value = " float4 " / >
< enumeration value = " text " / >
< enumeration value = " time " / >
< enumeration value = " timestamp " / >
</ restriction >
</ simpleType >
< complexType name = " SQLValueSet " >
< sequence >
< element name = " colTypes " type = " tns:colType " maxOccurs = "
unbounded " / >
< element name = " SQLRows " type = " tns:SQLRows " nillable = " true " / >
< element name = " MessageAmount " type = " int " / >
< element name = " MessageNumber " type = " int " / >
< element name = " resultSetId " type = " long " nillable = " true " / >
< element name = " persistentId " type = " long " nillable = " true " / >
</ sequence >
</ complexType >
< element name = " SQLValueSet " type = " tns:SQLValueSet " / >
< complexType name = " SQLExecuteResult " >
< sequence >
< element name = " result " type = " int " / >
< element name = " persistentId " type = " long " nillable = " true " / >
</ sequence >
</ complexType >
< element name = " SQLExecuteResult " type = " tns:SQLExecuteResult " / >
< simpleType name = " right " >
< restriction base = " basic:String " >
< enumeration value = " read " / >
< enumeration value = " write " / >
< enumeration value = " none " / >
</ restriction >
</ simpleType >
< complexType name = " SQLPermission " >
< sequence >
< element name = " domain " type = " basic:String "/ >
< element name = " username " type = " basic:String " / >
< element name = " name " type = " basic:String " / >
< element name = " right " type = " tns:right " / >
</ sequence >
</ complexType >
< element name = " SQLPermission " type = " tns:SQLPermission " / >
< complexType name = " SQLPermissions " >
< sequence >
< element name = " database " type = " tns:databaseName " / >
< element name = " tables " type = " tns:SQLPermission " nillable = "
true " minOccurs = " 0 " maxOccurs = " unbounded " / >
</ sequence >
</ complexType >
< element name = " SQLPermissions " type = " tns:SQLPermissions " / >
</ schema >
Listing C.1: Vollständiges XML-Schema
C.2. Codebeispiele
73
C.2. Codebeispiele
Die zentralen Umwandlungsalgorithmen der Anwendung, um Datentypen von PostgreSQL nach SOAP/XML umzuwandeln oder von SOAP/XML nach Java.
C.2.1. Serverseite (TypeMapper)
Dieses gekürzte Beispiel zeigt die Umwandlung von
PostgreSQL
nach
SOAP .
Al-
lerdings wird nur der Datentyp int8 betrachtet (Zeile 31). Im Normalfall werden 14
weitere Datentypen verarbeitet.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public static Vector < SQLValueSet > convert ( ResultSet result ) throws
SQLException , SQLFault {
int types [] = getResultTypeArray ( result . getMetaData () ) ;
int counter = 0;
int col = 0;
SQLRow row = null ;
Vector < SQLValueSet > valueSets = new Vector < SQLValueSet >() ;
Vector < SQLRow > rowVector = new Vector < SQLRow >() ;
int size = 0;
// How many values of the specific datatype are used . Needed to
set the right index of the typearray .
int int8Count = -1;
// ...
size = SizeCalculator . getBaseValue ( types . length ) ;
try {
while ( result . next () ) {
rowVector . add ( new SQLRow () ) ;
row = rowVector . get ( counter ) ;
// calculate the filesize of the actual message
size += SizeCalculator . getRowValue ( types . length ) ;
// split the message if maxvalue is reached
if ( size >= MAX_FILESIZE ) {
valueSets . add ( createValueSetFromVector ( rowVector , types )) ;
rowVector = new Vector < SQLRow >() ;
size = SizeCalculator . getBaseValue ( types . length ) ;
counter = -1;
LOGGER . info ( " Message splitted . " ) ;
}
// for every col of the actual row
for ( int i =0; i < types . length ; i ++) {
col = i +1;
switch ( types [ i ]) {
case DBTYPE_INT8 :
if ( int8Count == -1 || int8Count >= getTypeAmount ( types ,
DBTYPE_INT8 ) ) {
int8Count = 0;
row . setInt8 ( new LongArray () ) ;
row . getInt8 () . setContents ( new long [ getTypeAmount ( types ,
DBTYPE_INT8 ) ]) ;
}
row . getInt8 () . setContents ( int8Count , result . getLong ( col ) )
;
int8Count ++;
74
Anhang C. Datentypumwandlung
break ;
// ...
default :
LOGGER . error ( " This type is not supported : " + result .
getMetaData () . getColumnTypeName ( i +1) ) ;
39
40
41
42
}
}
counter ++;
43
44
45
46
47
48
49
50
51
52
53
54
}
}
// create SQLValueSet - array
valueSets . add ( createValueSetFromVector ( rowVector , types ) ) ;
return valueSets ;
} catch ( Exception e ) {
LOGGER . debug ( SQLUtils . errorStackToString ( e ) ) ;
}
return null ;
Listing C.2: Auschnitte - TypeMapper (PostgreSQL -> SOAP/XML)
C.2.2. Clientseite (ResultSet)
Das folgende Listing zeigt die Verarbeitung von
fe der
ResultSet -Klasse
der
SOAP-JDBC-Bridge .
SQLValueSet -Objekten
mit Hil-
Man kann im Konstruktur (Zeile
1) und der Methode next (Zeile 27) erkennen, das die parallele Verarbeitung von
SQLValueSet-Objekten im Fall einer Aufteilung möglich ist. Auch hier wird nur der
Datentyp int8 betrachtet (Zeile 61).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public ResultSet ( final Connection con , final SQLValueSet values ) {
this . con = con ;
if ( values . getMessageAmount () > 0) {
messageAmount = values . getMessageAmount () ;
}
// if valueset was splitted start serveral threads to get other
parts
this . values = new SQLValueSet [ values . getMessageAmount () ];
this . values [0] = values ;
new Thread () { public void run () {
int priority = 4;
int maxValue = 5;
for ( int i =1; i < values . getMessageAmount () ; i ++) {
Thread t = new Thread ( new ResultSetWorker ( con , ResultSet . this
, values . getResultSetId () ) ) ;
priority = maxValue - ( int ) Math . ceil ( ( double ) i / ( values .
getMessageAmount () / 4) ) ;
if ( priority < 1) priority = 1;
t . setPriority ( priority ) ;
t . start () ;
try {
Thread . sleep ( NEXTSET_THREADSTARTTIME ) ;
} catch ( InterruptedException e ) {}
}}}. start () ;
this . row = values . getSQLRows () . getSQLRow () ;
this . colTypes = getIntDBTypes ( values . getColTypes () ) ;
C.2. Codebeispiele
24
25
}
75
buildParameterPositions () ;
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public boolean next () throws SQLException {
if ( row != null && actualMessage <= messageAmount ) {
// if there is another row
if ( cursor < row . length -1) {
cursor ++;
return true ;
}
// other parts available ?
else if ( actualMessage < messageAmount ) {
cursor = 0;
long timeWaited = System . currentTimeMillis () ;
// if next part not available , wait some time
while ( values [ actualMessage ] == null ) {
try {
if ( System . currentTimeMillis () - timeWaited >
NEXTSET_WAITINGTIME ) {
throw new SQLException ( " ResultSet timeout : can not
retrieve parts of the actual ResultSet ( " +
actualMessage + " ) . " ) ;
}
if ( retrievalError ) {
throw new SQLException ( retrievalErrorException ) ;
}
Thread . sleep ( NEXTSET_SLEEPTIME ) ;
} catch ( InterruptedException e ) { }
}
this . row = values [ actualMessage ]. getSQLRows () . getSQLRow () ;
if ( actualMessage > 1) values [ actualMessage -1] = null ;
actualMessage ++;
return true ;
} else return false ;
}
clean () ;
return false ;
}
59
60
61
62
63
64
65
66
67
68
69
70
71
// get an int value
public int getInt ( int parameterIndex ) throws SQLException {
int index = getIndex ( parameterIndex ) ;
switch ( colTypes [ parameterIndex -1]) {
case DBTYPE_INT8 :
return ( int ) row [ cursor ]. getInt8 () . getContents ( index ) ;
case DBTYPE_INT4 :
return row [ cursor ]. getInt4 () . getContents ( index ) ;
default :
throw new SQLException ( " Wrong get - method for datatype '" +
DBTYPES . get ( colTypes [ index ]) + " '. " ) ;
}
}
Listing C.3: Ausschnite - ResultSet (SOAP -> Java)
76
Anhang C. Datentypumwandlung
Abbildungsverzeichnis
2.1.
SOA - Schichtenmodell nach [Erl05] . . . . . . . . . . . . . . . . . . .
4
2.2.
Venice aus Provider-Perspektive [HGM] . . . . . . . . . . . . . . . . .
6
2.3.
Webservices - Aufbau und Kommunikation . . . . . . . . . . . . . . .
7
2.4.
Venice Service Compiler (VSC)
8
2.5.
JDBC - 2-Schichten-Architektur nach [Des03]
3.1.
Datenbank Webservice: Schematische Übersicht
3.2.
Erweiterter Pooling-Mechanismus
3.3.
Zusammenhang der Rechtegruppen
. . . . . . . . . . . . . . . . . . .
24
3.4.
Datenbank Webservice: Detaillierte schematische Übersicht . . . . . .
46
4.1.
Bibliothek: Architektur . . . . . . . . . . . . . . . . . . . . . . . . . .
47
4.2.
Bibliothek: Grasche Oberäche . . . . . . . . . . . . . . . . . . . . .
48
5.1.
Testfall: DynamicInsert - Laufzeit eines vollständigen Aufrufs . . . . .
53
5.2.
Testfall: NormalSelect - Laufzeit eines vollständigen Aufrufs
54
. . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . .
13
. . . . . . . . . . . .
18
. . . . . . . . . . . . . . . . . . . .
21
. . . . .
77
78
Abbildungsverzeichnis
Tabellenverzeichnis
2.1.
Java-XML-Mappings
. . . . . . . . . . . . . . . . . . . . . . . . . . .
10
3.1.
PostgreSQL-XML-Mappings . . . . . . . . . . . . . . . . . . . . . . .
32
3.2.
JDBC-Mapping nach [WFC
+ 99] . . . . . . . . . . . . . . . . . . . . .
33
3.3.
Dateigröÿe von SOAP-Nachrichten - Sichtproben mit Abweichung
3.4.
Operationen des Datenbankdienstes (DBControl)
3.5.
Funktionsumfang der SOAP-JDBC-Bridge
. .
34
. . . . . . . . . . .
36
. . . . . . . . . . . . . . .
41
5.1.
Testumgebung: Eingesetzte Ressourcen . . . . . . . . . . . . . . . . .
51
5.2.
Testwertvergleich
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
53
5.3.
Ergebnisse des Belastungstests . . . . . . . . . . . . . . . . . . . . . .
55
5.4.
Ergebnisvergleich
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
55
5.5.
SplittingSelect - Messung bei unterschiedlichen Maximaldateigröÿen .
56
79
80
Tabellenverzeichnis
Listings
2.1.
VSC - Operationen . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2.
SOAP-Nachricht
2.3.
XML-Schemadatei
9
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
11
3.1.
JDBC API - Beispiel . . . . . . . . . . . . . . . . . . . . . . . . . . .
19
3.2.
DBAccess-Objekt - Beispiel
22
3.3.
SQL-Syntax: CREATE TABLE
3.4.
Regulärer Ausdruck -
. . . .
27
3.5.
Tabelle - Rechteverwaltung . . . . . . . . . . . . . . . . . . . . . . . .
27
3.6.
SOAP-Datentyp für die Ergebnismengen
. . . . . . . . . . . . . . . .
31
3.7.
SQLValueSet Anwendungsbeispiel . . . . . . . . . . . . . . . . . . .
32
3.8.
Datentypen Clientseitige Übermittlung . . . . . . . . . . . . . . . .
33
3.9.
SOAP-Nachrichten: Formel zur Dateigröÿenberechnung . . . . . . . .
34
3.10. SQLQuery-Objekt
. . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . .
CREATE TABLE
mit Typrestriktionen
25
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
36
3.11. SQLPermission-Objekt . . . . . . . . . . . . . . . . . . . . . . . . . .
38
3.12. Nutzung des Dienstes (ohne Wrapperklassen)
. . . . . . . . . . . . .
41
. . . . . . . . . . . . . .
42
4.1.
Bibliotheksdienst - Auschnitt der VSC-Datei . . . . . . . . . . . . . .
47
4.2.
Bibliotheksdienst - Javaimplementierung
3.13. Nutzung des Dienstes (mit Wrapperklassen)
. . . . . . . . . . . . . . . .
48
A.2. DatabaseService.vsc . . . . . . . . . . . . . . . . . . . . . . . . . . . .
61
A.3. DatabaseService.wsdl . . . . . . . . . . . . . . . . . . . . . . . . . . .
61
A.4. Database-SOAP.wsdl . . . . . . . . . . . . . . . . . . . . . . . . . . .
62
A.5. Database.wsdl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
63
B.1. SQL-Syntax: SELECT - Problems . . . . . . . . . . . . . . . . . . . .
66
B.2. SQL-Syntax: CREATE TABLE - Problems . . . . . . . . . . . . . . .
66
C.1. Vollständiges XML-Schema
. . . . . . . . . . . . . . . . . . . . . . .
69
C.2. Auschnitte - TypeMapper (PostgreSQL -> SOAP/XML) . . . . . . .
73
C.3. Ausschnite - ResultSet (SOAP -> Java)
74
. . . . . . . . . . . . . . . .
81
82
Listings
Literaturverzeichnis
[AAM06]
[AKA
+ 05]
Best practices in web
service style, data binding and validation for use in data-centric scientic applications, September 2006.
Asif Akram, Rob Allan, and David Meredith,
M. Atkinson, K. Karasavvas, M. Antonioletti, R. Baxter, A. Borley,
N. Chue Hong, A. Hume, M. Jackson, A. Krause, S. Laws, N. Paton,
J. Schopf, T. Sugden, K. Tourlas, and P. Watson,
for OGSA-DAI,
A new architecture
AHM (2005).
[axi]
Webseite: Apache Axis, http://ws.apache.org/axis/.
[BCK03]
Len Bass, Paul Clements, and Rick Kazman,
Practice, 2nd Edidtion,
[BPSMM00]
[Des03]
Software Architecture in
Addison-Wesley, 2003.
T. Bray, J. Paoli, C. M. Sperberg-McQueen, and E. Maler, Extensible Markup Language (XML) 1.0 (Second Edition), Technical Report
REC-xml-20001006, 2000, http://www.w3.org/TR/REC-xml.
SOAP-JDBC: A Bridge Between Heterogeneous Clients and Data Sources, Master's thesis, College of Arts and Sciences
Swetha Desetty,
Georgia State University, 2003.
[DWD06]
Erdogan Dogdu, Yanchao Wang, and Swetha Desetty,
tabase Web Service,
[Erl05]
Thomas Erl,
Design,
[Fos03]
A Generic Da-
SWWS, pp. 117121, 2006.
Serive-Oriented Architecture - Conepts, Technology, and
Prentice Hall, 2005.
Ian T. Foster, The Grid: Computing Without Boundaries, 2003, http:
//www.sciam.com/.
[glo]
Webseite: Globus Toolkit, http://www.globus.org/toolkit/.
[Gon01]
Li Gong,
ment,
Industry Report: JXTA: A Network Programming Environ-
IEEE Internet Computing vol. 5 (2001), no. 3, pp. 88.
Java Bean - Dokumentation, http://java.sun.
com/javase/technologies/desktop/javabeans/docs/spec.html.
[Ham]
Graham Hamilton,
[HGM]
Markus Hillenbrand, Joachim Götze, and Paul Müller,
Lightweight Service Grid.
Venice A
83
84
Literaturverzeichnis
[HGZM07]
A
Lightweight Service Grid Based on Web Services and Peer-to-Peer,
Markus Hillenbrand, Joachim Götze, Ge Zhang, and Paul Müller,
KiVS, pp. 89100, 2007.
[HM01]
Wolfgang Hoschek and Gavin McCance,
tabase Middleware.
[Mel07]
Ingo Melzer,
Grid enabled Relational Da-
Service-orientierte Architekturen mit Web Services,
Spektrum - Akademischer Verlag, 2007.
[NSS03]
Developing Java Web Service - Architecting and Developing Secure Web
Services Using Java, WILEY, 2003.
Ramesh Nagappan, Robert Skoczylas, and Rima Patel Sriganesh,
Webseite: Organization for the Advancement of Structured Information Standards, http://www.oasis-open.org.
[oas]
[ogs08]
Fifth International Conference on Information Technology: New Generations (ITNG 2008), 7-8 April 2008, Las Vegas, Nevada, USA,
IEEE Computer Society, 2008.
[Rhe04]
[SYCFa]
[SYCFb]
[SYCFc]
Bamboo Website,
2004,
http://www.bamboo-dht.org.
Stefan Simkovics, A. Yu, J. Chen, and Zelaine Fong, PostgreSQL Webseite/Dokumentation, http://www.postgresql.org/.
PostgreSQL Webseite/Dokumentation - Create Table, http://www.postgresql.org/
docs/8.3/static/sql-createtable.html.
Stefan Simkovics, A. Yu, J. Chen, and Zelaine Fong,
Stefan Simkovics, A. Yu, J. Chen, and Zelaine Fong, PostgreSQL: XML-Funktionen, http://www.postgresql.com.cn/docs/8.3/
static/functions-xml.html.
Webseite:Apache Tomcat, http://tomcat.apache.org/.
[tom]
Webseite: Webservices - W3C-Standard, http://www.w3.org/2002/
ws/.
[w3c]
[WFC
Sean Rhea,
+ 99]
[wsi]
[ZTP03]
White, Fisher, Cattell, Hamilton, and Hapner, JDBC API Tutorial
and Reference, Second Edition - Universal Data Access for the Java
2 Platform, Addison-Wesley, 1999.
WS-I: Web Services Interoperability Organization, http://www.
ws-i.org/.
Perspectives
on Web Services - Applying SOAP, WSDL and UDDI to Real-World
Projects, Springer, 2003.
Olaf Zimmermann, Mark Tomlinson, and Stefan Peuser,
Herunterladen