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,