Vergleich von Objektpersistenzmechanismen für Java: Integration der Object Server Suite von Poet in Scone Björn Stephan Betreuer: Prof. Dr. W. Lamersdorf Universität Hamburg Fachbereich Informatik Vogt-Köll-Straße 30 22527 Hamburg 18. April 2002 ii Studienarbeit: Vergleich von Objektpersistenzmechnismen in Java: Integration der Object Server Suite von Poet in Scone Autor: Björn Stephan Morgensternsweg 5 22305 Hamburg E-Mail: [email protected] Betreuer: Prof. Dr. Winfried Lamersdorf Arbeitsgruppe Verteilte Systeme und Informationssysteme (VSIS) Dipl. Inform. Harald Weinreich Arbeitsgruppe Verteilte Systeme und Informationssysteme (VSIS) Universität Hamburg Fachbereich Informatik Vogt-Kölln-Straße 30 22527 Hamburg Inhaltsverzeichnis 1 Einleitung 1 2 Scone 2.1 Architektur . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2 Problemstellung . . . . . . . . . . . . . . . . . . . . . . . . . . 3 3 8 3 Grundlagen der Persistenz 3.1 Persistenzmechanismen . . . . . . . . . . . 3.2 Datenbankarchitekturen . . . . . . . . . . 3.3 Grundkonzepte von Datenbankensystemen 3.4 Das relationale Datenmodell . . . . . . . . 3.5 Das objektorientierte Datenmodell . . . . 3.6 Objektrelationale Datenbanken . . . . . . 3.7 Java Serialisierung . . . . . . . . . . . . . 3.8 EJB, JDO und XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 12 13 14 16 17 19 20 21 4 Integration der Objektdatenbank 4.1 Änderung der Paketstruktur . . . . . . 4.2 Persistenzfähige Klassen . . . . . . . . 4.3 Orphaned Objects . . . . . . . . . . . 4.4 Konzept der Schattenobjekte . . . . . . 4.4.1 Integration der Schattenobjekte 4.5 Konzept der Info-Objekte . . . . . . . 4.5.1 Integration der Info-Objekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 24 26 28 31 32 37 38 5 Resümee . . . . . . . . . . . . . . 41 iii iv INHALTSVERZEICHNIS Kapitel 1 Einleitung Sieht man sich heutzutage das World Wide Web an, so ist festzustellen, dass sich auf Seiten des Browsers seit den Tagen von Mosaic (s. Abb 1.1) nicht viel verändert hat: Einer wachsenden Informationsmenge steht ein Navigationsinterface gegenüber, dass nur geringfügigen Änderungen unterzogen wurde. Abbildung 1.1: Mosaic Web Browser (1994/1995) Vor diesem Hintergrund und des daraus enstehenden Lost In HyperspaceProblems, existieren zwar viele Lösungsansätze, aber jedes Projekt zur Verbesserung bzw. Erweiterung von Webbrowsern muss dabei von vorn anfangen: Es fehlt ein Fundament auf das Prototypen aufgesetzt werden können, 1 2 KAPITEL 1. EINLEITUNG ohne dass z.B. eigene Persistenzmechanismen implementiert werden müssen. Hier steht mit Scone eine Prototyping-Plattform zur Verfügung, die es ermöglicht Navigationsprototypen schnell umzusetzen. Erreicht wird dies durch ein Plugin-Konzept. Ein Navigationsprototyp, der mit Hilfe von Scone implementiert wird, wird als ein Scone-Plugin realisiert und kann auf alle Komponenten von Scone zu greifen. Scone wurde unter Leitung von Harald Weinreich [20] entwickelt und ist vollständig in Java implementiert. Das Konzept von Scone als Prototyping-Plattform hat sich als erfolgreich herausgestellt. Aktuelle Projekte sind u.a. Hyperscout [21] von Harald Weinreich oder BrowsingIcons [13] von Matthias Mayer. Weiterhin wurde Scone erfolgreich in einigen kleineren Projekten am Fachbereich Informatik der Universität Hamburg eingesetzt. Um die Navigation im WWW zu verbessern, wird die zur Verfügung stehende Information angereichert. Damit diese Information auch in einer späteren Sitzung wieder zur Verfügung steht, ist ein Persistenzmechanismus notwendig. Eine große Menge an gleich strukturierten Daten – wie die von Scone gesammelten Informationen – wird in aller Regel in Datenbanken gesichert. Aktuelle Datenbanksysteme lassen sich hauptsächlich in zwei Arten unterteilen: zum einem in die relationalen Datenbanken (RDBMS, Relational Database Management Systems), zum anderen in die objektorientierten Datenbanken (OODBMS, Object Oriented Database Management Systems). Thema dieser Studienarbeit ist der Persistenzmechanismus von Scone: Dieser basierte bislang auf der relationalen Datenbank MySQL [10] und wurde im Rahmen dieser Arbeit durch die objektorientierte Datenbank Object Sever ” Suite“ von Poet [15] ersetzt. Dabei fand eine komplett neue Implementierung des Persistenzmechanismuses statt, wobei die die vorhandene API soweit als möglich beibehalten oder gar vereinfacht wurde. Zunächst wird in Kapitel 2 die Architektur von Scone beschrieben. In Kapitel 3 werden wichtige Persistenzmechanismen für Java vorgestellt und verglichen. Kapitel 4 beschreibt dann die vollzogenen Schritte bei der Umstellung von Scone und geht auf dabei aufgetretene Probleme ein. Ein Resümee schließt die Arbeit ab. Kapitel 2 Scone Scone wird im Rahmen des Hyperscout-Pojektes [21] unter der Leitung von Harald Weinreich entwickelt. Scone ist eine Protyping-Plattform, die es ermöglicht verschiedenste Navigationsprototpen für das World Wide Web umzusetzen. Da das Thema dieser Studienarbeit der Persistenzmechanismus von Scone ist, wird in Abschnitt 2.1 die Architektur von Scone vorgestellt. Die fünf Komponenten von Scone werden jeweils kurz erläutert. Abschnitt 2.2 geht dann auf die Problemstellung der Arbeit ein. 2.1 Architektur Im folgenden stelle ich die Architektur (Abb. 2.1) von Scone vor. Die fünf Komponenten • Proxy: Paket scone.proxy, • Persistenz: Paket scone.netobjects, • Robot: Paket scone.robot, • Usertracking: Paket scone.accesstracking und • RAS - Remote Access Server: Paket scone.ras werden kurz beschrieben. 3 4 KAPITEL 2. SCONE Scone Proxy Generator Applets, Images... Browser Modified HTML/XML User Tracking Database Cache Web Events RAS Handler Robot Task Custom data Applet Abbildung 2.1: Scone-Architektur aus [21] Proxy Anstatt einen eigenen Proxy zu implementieren, wird der Proxy WBI [8, 2] (sprich: Webbi) von IBM Almaden verwendet. WBI steht für Web Based Intermediary. Da WBI nicht direkt den Anforderungen von Scone entsprach, wurde WBI in Kooperation mit IBM an Scone angepasst. WBI arbeitet mit dokumentenbasierten Regeln und basiert auf einer BytestromAuswertung. Die Möglichkeit eigene Plugins für WBI zu implementieren, machen WBI zu einem flexibel konfigurierbaren Proxy. Diese Plugins werden aus MEGs (Monitor Editor Generator) zusammengesetzt. Diese MEGs sind Java Klassen, die auch vom Pluginprogrammierer selbst implementiert werden können. WBI kennt folgende vier MEGs: • Request Editor: Ändere Anfragen • Generator: Erzeuge Rückgabe • Document Editor: Bearbeite Antwort • Monitor: Beobachte Übertragung 2.1. ARCHITEKTUR 5 Aufbauend auf dieser Architektur stellt Scone einen Standard-Tokenhandler zur Verfügung. Dieser Handler arbeitet auf einem Tokenstream, der gezielt Token für alle Html-Tags und Links berücksichtigt. Implementiert ist der Handler in der Klasse scone.proxy.ParsingMeg. Die Methode handleRequest der Klasse ParsingMeg analysiert WWW-Dokumente und extrahiert eine Reihe von Meta-Daten. Dies sind unter anderem: • Titel • Meta-Tags (Autor, Keywords, etc.) • Strukturinformationen (Linkliste, etc.) Die extrahierten Daten werden dann, über die Klassen des Paketes scone.netobjects persistent gemacht. Persistenz Abb. 2.2 zeigt das Entity-Relationship-Modell von Scone für die relationale Datenbank MySQL. Das Paket scone.netobjects stellt Objekte bereit, die tatsächliche Objekte aus dem Internet und dem WWW repräsentieren. Diese Objekte werden in der Scone-Datenbank persistiert. Abb. 4.1 auf Seite 25 zeigt die Klassenstruktur des Paketes scone.netobjects. Das Paket scone.netobjects persistiert Informationen über • Objekte des WWW, • Links (Link), • eingebundene Objekte (Inclusion), • Server (Server), • zur Benutzung (Access) und den Benutzern (User). Objekte des WWW werde unterteilt in Netzknoten (NetNode) und HTMLKnoten (HtmlNode). Ein Netzknoten repräsentiert beliebige Objekte im WWW. Ein HTML-Knoten ist ein Knoten, der explizit eine HTML-Seite repräsentiert. Die Klasse SimpleUri entspricht der Definition nach RFC 2396 [12] und repräsentiert Internetadressen. Ich stelle hier nun kurz vor, wie Netzknoten mit MySQL als Datenbank persistiert werden. Die Methode handleRequest der Klasse scone.proxy.ParsingMeg implementiert den Standard-Tokenhandler. Die 6 KAPITEL 2. SCONE LinkTable InclusionTable parentNodeId childNodeId tag n linkId n n fromNodeId n toNodeId fragment ... Node2KeyTable n nodeId keywordId NetNodeTable HtmlDocumentTable nodeId 0..1 1 AccessTable userId n nodeId keyword n 1 keywordId ServerTable 1 nodeId uri ... titel ... KeywordTable 1 1 host titel ... UserTable n n time ... userId personId ... PersonTable 1 personId firstName ... GroupTable Person2GroupTable 1 n 1 personId groupId groupId n Abbildung 2.2: ER-Diagramm von Scone Methode arbeitet auf einem Tokenstream, der von der Klasse SconePipe zur Verfügung gestellt wird. Die Klasse SconePipe befindet sich im Paket scone.util.tokenstram. Den Klassen der Form xxxCache kommt neben der Cache-Funktionalität noch eine spezielle Rolle zu: Die Cache Klassen kapseln die Datenbankanbindung, d.h. ein Instanz von NetNode wird nicht direkt instanziert sondern über die get-Methode von NetNodeCache. Mit NetNode node = NetNodeCache.get("http://www.informatik.unihamburg.de") wird ein Netzknoten angefordert. Die get-Methode von NetNodeCache durchsucht den Cache, ob der angeforderte NetNode bereits im Cache gespeichert ist. Ist dies nicht der Fall, so erzeugt NetNodeCache eine neue Instanz von NetNode und puffert diesen. Der neu erzeugte NetNode 2.1. ARCHITEKTUR 7 lädt, sofern er schon in der Datenbank existiert, seine Daten, anderenfalls erzeugt er einen neuen Datenbankeintrag. Dabei wird das Laden der Daten oder das Erzeugen eines neuen Datenbankeintrages von der Klasse DBTable übernommen, damit obliegt ihr die Konsistenzwahrung der Datenbank. Dadurch wird gewährleistet, dass jede Instanz von NetNode zu einem Datenbankeintrag korrespondiert und diese Korrespondenz nicht manuell erzeugt werden muss. Durch diesen Mechanismus braucht der Nutzer sich nicht mehr um datenbankspezifische Probleme wie Konsistenzwahrung etc. zu kümmern. Er kann mit persistenten Objekten umgehen wie mit transienten Objekten. Dieser Mechanismus wird auch als transparente Persistenz bezeichnet [18]. Die Klassen Link, HtmlNode, Server etc. werden in derselben Art wie NetNode gespeichert. Robot Der Robot von Scone wurde im Rahmen einer Studienarbeit von Frank Wollenweber entwickelt. Der Robot basiert konzeptionell auf WebSphinx (Specific Processors for HTML INformation eXtraction) von der Carnegie Mellon University [3, 14]. Konzipiert ist der Robot als ein WebAgent, der einen kleinen Ausschnitt des Web schnell erfassen kann und flexibel konfigurierbar ist. Er verfügt unter anderem über folgende Eigenschaften: • multi-threaded, • in Scone integriert, nutzt z.B. die Möglichkeiten des Parsing, StandardTokenhandlers etc., • einfache, aber mächtige API, • flexibel konfigurierbar über Filter- und Classifier-Modell. Für mehr Informationen über den Robot und seine Architektur verweise ich auf die Studienarbeit von Frank Wollenweber [22]. Usertracking Mit dem Usertracking kann das Navigationsverhalten eines Benutzers protokolliert werden, folgende Navigationsaktionen z.B. können aufgezeichnet werden: • Link angewählt, • Formular ausgefüllt, 8 KAPITEL 2. SCONE • Back-Button gedrückt. Die Protokollierung ermöglicht es angebotene Information an den Nutzer anzupassen. Das Usertracking unterstützt auch auch die Evaluation von SconePlugins. Evaluierbarkeit ist ein wichtiges Kriterium bei der Entwicklung von neuen Navigationswerkzeugen. RAS Das Remote Access System ermöglicht die Kommunikation zwischen verschiedenen Scone-Instanzen und mit Applets, die in Seiten eingebunden sind. Dieser Mechanismus wird beispielsweise in Hyperscout 2 eingesetzt. Durch die Möglichkeit mit Applets zu kommunizieren, werden die eingeschränkten Fähigkeiten von DHTML um die Möglichkeiten von Java Applets erweitert. Weitere Paktete Neben den vorgestellten fünf Komponenten von Scone, die durch jeweils ein Paket realisiert sind, gibt es noch drei weitere Pakete: • Das Paket scone stellt die Hauptklassen zum Starten von Scone zur Verfügung. • Das Paket scone.util stellt Hilfs- und Werkzeugklassen zur Konfiguration von Plugins und Scone, sowie zur Verarbeitung von Dokumenten zur Verfügung. • Das Paket scone.util.tokenstream stellt die Tokenstreams für die Tokenhandler zur Verfügung. 2.2 Problemstellung Ausgangspunkt dieser Studienarbeit sind zwei Beobachtungen: • Die eingesetzte relationale Datenbank ermöglicht keine komplexen Abfragen auf Objektstrukturen. • Der Paradigmenbruch zwischen eingesetztem Persistenzmechanismus und Programmiersprache sollte vermieden werden. Letzteres wird als Impedance Mismatch bezeichnet und genauer in [16] beschrieben: 2.2. PROBLEMSTELLUNG 9 Damit ist die Kluft zwischen der deklarativen, mengenorientier” ten Anfragesprache SQL eines RDBMS und dem prozeduralen, satzorientierten Paradigma der in der Regel zur Implementierung von Anwendungen genutzten Programmiersprachen gemeint, die durch unterschiedliche Typsysteme noch vergrößert wird.“ In SQL existiert nur ein sehr begrenzter Satz an vordefinierten Typen und es ist nicht möglich eigene Typen zu definieren. Die Kluft zwischen den Paradigmen muss also vom Entwickler selbst überbrückt werden. Ziel dieser Studienarbeit war es einen Persistenzmechanismus zu finden, der komplexe Abfragen ermöglicht und den Impedance Mismatch vermeidet. Es wurden mehrere Persistenzmechanismen für Java untersucht, von denen dann einer ausgewählt und integriert wurde. 10 KAPITEL 2. SCONE Kapitel 3 Grundlagen der Persistenz In [7] wird Persistenz als Bestehenbleiben eines Zustandes über einen länge” ren Zeitraum“ definiert. Wenn im folgenden von Persistenz die Rede ist, so ist darunter genauer zu verstehen: Persistenz ist ein Mechanismus, der es erlaubt, die Lebenszeit von Daten über die Blockstruktur eines Programms und Gültigkeitsbereiche von Variablen hinaus zu verlängern. Hiermit verbunden ist auch die Sicherheit, die Daten auch bei Ausfall von Software und Hardware zu erhalten. In einem persistenten System werden Daten also über das Programmende hinaus gespeichert. Sie stehen damit bei einem erneuten Start des Programms wieder zur Verfügung. Zudem fungiert ein Persistenzmechanismus bei großen Datenmengen als Erweiterung des knappen Arbeitsspeichers. Mit großen Datenmengen sind hier Daten im Giga- oder sogar im Terabyte Bereich gemeint. In Abschnitt 3.1 wird eine Unterteilung von Persistenzmechanismen in drei Kategorien vorgenommen. In Abschnitt 3.2 werden zwei Datenbankarchitekturen vorgestellt. Abschnitt 3.3 geht dann auf die grundlegenden Konzepte von Datenbanksystemen ein. Danach wird in den Abschnitten 3.4, 3.5 und 3.6 nacheinander das relationale, objektorientierte und objektrelationale Datenmodell erläutert. In Abschnitt 3.7 wird die Java eigene Serialisierung vorgestellt und im letzten Abschnitt 3.8 werden EJB, JDO und XML als Persistenzmechanismen kurz erläutert. 11 12 KAPITEL 3. GRUNDLAGEN DER PERSISTENZ 3.1 Persistenzmechanismen Es stehen mehrere Mechanismen zur Verfügung um Daten persistent zu machen. Für Java lassen sich die zur Verfügung stehenden Persistenzmechanismen grob in drei Kategorien unterteilen und zwar Dateien, Datenbanken und orthogonal persistente Systeme. Dateien Die Speicherung in Dateien lässt sich weiter in • eigene Formate, • oder standardisierte Formate, z.B. GIF, MP3 unterteilen. Eine Mischform stellt die Neuentwicklung XML dar.In diese Kategorie fällt auch die Java eigene Serialisierung. Sie wird kurz in Abschnitt 3.7 vorgestellt. Datenbanken Datenbanken unterscheidet man nach dem eingesetzten Datenmodell in • Netzwerk-Datenbanken, • hierarchische Datenbanken1 , • relationale Datenbanken, • objektorientierte Datenbanken oder • objektrelationale Datenbanken. Orthogonal persistente Systeme Orthogonal persistente Systeme sind Systeme, die die Anforderungen der orthogonalen Persistenz erfüllen. Orthogonalen Persistenz ist wie folgt definiert (nach [16]): • Typ-Orthogonalität: Persistenz ist für alle Objekte der Programmiersprache verfügbar. • Transitive Persistenz : Alle Objekte, die von einem persistenten Wurzelobjekt erreichbar sind, werden ebenfalls persistiert. • Persistenz-Unabhängigkeit: Es ist irrelevant, ob Programmcode mit persistenten oder transienten Objekten arbeitet. Der Code für beide Fälle ist identisch. 1 Die Netzwerk- und Hierarchischen Datenbanken haben nur noch historische Bedeutung, deshalb werden sie hier nicht näher besprochen. Bei näherem Interesse verweise ich auf das Buch von P.C. Lockemann und J.W. Schmidt [11]. 13 3.2. DATENBANKARCHITEKTUREN Benutzerschnittstelle Benutzerschnittstelle Anwendungslogik Client Corba, RMI, JDO, EJB, ... Client Anwendungslogik Applikations− server DB−Schnittstelle DB−Schnittstelle DBMS−Protokoll DBMS−Protokoll DB−Server a 2-Schichten-Architektur DB−Server b 3-Schichten-Architektur Abbildung 3.1: Client-Server-Architekturen aus [16] Ein Beispiel für ein orthogonal persistentes System ist das von M. Atkinson et. al. (University of Glasgow) und M. Jordon et. al. (Sun Microsystems) implementierte PJama-System [6, 16]. 3.2 Datenbankarchitekturen Die meisten modernen Datenbankanwendungen sind als Client-ServerArchitekturen realisiert. Dies bietet unter anderem die Möglichkeit, Daten mehreren Clients zur Verfügung zu stellen und Datenredundanz zu vermeiden. Bei einer Client-Server-Architektur spielt dann noch die Frage eine Rolle, wie eng Anwendungsprogramm und Speicherung gekoppelt sein sollen. Hier kann man sich entweder für eine 2-Schichten-Architektur (Abb. 3.1a) oder eine 3-Schichten-Architektur (Abb. 3.1b) entscheiden. Jeder der in Abschnitt 3.1 vorgestellten Persistenzmechanismen läßt sich in beiden Architekturen einsetzen. Im Falle von Dateien wird allerdings zumeist kein Datenbankserver eingesetzt; dies ist aber ohne weiteres möglich. Soll das Anwendungsprogramm eng mit dem Persistenzmechanismus ge- 14 KAPITEL 3. GRUNDLAGEN DER PERSISTENZ koppelt sein, so wird man eine 2-Schichten-Architektur wählen. Eine 2Schichten-Architektur hat den Vorteil, dass sie vergleichsweise einfacher zu entwickeln ist als eine 3-Schichten-Architektur. In Scone wird die 2-SchichtenArchitektur eingesetzt. Die 3-Schichten-Architektur hat den Vorteil, dass die komplette Anwendungslogik im Applikationsserver implementiert ist. Dadurch wird unter anderem die Wartungs- und Installationskomplexität erheblich reduziert. 3.3 Grundkonzepte von Datenbankensystemen Ein wesentlicher Aspekt, der für den Einsatz von Datenbanksystemen spricht, ist die von ihnen umgesetzte Datenunabhängigkeit. Durch die Datenunabhängkeit werden Anwendungsprogramm und Datenbank voneinander entkoppelt. Das hat den Vorteil, dass Anwendungsprogramm und Datenbank unabhängig voneinander Änderungen unterzogen werden können. Mit Datenbanksystemen hängt weiterhin noch der Begriff der Transaktion zusammen. Transaktionen werden aber nicht von allen Datenbanksystemen angeboten. Datenunabhängigkeit Das Konzept der Datenunabhängigkeit läßt sich nach [9, 16] in zwei Aspekte aufteilen: • Physische Datenunabhängigkeit: Die physische Datenunabhängigkeit entkoppelt die Speicherung der Daten von der logischen Sicht auf die Daten. Somit ist es möglich Optimierungen an der physischen Basis der Daten unabhängig vom Datenbankschema durchzuführen. Die Änderungen haben damit nur Einfluß auf die Effizienz der Datenbank. • Logische Datenunabhängigkeit: Die logische Datenunabhängigkeit koppelt die Datenbank von den Anwendungsprogrammen ab. Dadurch bleibt das Datenbankschema von Änderungen der Anwendungsschnittstellen unberührt. Diese zwei Ebenen der Datenunabhängigkeit werden durch das Konzept der Datenabstraktion gewährleistet. Datenabstraktion Nach [9, 16] werden in einem Datenbanksystem drei Abstraktionsebenen unterschieden (s. Abb. 3.2): 3.3. GRUNDKONZEPTE VON DATENBANKENSYSTEMEN 15 • Die physische Ebene: Hier wird festgelegt, wie die Daten gespeichert werden. Dies ist im Normalfall der Hintergrundspeicher, der in der Regel als Festplattenspeicher realisiert ist. • Die logische Ebene: Auf dieser Ebene wird festgelegt, welche Daten abgespeichert sind. Das hier beschriebene integrierte Modell der gesamten Informationsmenge wird auch Datenbankschema genannt. • Die Sichten: Während das Datenbankschema der logischen Ebene die gesamte Informationsmenge enthält, werden in den Sichten Teilmengen dieser Informationsmenge bereitgestellt. Diese Sichten sind auf die Bedürfnisse der jeweiligen Benutzer bzw. Benutzergruppen zugeschnitten. Sicht 1 Sicht 2 Sicht n logische Ebene physische Ebene Abbildung 3.2: Drei Abstraktionsebenen eines Datenbanksystems aus [9] Transaktionen Einen Transaktion ist eine Folge von Operationen auf einer Datenbank, die die Datenbank von einem konsistenten Zustand in einen neuen konsistenten Zustand überführt. Um dies zu gewährleisten, ist die Einhaltung der ACID-Eigenschaften für eine Transaktion wichtig. Der Name ACID steht dabei für die vier Prinzipien, die von Transaktionen gefordert werden: 16 KAPITEL 3. GRUNDLAGEN DER PERSISTENZ • Atomicity Ein Transaktion ist atomar d.h. sie wird entweder ganz oder gar nicht ausgeführt. • Consistency Eine Transaktion gewährleistet, dass sie nach ihrer Ausführung die Datenbank in einem konsistentem Zustand hinterläßt. Die Datenbank wird in den Zustand vor Ausführung der Transaktion zurückgesetzt, wenn die Überführung in einen konsistenten Zustand nicht gewährleistet werden kann. • Isolation Das Ergebnis einer Transaktion muss einer isolierten Ausführung der Transaktion entsprechen, d.h. als ob sie alleine ausgeführt wurde, unabhängig davon ob mehrere Transaktionen parallel oder verschränkt laufen. Dies wird durch Serialisierbarkeit erreicht. • Durability Ein Transaktion garantiert, dass die in der Datenbank neu abgelegten oder geänderten Daten dauerhaft gespeichert sind, d.h. die Daten sind nicht an die Lebensdauer von Variablen oder Blöcken gebunden. Die Daten werden zumindestens in den Sekundärspeicher geschrieben. 3.4 Das relationale Datenmodell Relationale Systeme basieren auf der von Codd in [4] entwickelten Theorie, die the relational model of data“ genannt wird. Drei wichtige Aspekte lassen ” sich nach [5] an dieser Theorie festmachen und zwar: • Struktureller Aspekt: Daten werden in Tabellen abgelegt. Der Nutzer einer Datenbank nimmt ihren Datenbestand als eine Sammlung von Tabellen wahr. • Integritätsaspekt: Die Tabellen einer Datenbank erfüllen bestimmte Integritätseinschränkungen. • manipulativer Aspekt: Die Operatoren die dem Nutzer zur Verfügung stehen, um die Tabellen einer Datenbank zu manipulieren, sind ausschließlich Operatoren die Tabellen aus Tabellen generieren. Von diesen Operatoren sind drei besonders wichtig, dies sind restrict, project 3.5. DAS OBJEKTORIENTIERTE DATENMODELL 17 und join. Da das Ergebnis einer Manipulation wieder eine Tabelle ist, lassen sich Operationen beliebig tief schachteln. Die Speicherung von Daten in Tabellen und die mengenorientierten Verarbeitung hat diesen Systemen zum Durchbruch verholfen. Im Gegensatz zur mengenorientierten Verarbeitung von Daten, wurden die Daten im Netzwerkmodell und hierarchischen Modell satzorientiert verarbeitet. Hinter einer Tabelle steht der mathematische Begriff der Relation, der diesen Systemen auch ihren Namen gegeben hat. Die mathematische Untermauerung ermöglicht es, Normalisierungen der Daten durchzuführen, um Redundanz und damit Mehrfachspeicherung zu vermeiden. Die Anbindung von relationalen Datenbanken an eine Programmiersprache geschieht in aller Regel über eine standardisierte Schnittstelle. Die Schnittstelle für Java heißt JDBC (Java Database Connectivity). 3.5 Das objektorientierte Datenmodell Das objektorientierte Datenmodell basiert auf Konzepten aus der Welt objektorientierter Programmiersprachen. Das zentrale Konzept des objektorientierten Datenmodells ist, wie bei den objektorientierten Programmiersprachen, der Begriff des Objektes. Ein Objekt integriert die strukturelle Repräsentation mit der operationalen Komponente in einem Objekttyp (auch Klasse genannt). Ein Objekttyp spezifiziert also strukturell und operational ähnliche Objekte. Der Standard für das objektorientierte Datenmodell ist der ODMG 3.0 (Object Database Management Group) Standard. Dieser Standard definiert ein Objektmodell und die Anbindung an verschiedene Programmiersprachen. Der ODMG 3.0 Standard definiert Anbindungen an die Sprachen C++, Smalltalk und Java. Der Standard definiert auch eine deklarative Anfragesprache, die in Anlehnung an SQL, OQL (Object Query Language) heißt. Der ODMG 3.0 Standard schreibt Persistenz durch Erreichbarkeit vor, d.h. alle von einem Objekt direkt oder indirekt referenzierten Objekte werden zusammen mit diesem persistiert. Der Standard sieht auch sogenannte Wurzelobjekte vor, dies sind Objekte, die beim Persistieren einen Namen bekommen. Unter diesem Namen findet man sie in der Datenbank wieder; damit fungieren sie als Einstiegspunkte für die Objektnavigation, d.h. ausgehend von einem Wurzelobjekt erreicht man alle zu einem Objektnetz gehörenden Objekte. Das Wiederfinden von Objekten ist aber auch über die Extension 18 KAPITEL 3. GRUNDLAGEN DER PERSISTENZ einer Klasse – damit ist die Menge aller Instanzen einer Klasse gemeint – möglich. Der Standard sieht Extensionen (noch) nicht vor [18]. Eigenschaften von Objekten Nach [9] hat ein Objekt im objektorientierten Modell drei Bestandteile: • Typ: Die Struktur und das Verhalten werden durch den Objekttyp, also die Klasse, festgelegt. Indivuelle Objekte werden durch Instanziierung des Objekttyps erzeugt. • Identität: Jedes Objekt hat eine eindeutige Objektidentität, die sich während seiner Lebenszeit nicht ändert. • Wert bzw. Zustand : Zu jedem Zeitpunkt seiner Lebenszeit hat ein Objekt einen bestimmten Zustand (auch Wert genannt). Der Zustand eines Objektes ist durch die Werte seiner Attribute und durch die Beziehungen zu anderen Objekten gegeben. Objektidentität Ein besonders wichtiger Aspekt ist die Identität von Objekten. Die Objektidentität ermöglicht es ein Objekt genau zu identifizieren und zwar unabhängig vom Zustand des Objektes und seinem Aufenthalts” ort“. Im Gegensatz zum relationalen Modell, wo Tupel über ihre Schlüsselattribute identifiziert werden, werden Objekte in objektorientierten Programmiersprachen durch Zeiger referenziert. Ein Objekt wird also durch seine Adresse im Speicher identifiziert. Diese Methode der Objektidentifizierung kann aber für persistente Objekte aus folgenden Gründen nicht benutzt werden [9]: • Während seiner Lebensdauer, kann ein Objekt nicht verschoben werden. Dies ist bei transienten Hauptspeicherobjekten kein so großes Problem wie im Datenbankbereich, da man es hier mit persistenten Objekten zu tun hat. • Weiterhin kann nicht sichergestellt werden, daß alle Referenzen auf ein gelöschtes Objekt ihre Gültigkeit verlieren. Der ehemalige Speicherort könnte unbemerkt durch neue, andere Objekte belegt werden. Dieses Problem wird in objektorientierten Datenbanken durch Objektidentifikatoren(OIDs) gelöst. Diese OIDs sind zustands- und speicherungsortunabhängig und werden vom Datenbanksystem systemweit eindeutig generiert, sobald ein neues persistentes Objekt erzeugt wird. Sie stehen dann zur 3.6. OBJEKTRELATIONALE DATENBANKEN 19 Referenzierung der Objekte zur Verfügung. Die OIDs bleiben während der ganzen Lebenszeit eines Objektes invariant. Weiterhin sorgt das Datenbanksystem dafür, daß ein OID nur einmal generiert wird, d.h. der OID eines gelöschten Objektes wird nie wieder verwendet. 3.6 Objektrelationale Datenbanken Für objektrelationale Datenbanken existiert noch kein einheitliches Datenmodell. Objektrelationale Datenbanken basieren auf der funktionalen Erweiterung des relationalen Datenmodells. Hierbei werden objektorientierte Konzepte in das relationale Datenmodell integriert. Die meisten führenden Hersteller von relationalen Datenbanken bieten heute schon einige der objektrelationalen Konzepte in ihren Produkten an, zumeist aber in uneinheitlicher Form. Die Erweiterung des relationalen Datenmodells beziehen sich hauptsächlich auf folgende Aspekte (nach [9]): Mengenwertige Attribute Es wird die flache Struktur des relationalen Modells aufgegeben. Es wird also ermöglicht das ein Tupel nicht nur aus atomaren Entitäten bestehen kann, d.h. die erste Normalform des relationalen Modells wird aufgegeben2 . Typdeklarationen Wie im objektorientierten Datenmodell ist es möglich eigene Typen zu definieren. Dadurch ist man nicht mehr an den begrenzten Satz von vordefinierten SQL-Attributtypen gebunden. Referenzen Es werden wie im objektorientierten Datenmodell Referenzen eingeführt. Fremdschlüssel zur Modellierung von Beziehungen sind damit unnötig. Die Einführung von Referenzen führt automatisch zum nächsten Punkt. Objektidentität Durch die Einführung von Referenzen wird die Einführung von Objektidentität notwendig. Denn Referenzen sind nur dann sinnvoll, wenn Objekte (Tupel) eindeutig identifiziert werden können. 2 Dieses Modell wird auch geschachteltes relationales Modell oder kurz NF2 (Non First Normal Form) genannt. 20 KAPITEL 3. GRUNDLAGEN DER PERSISTENZ Pfadausdrücke Die Unterstützung von Pfadausdrücken in der Anfragesprache wird durch die Einführung von Referenzen notwendig. Denn welchen Vorteil hat man durch die Einführung von Referenzen, wenn diese nicht in der Anfragesprache genutzt werden. Vererbung Durch die Einführung von Vererbung ist es möglich eine Relation als einen Untertyp von einer oder mehreren Relationen zu definieren. Operationen Im objektorientierten Datenmodell ist es möglich die den Daten zugeordneten Operationen (Methoden) in der Anfragesprache zu nutzen. Das bedeutet, dass in der OQL die Methoden von Objekten ausgeführt werden können, da diese im Datenbankschema ebenfalls persistiert werden. D.h. man kann die volle Funktionalität der Objekte in einer anderen Anwendung nutzen, ohne die auf einen Objekt möglichen Operationen neu implementieren zu müssen. Dies ist im relationalen Modell nicht möglich. 3.7 Java Serialisierung Das Paket java.io stellt grundlegende Klassen für Dateioperationen zur Verfü-gung. Mit Hilfe dieser Klassen ist es möglich von einer beliebigen Quelle zu lesen und in eine beliebige Senke zu schreiben. Vor allem die beiden Klassen java.io.ObjectOutputStream und java.io.ObjectInputStream sind interessant. Mit ihnen ist das Serialisieren beliebiger Objektgraphen in und aus Dateien möglich. Eine Klasse wird serialisierbar, wenn sie das Interface java.io.Serializable implementiert. Dies ist mit wenig Aufwand verbunden, da das Interface keine Methoden hat, also nur als Indikator dient. Zu beachten ist nur, das alle referenzierten Klassen auch serialisierbar sein müssen. Die Serialisierung wurde mit dem Ziel entwickelt, entfernte Methodenaufrufe (RMI) zu unterstützen [16]. Dieser einfache Mechanismus erlaubt es Daten schnell persistent zu machen. Durch die enge Integration der Serialisierung in die Sprache Java wird der Impedance Mismatch vermieden. Die Serialisierung unterstützt aber • keine Abfragen der persistenten Daten, • keinen Mehrbenutzerzugriff und • die Datenunabhängigkeit ist ebensowenig gegeben. 3.8. EJB, JDO UND XML 21 Aus diesen Gründen eignet sich Serialisierung nicht dafür, den Persistenzmechanismus in Scone zu ersetzen. 3.8 EJB, JDO und XML Die hier vorgestellten Persistenzmechanismen sind insofern nicht in die Unterteilung von Kapitel 3.1 aufgenommen, da sie in der Mittelschicht von 3-Schicht-Architekturen (s.Abb. 3.1b) eingesetzt werden. Damit kapseln sie den zugrundeliegenden Persistenzmechanismus. EJB (Enterprise Java Beans) ist eine von Sun entwickelte Komponententechnologie für den serverseitigen Einsatz [1]. Mit EJB entwickelt man hauptsächlich Applikationsserver für eine 3-Schichten-Architektur. EJB bietet eine Persistenzschnittstelle an, die auf einem der in Abschnitt 3.1 beschriebenen Persistenzmechanismen aufsetzt. EJB ist also nur scheinbar ein neuer Persistenzmechanismus, tatsächlich aber kapselt EJB lediglich den zugrundeliegenden Persistenzmechanismus und bietet eine einheitlich Schnittstelle an. In den meisten Fällen erledigt dann auch eine relationale Datenbank die eigentliche Arbeit. EJB reduziert den Entwicklungsaufwand für Applikationsserver erheblich. JDO (Java Data Objects) beschreibt wie Java Objekte gespeichert werde sollen [17]. JDO stellt eine einheitliche Schnittstelle für jede Art von Persistenzmechanismus zur Verfügung. Somit ist JDO kein eigenständiger Persistenzmechanismus, sondern kapselt wiederum nur den zugrunde liegenden Persistenzmechanismus. Durch JDO ist es möglich beliebige Persistenzmechanismen einzusetzen, ohne sich z.B. um Objektmapping kümmern zu müssen. Eine weitere Möglichkeit Java Objekte zu speichern ist XML (eXtensible Markup Language). Wenn man Objekte in XML speichert, ist damit aber noch nicht geklärt wie die XML-Daten tatsächlich gespeichert werden. Die in XML codierten Objekte werden dann entweder in Dateien oder Datenbanken abgelegt, wobei sich für die dauerhafte Persistierung nach [19] objektorientierte Datenbanken anbieten. Alle hier beschriebenen Ansätze sind Mechanismen, die vom eingesetzten Persistenzmechanismus abstrahieren. Sie kapseln die Persistenz vor dem Benutzer, damit dieser sich nicht mit Konsistenzwahrung, Integritätseinschränkungen etc. beschäftigen muss. Alle hier beschriebenen Mechanismen speichern ihre Daten entweder in Dateien oder in Datenbanken. Orthogonal persistente 22 KAPITEL 3. GRUNDLAGEN DER PERSISTENZ Systeme als dritte Möglichkeit werden zur Zeit noch hauptsächlich im Forschungsbereich eingesetzt. Alle hier beschriebenen Persistenzmechanismen erfordern die Implementierung eines Applikationsservers. Ein Applikationsserver entspricht aber nicht den in Abschnitt 2.2 auf Seite 8 beschriebenen Anforderungen, somit wurde keiner dieser Mechanismen ausgewählt um den Persistenzmechanismus von Scone zu ersetzen. Kapitel 4 Integration der Objektdatenbank Die in Abschnitt 2.2 auf Seite 8 dargelegte Problemstellung und die in Kapitel 3 auf Seite 11 beschriebenen Sachverhalte legten nahe, eine objektorientierte Datenbank zur Problemlösung einzusetzen. Das objektorientierte Datenbanksystem Object Server Suite (OSS)1 von Poet wurde ausgewählt um die relationale Datenbank MySQL zu ersetzen. Durch die Wahl einer objektorientierten Datenbank werden die Forderungen (s. Abschnitt 2.2 auf Seite 8) nach Vermeidung des Paradigmenbruchs und nach komplexeren Anfragen erfüllt. Aufgabe war es nun die relationale Datenbank MySQL durch die objektorientierte Datenbank von Poet zu ersetzen. Die Architektur von Scone wurde bereits im Kapitel 2 auf Seite 3 beschrieben, hier werde ich nun auf die vollzogenen Änderungen an Scone eingehen. Das Paket scone.netobjects stellt den Persistenzmechanismus zur Verfügung. Auf dieser bereits vorhandenen Paketstruktur setzte ich auf und modifizierte diese, dabei wurde der Persistenzmechanismus komplett neu implementiert. Die API des Paketes scone.netobjects wurde dabei soweit möglich erhalten. Zunächst werden in Abschnitt 4.1 die Änderungen an der Paketstruktur scone.netobjects vorgestellt. In Abschnitt 4.2 wird erläutert wie Klassen mit der OSS von Poet gespeichert werden, danach werden in Abschnitt 4.3 die orphaned Objects von Poet erklärt. In Abschnitt 4.4 und 4.5 werden dann 1 In Scone kommt OSS 6.1 zum Einsatz, die neuere Version hat in der Zwischenzeit ihren Namen geändert und heißt jetzt FastObjects t7 by Poet. 23 24 KAPITEL 4. INTEGRATION DER OBJEKTDATENBANK abschließend die Konzepte der Schatten- und Info-Objekte vorgestellt. Diese Konzepte lösten eine Reihe von Problemen, die bei der Modifikation von Scone auftraten. Die Implementierung der beiden Konzepte wird in den Abschnitten 4.4.1 und 4.5.1 erläutert. 4.1 Änderung der Paketstruktur Als erstes wurde die Paketstruktur scone.netobjects (s. Abb. 4.1) von den Klassen befreit, die durch den Wechsel des Persistenzparadigmas überflüssig wurden. Durch die Vermeidung des Paradigmenbruchs ergibt sich sofort der Wegfall der Klassen • TableRecord, • DBTableAdapter und • FieldValueHashTable. Diese drei Klassen sind für das Objektmapping der zu persistierenden Objekte auf relationale Tabellen zuständig. Da Objekte in objektorientierten Datenbanken direkt gespeichert werden, ist die Transformation von Objekten in tabellentaugliche Daten und zurück nicht mehr nötig. Eine weitere Vereinfachung der Paketstruktur scone.netobjects ergibt sich aus der Definition von Klassen. Eine Klasse vereinigt die strukturelle Repräsentation (Eigenschaften von Objekten) und die verhaltensmäßigen Komponenten (Methoden von Objekten). Eine Klasse spezifiziert also strukturell und verhaltensmäßig ähnliche Objekte. Aufgrund dieser Definition entschied ich mich, die Datenbankanbindung in die zu persistierenden Objekte zu verlagern. Vorher war die Datenbankanbindung in den Klassen • HtmlNodeCache, • NetNodeCache, • LinkCache, • etc. umgesetzt (vgl. Abschnitt 2.1). Durch die Verlagerung der Datenbankanbindung wurden die zu persistierenden Klassen befähigt, sich selbst zu speichern 25 4.1. ÄNDERUNG DER PAKETSTRUKTUR Dictionary HtmlTagToken Hashtable FieldValueHashTable LinkToken Frame AccessCache Observable HtmlNodeCache Cache DBTableAdapter NetNodeCache AccessEvent Access CacheTable HtmlNode CacheTable$Entry Inclusion SimpleUri Keyword Link NetNode Object TableRecord Node2Key PersonCache LinkCache Person Person2Group InclusionCache PGroup KeywordCache Server ServerCache User Person2GroupCache Legende aus externem Paket PGroupCache A Node2KeyCache B B erweitert A CacheTable$Cleaner x$y Thread Cache$Notifier y ist innere Klasse von x UserCache Vector LinkVector Abbildung 4.1: Klassendiagramm mit MySQL als Datenbank persistenz− fähige Klassen 26 KAPITEL 4. INTEGRATION DER OBJEKTDATENBANK und aus der Datenbank zu lesen. Aufgrund der Entfernung der Datenbankanbindung aus den Klassen der Form xxxCaches verblieb nur die Cachefunktionalität in diesen Klassen. Da POET einen Caching-Mechanismus zur Verfügung stellt entfernte ich auch alle Cacheklassen. Abb. 4.2 zeigt die resultierende Paketstruktur. HtmlTagToken LinkToken Inclusion NetNode NetNodeInfo Object Link HtmlInfo Legende aus externem Paket Server A B B erweitert A Frame SimpleUri persistenz− fähige Klassen Abbildung 4.2: Klassendiagramm mit OSS 6.1 als Datenbank 4.2 Persistenzfähige Klassen Bevor persistente Objekte zur Verfügung stehen, muss dem Datenbanksystem zunächst einmal bekannt gemacht werden, welche Klassen überhaupt persistenzfähig sind. Das Datenbankschema ergibt sich im objektorientierten Paradigma dann direkt aus der Klassenstruktur. Es kann also direkt aus scone.netobjects gewonnen werden (Abb. 4.2) und muss nicht wie im relationalen Fall getrennt erzeugt werden (s. ER-Diagramm in Abb. 2.2 auf Seite 6). Der ODMG-Standard sieht vor, dass Klassen von außen als persistenzfähig deklariert werde sollen [18]. Klassen werden also nicht mit Mitteln der Sprache Java persistenzfähig gemacht, sondern dies geschieht im Fall von Poet über eine Konfigurationdatei und den Postprozessor ptj. Der Postprozessor ptj verarbeitet die Konfigurationsdatei und erzeugt persistenzfähige Klassen. Diese Konfigurationsdatei hat standardmäßig die Endung *.opt und enthält 4.2. PERSISTENZFÄHIGE KLASSEN 27 die Informationen über die persistent zu machenden Klassen. Im Falle von Scone hat diese Datei den Namen scone.opt und folgenden Inhalt: [schemata\NetSchema] oneFile = false name = ..\run\NetBase [databases\NetBase] schema = NetSchema name = ..\run\NetBase Die obenstehenden Zeilen legen fest, dass zwei Dateien erzeugt werden sollen, zum einen das Dictionary, zum anderen die Datenbank. Das Dictionary, auch Klassenschema genannt, speichert alle Informationen über die Struktur der persistenzfähigen Klassen. In der Datenbank werden alle vom Nutzer erzeugten Objekte gespeichert. Die folgenden Zeilen der Datei scone.opt legen die Klassen fest, die vom Postprozessor ptj persistenzfähig gemacht werden sollen. [classes\scone.netobjects.NetNode] persistent = true [classes\scone.netobjects.SimpleUri] persistent = true [classes\scone.netobjects.NetNodeInfo] persistent = true [classes\scone.netobjects.HtmlInfo] persistent = true [classes\scone.netobjects.Link] persistent = true [classes\scone.netobjects.Inclusion] persistent = true 28 KAPITEL 4. INTEGRATION DER OBJEKTDATENBANK Der Postprozessor ptj generiert mit Hilfe dieser Informationen und der *.class Dateien persistenzfähige Klassen, indem er sie mit Code, Methoden und Hilfsklassen erweitert. Wenn beim ersten Aufruf noch keine Datenbank existiert, legt der Postprozessor ptj automatisch die Datenbank und das Dictionary (Datenbankschema) an. Der Postprozessor wird mit folgendem Kommando gestartet: ptj -enhance -create -inplace scone.opt Mit diesem Kommando wird die Datenbank erzeugt, und die angegebenen Klassen werden persistenzfähig gemacht. Soll jetzt ein Objekt persistiert werden, muß zunächst die Datenbank für Schreib-Lese-Zugriffe geöffnet werden. Mit package scone; ... public class Scone{ ... //the POET-Database public static Database db = new Database(); wird ein Datenbankobjekt erzeugt. Das Datenbankobjekt wird dann mit //initialize the database db.open("poet://LOCAL/NetBase",Database.OPEN_READ_WRITE); für Lese- und Schreiboperationen geöffnet und zwar für die Datenbank NetBase. Jetzt steht eine Datenbank unter dem Namen NetBase zur Verfügung und die relevanten Klassen wurden persistenzfähig gemacht. 4.3 Orphaned Objects Die beiden Datenbanken unterscheiden sich nicht nur im eingesetzten Persistenzmechanismus (relational vs. objektorientiert). Ein weiterer wesentlicher 4.3. ORPHANED OBJECTS 29 Unterschied zu MySQL besteht darin, dass MySQL keine Transaktionen unterstützt (s. hierzu [10]). Poet hingegen arbeitet vollständig transaktionsorientiert. Das bedeutet, dass jede Datenbankoperationen nur innerhalb eines Transaktionkontextes ausgeführt werden kann. Ist eine Transaktion beendet, werden alle mit ihr assoziierten Objekte als schlafend bezeichnet. Poet bezeichnet diesen Zustand auch als orphaned (verwaist). Poet ermöglicht es aber, diese Objekte zu einem späteren Zeitpunkt in einer neuen Transaktion wieder aufzuwecken. Dies geschieht über die Methoden der statischen Klasse ObjectServices. Dieser Mechanismus erlaubt es, Objekte in einer späteren Transaktion wieder zu verwenden. Der Code dafür sieht folgendermassen aus: Transaction txn1 = new Transaction(); txn1.begin(); NetNode n = new NetNode(); txn1.commit(); Das Objekt n befindet sich nach dem commit() jetzt im schlafenden Zustand. Mit Transaction txn2 = new Transaction(); txn2.begin(); //awake the object in the current Transaction ObjectServices.current().awake(n); wird n wieder aufgeweckt. Jetzt ist es möglich das Objekt n in der zweiten Transaktion txn2 weiter zu verwenden. n.modify(); txn2.commit(); Dieser Mechanismus funktioniert aber nur sofern die benötigten Objektreferenzen sich noch im Sichtbarkeitsbereich befinden. Ist dies nicht der Fall funktioniert das Aufwecken nicht mehr. Abb. 4.3 veranschaulicht diesen Zusammenhang noch einmal graphisch. 30 KAPITEL 4. INTEGRATION DER OBJEKTDATENBANK Programm Methode 1 Transaction txn1 = new Transaction(); txn1.begin(); NetNode n =new NetNode(); txn1.commit(); ... Transaction txn2 = new Transaction(); txn2.begin(); ObjectServices.current().awake(n); n.modify(); txn2.commit(); Methode 2 Transaction txn3 = new Transation(); txn3.begin(); ObjectServices.current().awake(n); Ausführung möglich, da n im Sichtbarkeitsbereich von Methode 1 liegt. Kann nicht ausgeführt werden, da n nicht im Sichtbarkeits− bereich von Methode 2 liegt. Abbildung 4.3: Sichtbarkeitsbereich (Scope) von Objekten 4.4. KONZEPT DER SCHATTENOBJEKTE 4.4 31 Konzept der Schattenobjekte Wie in (Kapitel) 2.1 dargestellt, stellt die Methode handleRequest( SconePipe pipe) der Klasse ParsingMeg den Standard-TokenHandler zur Verfügung. In dieser Methode werden die zu persistierenden Objekte erzeugt und gespeichert. Da persistente Objekte nicht zugreifbar sind, wenn eine Transaktion beendet ist (s. Kapitel 4.3), wäre es die einfachste Lösung die ganze Methode handleRequest durch eine Transaktion einzurahmen. public void handleRequest(SconePipe pipe){ try{ Transaction txn = new Transaction(); txn.begin(); // lade vorhandene Daten <- Daten werden gesperrt // Dokument parsen // aktualisiere und persistiere Daten txn.commit(); } catch(Exception exc){ //Ausnahmebehandlung txn.abort(); } } Diese oben dargestellte Lösung hat aber die Nachteile, dass zum einen alle beteiligten persistenten Objekte gesperrt werden, und zum anderen die Datenkonsistenz nicht sichergestellt ist. Der erste Nachteil resultiert daraus, dass Scone multi-threaded ist und ein anderer Thread nicht auf benötigte persistente Objekte zugreifen kann. Der zweite Nachteil resultiert aus der Tatsache, dass bei einem Abbruch der Datenübertragung aus dem WWW nicht sichergestellt ist, wie die Datenbank darauf reagiert. Diese Lösung hat andererseits den Vorteil, dass auf alle persistenten Objekte zugegriffen werden kann. Die favorisierte Lösung sollte zum einen die gesperrten Objekte schnell wieder freigeben, die Problematik des Datenübertragungsabbruchs entschärfen und persistente Objekte ausserhalb eines Transaktionskontextes zugreifbar machen. Wie in der Abb. 4.4 dargestellt besitzen die Objekte B und C jeweils eine Referenz auf das Objekt A in der Datenbank. Objekt B und C sind 32 KAPITEL 4. INTEGRATION DER OBJEKTDATENBANK Kopien von A. Objekt B und C sind also Projektionen von Objekt A in den Hauptspeicher. Aus dieser Sichtweise leitet sich der Name des Konzeptes ab, ein Schattenobjekt ist also ein Objekt, welches im Hauptspeicher liegt und eine Kopie von einem persistenten Datenbankobjekt ist. Ein Schattenobjekt hat dabei folgende Eigenschaften: • Die Datenbankanbindung ist im Objekt selbst umgesetzt. • Es ist ausserhalb eines Transaktionskontextes zugreifbar. • Es referenziert sich selbst in der Datenbank. Hauptspeicher C User 1 Datenbank transiente Referenz A ente transi User 2 enz Refer B B.equals.(A) = true Abbildung 4.4: Schattenobjekte 4.4.1 Integration der Schattenobjekte Die Integration der Schattenobjekte wird exemplarisch an der Klasse NetNode vorgestellt. Die vollzogenen Änderungen gelten analog für die Klassen Link und Inclusion. Von diesen drei Klassen fungieren NetNode und Link als Einstiegspunkte für die weitere Navigation. Einstiegspunkte nach 4.4. KONZEPT DER SCHATTENOBJEKTE 33 dem ODMG-Standard sind persistente Objekte die einen Namen erhalten, diese werden auch als Wurzel-Objekte bezeichnet. Unter diesem Name können sie später wieder in der Datenbank lokalisiert werden [18]. Der Name für ein Wurzel-Objekt kann nur einmal vergeben werden. Im Falle von NetNode ergibt sich der Name aus der Klasse SimpleUri. Die Klasse SimpleUri ist kompatibel zu RFC 2396 [12]. Einen spezifischen NetNode findet man in der Datenbank also unter seiner URL. Der Name für einen Link setzt sich aus den beiden URLs der verlinkten NetNodes zusammen. Die Klasse NetNode hat folgende Eigenschaften: package scone.netobjects; public class NetNode{ protected SimpleUri sUri; protected long accessCounter; protected long size; protected String mimeType; protected String accessStatus; protected Date lastModified; protected Date firstAccess; protected Date lastAccess; Eine Forderung des Konzeptes der Schattenobjekte ist es, dass sich ein Schattenobjekt selbst in der Datenbank referenzieren kann. Dies wird durch das hinzufügen einer weiteren Eigenschaft erreicht. Mit transient protected NetNode dbReferenz; } kann sich ein NetNode selbst in der Datenbank referenzieren. Dadurch dass dbreferenz transient deklariert ist, wird diese Referenz beim Persistieren nicht gespeichert. Das ist wichtig, da ein Datenbankobjekt ansonsten sein eigener Schatten wäre. Die transiente Referenz ist nur für das Schattenobjekt notwendig, sie ermöglicht es Änderungen am Schattenobjekt wieder auf das Datenbankobjekt übertragen zu können. Dadurch ist es nicht notwendig eine Datebankabfrage zu starten, um das korrespondierende Datenbankobjekt zu einem Schattenobjekt zu finden. 34 KAPITEL 4. INTEGRATION DER OBJEKTDATENBANK Die Klasse NetNode erhielt einen neuen Konstruktor, der sicherstellt, dass ein Schatten- und ein Datenbankobjekt erzeugt wird. public NetNode(SimpleUri sUri, Transaction txn){ txn.begin(); try{ this.dbReferenz = (NetNode)txn.lookup(sUri.getNodeUri()); Mit dieser Zeile überprüft der Konstruktor ob der neu zu erzeugende NetNode nicht schon in der Datenbank existiert. Wenn der Lookup in der Datenbank erfolgreich war, wird der NetNode aus der Datenbank gelesen. readAttributes(); txn.abort(); } Die Methode readAttributes() liest die Attribute und schreibt sie in das Schattenobjekt. protected void readAttributes() { setSUri(new SimpleUri(dbReferenz.getSUri().toString())); Da die Eigenschaft protected SimpleUri sUri eine Referenz auf ein SimpleUri Objekt ist, muss das Objekt geklont werden, da es ansonsten nach Beenden der Transaktion nicht mehr zur Verfügung stände. Das Konzept der Schattenobjekte fordert aber, dass persistente Daten außerhalb eines Transaktionskontextes zur Verfügung stehen, also muss jede Objektreferenz tief kopiert werden. setAccessCounter(dbReferenz.getAccessCounter()); setSize(dbReferenz.getSize()); setMimeType(dbReferenz.getMimeType()); setAccessStatus(dbReferenz.getAccessStatus()); setLastModified(dbReferenz.getLastModified()); setFirstAccess(dbReferenz.getFirstAccess()); setLastAccess(dbReferenz.getLastAccess()); } 4.4. KONZEPT DER SCHATTENOBJEKTE 35 Die restlichen obenstehenden Zeilen lesen die einfachen Eigenschaften aus der Datenbank in das Schattenobjekt ein. Wird der Name nicht in der Datenbank gefunden, existiert also kein Wurzelobjekt, so wirft lookup(String) eine ObjectNameNotFoundException aus. catch(ObjectNameNotFoundException exc){ Wenn kein Wurzelobjekt in der Datenbank existiert, wird vom Konstruktor ein neues Datenbankobjekt angelegt. this.sUri = sUri; setMimeType(getMimeTypeFromExtension(this.sUri.getExtension())); try{ dbReferenz = new NetNode(); Die obige Zeile legt einen leeren NetNode an. dbReferenz.setSUri(new SimpleUri(toString())); storeAttributes(); txn.bind(dbReferenz,sUri.getNodeUri()); } Mit txn.bind(Object,String) wird dbReferenz in der Datenbank persistiert. catch(ObjectNameNotUniqueException ex){ System.out.println("Error in NetNode: Name not Unique!"); } txn.commit();}} Mit dem abschließenden txn.commit steht nun ein Schattenobjekt zur Verfügung. Alle Eigenschaften und Methoden von einem persistenten NetNode stehen nun außerhalb eines Transaktionskontextes zur Verfügung. Das gesamte Objektnetz kann nun nach belieben manipuliert werden, ohne dass die beteiligten persistenten Objekte gesperrt sind. Sollen die Änderungen gesichert werden, so geschieht das mit Hilfe der neuen Methode store(Transaction txn). 36 KAPITEL 4. INTEGRATION DER OBJEKTDATENBANK public void store(Transaction txn){ txn.begin(); ObjectServices.current().awake(dbReferenz); Die Eigenschaft dbReferenz wird aufgeweckt (s. Kapitel 4.3). Der Zugriff auf das Datenbankobjekt ist jetzt wieder möglich und mit storeAttributes(); txn.commit(); } werden die Änderungen an dem Schattenobjekt in der Datenbank persistiert. Die Methode storeAttributes() liest dabei jede Eigenschaft des Schattenobjektes und verändert die Eigenschaften des Datenbankobjektes. protected void storeAttributes() { dbReferenz.setAccessCounter(getAccessCounter()); dbReferenz.setSize(getSize()); dbReferenz.setMimeType(getMimeType()); dbReferenz.setAccessStatus(getAccessStatus()); dbReferenz.setLastModified(getLastModified()); dbReferenz.setFirstAccess(getFirstAccess()); dbReferenz.setLastAccess(getLastAccess()); } Beim Sichern der neugewonnen Informationen wird von einer optimistischen Speicherungsstrategie ausgegangen, d.h. sollte eine Datenbankobjekt mehrfach manipuliert werden, so wird davon ausgegangen, dass die zuletzt gesicherten Informationen auch die aktuellsten sind. Neu zu sichernde Informationen werden also nicht auf ihre Aktualität überprüft. Durch das Konzept der Schattenobjekte ist es nicht mehr nötig die Methode handleRequest in eine einzige Transaktion einzurahmen. Stattdessen wird am Anfang der Methode ein Transaktionsobjekt erzeugt, das dann den jeweiligen Methodenaufrufen von NetNode übergeben wird. Durch diesen Mechanismus wird auch die Problematik eines plötzlichen Übertragungsabbruches entschärft, denn Transaktionen sind nur kurz zum Lesen oder Schreiben aktiv, d.h die Datenbank befindet sich immer nur für kurze Zeit im kritischen Zustand. 4.5. KONZEPT DER INFO-OBJEKTE 37 Somit wird die Methode handleRequest wie folgt modifiziert: public void handleRequest(SconePipe pipe){ try{ Transaction txn = new Transaction(); NetNode net = new NetNode(sUri,txn); // Dokument parsen ... // manipuliere Schattenobjekt net.setMimeTyp("text/html"); //dies ist ein Beispiel ... // aktualisiere und persistiere Daten net.store(); } catch(Exception exc){ //Ausnahmebehandlung txn.abort(); } } 4.5 Konzept der Info-Objekte Durch die Verwendung einer objektorientierten Datenbank, bot es sich an die Klasse HtmlNode von der Klasse NetNode abzuleiten. Wie in Abb. 4.1 zu sehen, ist HtmlNode nicht von NetNode abgeleitet. NetNode sowie HtmlNode waren jeweils auf eine relationale Tabelle abgebildet und standen in einer 1-0..1 Beziehungen (s. Abb. 2.2). HtmlNode ist somit eine spezieller NetNode. Dadurch das zunächst einmal jedes Objekt, das von Scone geparst wird, als eine Instanz von NetNode aufgefasst wird, ist es nicht möglich eine Instanz von NetNode einfach in eine Instanz von HtmlNode umzuwandeln. Wird HtmlNode von NetNode abgeleitet, so kann man eine Instanz von NetNode nur dann in explizit in eine Instanz von HtmlNode casten, wenn instanceof HtmlNode von einer Instanz von NetNode wahr ist, ansonsten tritt eine ClassCastException auf. Da jede Instanz von NetNode als ein benanntes Wurzelobjekt in der Datenbank gespeichert ist, müßte die umzuwandelnde Instanz von NetNode durch eine neu erzeugte Instanz von HtmlNode ersetzt werden. Dies ist aber mit 38 KAPITEL 4. INTEGRATION DER OBJEKTDATENBANK implementiert NetNodeInfo HtmlInfo nicht implementiert MultimediaInfo SoundInfo WAVInfo BildInfo MP3Info Abbildung 4.5: Mögliche Info-Objekthierarchie erheblichen Aufwand von Datenbankoperationen verbunden. Das Konzept der Info-Objekte reduziert die notwendigen Datenbankoperationen und ermöglicht es, dass erst zu einem späteren Zeitpunkt entschieden wird um was für einen Netzknoten es sich handelt. Das Konzept sieht weiterhin vor, dass eigene Informationsstrukturen implementiert werden können. Eine denkbare Informationsstruktur ist in Abb. 4.5 dargestellt. Implementiert sind zur Zeit nur die Klassen NetNodeInfo und HtmlInfo. HtmlInfo stellt die gleiche Funktionalität wie HtmlNode zur Verfügung. Ein Info-Objekt ist im Sinne dieses Konzeptes, ein Objekt das spezialisierte Informationen enthält und damit eine Instanz von NetNode näher spezifiziert. So erhält eine Instanz von NetNode quasi einen zusätzlichen Typ. 4.5.1 Integration der Info-Objekte Die Integration der Info-Objekte gestaltet sich denkbar einfach. Zuerst wurde die abstrakte Klasse NetNodeInfo implementiert. Diese ist folgendermaßen implementiert: package scone.netobjects; public abstract class NetNodeInfo{ } 4.5. KONZEPT DER INFO-OBJEKTE 39 Diese Klasse ist völlig leer. Es wäre auch möglich gewesen ein Interface zu implementieren, es war mir aber nicht möglich Interfaces in POET zu persistieren. Der ODMG-Standard sieht nicht vor, dass abstrakte Klassen und Interfaces persistenzfähig sind [18]. Alle Info-Objekte müssen von NetNodeInfo abgeleitet werden, wie in Abb. 4.5 dargestellt. Zu beachten ist, dass nur die Klassen NetNodeInfo und HtmlInfo implementiert sind. Die Klasse HtmlInfo hat folgenden Eigenschaften: package scone.netobjects; public class HtmlInfo extends NetNodeInfo{ protected protected protected protected protected protected protected protected protected protected protected protected protected protected protected protected protected protected protected protected protected ... } int scannedDepth=0; String title; String author; String keywords; String description; String language; String body; String thumbnail; long numberOfLinks; long numberOfExternalLinks; long numberOfImages; long spaceOfImages; long numberOfWords; long numberOfParagraphs; boolean frames; boolean forms; boolean commercial; boolean javascript; boolean plugins; boolean animation; boolean sound; 40 KAPITEL 4. INTEGRATION DER OBJEKTDATENBANK Info-Objekte können nun einfach als zusätzliche Eigenschaft mit get- und set-Methode in die Klasse NetNode integriert werden. package scone.netobjects; ... public class NetNode{ ... protected NetNodeInfo netNodeInfo; ... public NetNodeInfo getNetNodeInfo(){ return this.netNodeInfo; } /** * Set and Store the Document Info to a NetNode. */ public void setNetNodeInfo(NetNodeInfo netNodeInfo, Transaction txn){ ... } } Dadurch das die Klasse NetNodeInfo abstrakt ist, werden nie Instanzen von ihr erzeugt. Es werden nur Instanzen von Unterklassen von NetNodeInfo erzeugt, die dann mit der Methode setNetNodeInfo(NetNodeInfo n, Transaction txn) von NetNode in das Feld protected NetNodeInfo netNodeInfo geschrieben werden. Dadurch ist es möglich erst zu einem späteren Zeitpunkt zu entscheiden von welchem Typ eine Instanz von NetNode ist, d.h. ob ein NetNode z.B. eine Html-Seite ist. Außerdem ist die Erweiterung um neue Informationstypen möglich. Durch die Info-Objekte ist es quasi möglich den Typ einer Instanz von NetNode zu ändern, indem mit der Methode setNetNodeInfo(NetNodeInfo n,Transaction txn) ein neues Info-Objekt gesetzt wird, da sich verschiedene Arten von Netzknoten ja nur in ihren Info-Objekten unterscheiden, und diese alle Subklassen von NetNodeInfo sind. Hiermit sind alle notwendigen Änderungen am Persistenzmechanismus beschrieben. Die Änderungen an den anderen persistenzfähigen Klassen entsprechen den Änderungen an der Klasse NetNode, d.h. sie wurden auch um das Konzept der Schattenobjekte erweitert. Das Konzept der Info-Objekte ist nur in der Klasse NetNode integriert. Kapitel 5 Resümee In der vorliegenden Studienarbeit wurden mehrere Persistenzmechanismen für Java vergleichend betrachtet. Ziel war es einen neuen Persistenzmechanismus für die Scone-Plattform zu finden und zu implementieren. Von den untersuchten Persistenzmechanismen • relationales Datenmodell, • objektorientiertes Datenmodell, • objektrelationales Datenmodell, • Java Serialisierung, • PJAMA, • EJB, JDO etc. wurde das objektorientierte Datenmodell ausgewählt. Die relationale Datenbank MySQL, die Scones Persistenzmechanismus bisher zugrunde lag, wurde durch die Object Server Suite von Poet ersetzt. Durch den Einsatz einer objektorientierten Datenbank war ein Objektmapping auf relationale Tabellen nicht mehr erforderlich. Deshalb konnten alle Hilfsklassen aus dem Paket scone.netobjects entfernt werden. Die resultierende Paketstruktur ist dadurch stark vereinfacht worden (vgl. hierzu Abb. 4.1 auf Seite 25 und Abb. 4.2 auf Seite 26). Auch der Code wurde durch den Wegfall des Objektmappings erheblich vereinfacht. Das Konzept der Schattenobjekte wurde eingeführt. Schattenobjekte sind Kopien von persistenten Objekten, die lokal im Speicher gehalten werden. 41 42 KAPITEL 5. RESÜMEE Der Vorteil von Schattenobjekten liegt darin, dass zum einem Transaktionen auf der Datenbank nur kurz geöffnet werden, zum anderem darin, dass sie ausserhalb eines Transaktionskontextes manipulierbar sind. Dadurch wird zum einem den Instabilitäten des WWW Rechnung getragen, zum anderen können beliebig viele Schattenobjekte erzeugt werden, da das persistente Objekte nicht dauerhaft von einem Nutzer gesperrt ist. Beim Zurückschreiben der Informationen wird von einer optimistischen Strategie ausgegangen, d.h. es wird angenommen, dass der Nutzer, der zuletzt seine Informationen sichert, über die aktuellsten verfügt. Die Schattenobjekte heben den Vorteil des einfacheren Codes teilweise wieder auf. Da es notwendig ist, die transitive Hülle eines Netzknotens zu kopieren, wird der Code wieder komplizierter. Dies ist wohl auch der Hauptgrund dafür, dass Scone mit diesem Persistenzmechanismus langsamer ist. Für die Zukunft wäre eine Beschleunigung des neu implementierten Persistenzmechanismuses wünschenswert, da er eine deutlich schlechtere Performanz als die Implementation in MySQL bietet. Neben dem Konzept der Schattenobjekte wurde noch das Konzept der Info-Objekte eingeführt. Info-Objekte sind Objekte die einen Netzknoten (NetNode) näher spezifizieren, d.h. ein Netzknoten wird um genauere Informationen angereichert. Die Info-Objekte ermöglichen es zum einem eigene Informationsstrukturen (wie in Abb. 4.5 auf Seite 38 angedeutet) zu implementieren; zum anderen läßt sich die genauere Spezifizierung eines Netzknotens einfach austauschen. Die Weiterentwicklung der in dieser Studienarbeit entwickelten Konzepte ist eine interessante Herausforderung. Das Potential der eingesetzten Datenbank von Poet wurde sicherlich noch nicht voll ausgeschöpft. Abschließend läßt sich feststellen, dass es nach Anwendungsbereich ein anderer Persistenzmechanismus gewählt werden sollte. Es gibt keinen Persistenzmechanismus der für jeden Anwendungsbereich optimal geeignet ist. Auch der Wechsel eines Persistenzmechanismuses ist nicht trivial. Im vorliegenden Fall, war der Wechsel vom relationalen System MySQL zum objektorientierten System von Poet nicht einfach zu vollziehen. Es reichte nicht aus die zu persistierenden Klassen zu identifizieren und alle anderen Klassen zu entfernen. Es war nötig zwei neue Konzepte zu entwickeln, um die beim Wechsel auftretenden Probleme zu lösen und spezifische Charakteristika des Anwendungskontextes zu berücksichtigen. Literaturverzeichnis [1] R. Adatia. Professional EJB. Birmingham: Wrox Press, 2001. [2] R. Barret and P. P. Maglio. Intermediaries: New places for producing and manipulating web content. In Proceedings of the 7th Internationl WWW Conference, 1998. [3] Carnegie Mellon University. Die Homepage von WebSPHINX. http://www-2.cs.cmu.edu/ rcm/websphinx/. [4] E.F. Codd. A relational model of data for large shared data banks. CACM, 13(6th), Juni 1970. [5] C.J. Date. An Introduction to Database Systems. Addison-Wesley, 7th edition, 2000. [6] Persistence Group Glasgow der Universität Glasgow. Das PJAMA System. http://www.dcs.gla.ac.uk/pjava/, 1995. [7] Duden Verlag. Fremdwörter Duden - Band 6. Duden Verlag, 2nd edition, 1966. [8] IBM Almaden. WBI: Web Intermediaries, Homepage von WBI. http://www.almaden.ibm.com/cs/wbi/. [9] A. Kemper and A. Eickler. Datenbanksysteme - Eine Einführung. R. Oldenbourg Verlag, 3., korr. edition, 1999. [10] M. Kofler. MySQL - Einführung, Programmierung, Referenz. AddisonWesley, 2001. [11] P. C. Lockemann, J. W. Schmidt, et al. Datenbankhandbuch. Springer Verlag, 1st edition, 1993. [12] P. Loshin, editor. Big Book of World Wide Web RFCs. Morgan Kaufmann, 2000. 43 44 LITERATURVERZEICHNIS [13] M. Mayer. Kontextvisualisierung: BrowsingIcons und BrowsingGraphs zur Verbesserung der Orientierung und Navigation im WWW. In P. Ohly, G. Rahmstorf, and A. Sigel, editors, Fortschritte in der Wissensorganisation, pages 267–280. Ergon Verlag, 2000. [14] R. C. Miller and B. Krishna. Sphinx: A framework for creating personal, site-specific Web Crawlers. In Proceedings of the 7th International WWW Conference, 1998. [15] POET Software GmbH. POET Object Server Suite: POET Java Programmer’s Guide. POET Software GmbH, 2000. [16] G. Saake and K.-U. Sattler. Datenbanken und Java: JDBC, SQLJ und ODMG. dpunkt.Verlag (iX-Edition), 1st edition, 2000. [17] Sun Microsystems. http://access1.sun.com/jdo. JDO - Java Data Objects. [18] F. Thelen. Der ODMG-Java-Standard 3.0. Java Spektrum, 4(26):S.49 – S.55, Juli/August 2000. [19] F. Thelen. XML, Java und Persistenz. Java Spektrum, 1(29):S.59 – S.65, Januar/Februar 2001. [20] H. Weinreich and V. Jürgens. http://www.scone.de, 2001. SCONE Framework Dokumentation. [21] H. Weinreich and W. Lamersdorf. Concepts for improved visualization of Web link attributes. In Proceedings of the 9th International WWW Conference. Elsevier, 2000. [22] F. Wollenweber. Entwicklung eines generischen Robots für das SconeFramework (in progress), 2002.