.. .. .. .. .. Web-Publishing-Seminar WS 98/99 Web-basiertes DataWarehousing . . . . . . . . . . eingereicht bei Prof. Dr. Vornberger eingereicht von: Dipl.-Kfm. Karsten Brodmann Matrikel-Nr.: 806 343 Osnabrück, den 18.12.1998 Inhaltsverzeichnis Einleitung ................................................................................................................................... 1 Motivation............................................................................................................................. 1 Ziel und Vorgehensweise ....................................................................................................... 1 Client/Server-Architekturen......................................................................................................... 2 Protokolle: TCP/IP und HTTP ............................................................................................... 2 Die TCP/IP-Protokollfamilie ............................................................................................ 2 Das HTTP-Protokoll......................................................................................................... 2 2- und 3-stufige Architekturen ............................................................................................... 2 Das World Wide Web ............................................................................................................ 4 Datenbankarchitekturen ......................................................................................................... 4 Java-Client/Server-Architekturen: Remote Methode Invocation.............................................. 5 Architektur Web-basierter Data-Warehouses ............................................................................... 6 Realisierungsalternativen ....................................................................................................... 7 Datenbankintegration mit JDBC............................................................................................. 8 JDBC-Treiber................................................................................................................... 8 JDBC-Treiber-Architekturen ............................................................................................ 9 JDBC-Treiber-Architektur mit Typ-1-Treibern............................................................ 9 JDBC-Treiber-Architektur mit Typ-2-Treibern.......................................................... 10 JDBC-Treiber-Architektur mit Typ-3-Treibern.......................................................... 11 JDBC-Treiber-Architektur mit Typ-4-Treibern.......................................................... 11 Kommunikation im Web-basierten Data-Warehouse............................................................. 12 JDBC in der Praxis.................................................................................................................... 13 Grundlagen der Verwendung von JDBC............................................................................... 13 Die Driver-Schnittstelle .................................................................................................. 13 Das DriverManager-Objekt............................................................................................. 14 Verbindung zur Datenbank: Die Connection-Schnittstelle ............................................... 15 Projekt: Abenteuer und Freizeit............................................................................................ 16 Projektbeschreibung ....................................................................................................... 16 Anwendungsarchitektur.................................................................................................. 17 Relationales Datenmodell ............................................................................................... 17 Java-Klassen-Design ...................................................................................................... 17 Bewertung des Prototypen .............................................................................................. 18 Schlußbetrachtung und Ausblick ............................................................................................... 19 Literaturverzeichnis................................................................................................................... 19 Anhang ..................................................................................................................................... 20 Hinweise zu den Programmbeispielen .................................................................................. 20 Client-/Server-Kommunikation via Sockets.......................................................................... 21 SocketServer.java........................................................................................................... 21 SocketClient.java ........................................................................................................... 21 SocketClientFrame.java.................................................................................................. 23 DlgSocketClient.java...................................................................................................... 23 DialogLayout.java .......................................................................................................... 25 Remote Method Innvocation ................................................................................................ 28 Lookup.java ................................................................................................................... 28 LookupServer.java ......................................................................................................... 28 LookupClient.java .......................................................................................................... 29 .. .. .. .. .. Web-basiertes DataWarehousing Web-Publishing-Seminar WS 98/99 Einleitung Motivation Data-Warehousing nennt man einen Prozeß, bei dem Daten aus operativen Administrations- und Dispositionssystemen extrahiert und in einer dedizierten Datenbank zur Verarbeitung durch Programme für Abfragen, Berichte, Online-Analyse (OLAP – Online Analytical Processing), Entscheidungsunterstützungssysteme und ähnlichem mehr bereitgestellt werden [vgl. KIMB96]. Die meisten Data-Warehouse-Systeme setzen traditionelle Client/Server-Techniken ein, um auf den Warehouse-Informationsspeicher zuzugreifen. In jüngerer Zeit hat der Einsatz von Intranets zur Verwendung von Web-Clients zum Zugriff auf den Data-Warehouse-Datenbankserver geführt. Ein Web-basiertes Data-Warehouse-System bietet zahlreiche Vorteile. Zum Beispiel gehören zu den Web-Schnittstellen schlanke Clients, die geringere Hardwareanforderungen als normale Desktop-Systeme stellen. Dadurch werden Hardwarekosten gesenkt, die Zuverlässigkeit erhöht und der Aufwand für die Hardwarewartung verringert. Die Verwendung schlanker Clients ermöglicht den Unternehmen außerdem, die Vorteile neuer Hardwaretechnologien wie Netzwerkcomputer und Window-based-Terminals erheblich besser zu nutzen [vgl. WHIT98]. Darüber hinaus wird der größte Teil der von Web-Clients genutzten Software zentral auf Servern gespeichert und nach Bedarf von den Clients heruntergeladen. Dieser Ansatz verringert den mit der Installation und der Pflege der (bisherigen) Client-Software verbundene Aufwand. Ziel und Vorgehensweise OLAP-, MIS-Anwendungen (MIS – Management Information Systems) und ähnliche erfordern ein hohes Maß an Interaktionsmöglichkeiten, um den an sie gestellten Anforderungen gerecht werden zu können [vgl. THIE98]. Es ist noch nicht lange her, da waren Web-Anwendungen diesen Anforderungen nicht gewachsen, in jüngster Vergangenheit aber sprießen entsprechende Web-Clients geradezu aus dem Boden. Kaum ein Hersteller von Data-Warehouse-Frontends läßt es sich nehmen, neben den „klassischen“ Anwendungen auch eine entsprechende Web-Variante anzubieten [vgl. WHIT98]. Möglich wurde die o.g. Inflation durch Kompontentechnologien wie z.B. ActiveX aus dem Hause Microsoft oder der noch jungen und „Web-fähigen“ Programmiersprache Java von Sun Microsystems, Inc. Java erregt auch heute noch, etwa drei Jahre nach Freigabe der ersten Version Ende 1995, das Interesse der Fachwelt. Java stößt ständig in neue Anwendungsgebiete vor, unter anderem auch in das Data-Warehousing. Data-Warehouse-Anwendungen benötigen Datenbankanbindungen. Die Möglichkeiten, die mit Java bestehen, die hieraus resultierenden Architekturen der Anwendungssysteme sowie deren Einfluß auf die Performance sollen im folgenden untersucht werden. Die Analyse geht in folgenden Schritten vor: • Dedizierte Darstellung der einzelnen Client/Server-Architekturen, die in einem Web-basierten Data-Warehouse involviert sind 1 • Zusammenführung der Einzel-Architekturen zu einer Gesamt-Architektur, die für ein Web-basiertes Data-Warehouse geeignet ist • Darstellung der Java-Datenbankschnittstelle JDBC (Java Database Connectivity) • Systemarchitekturen Web-basierter Data-Warehouse-System auf der Grundlage von JDBC • Implementierung einer prototypischen Anwendung zu der dargestellten Architektur eines Web-basierten Data-Warehouses Hierbei werden schwerpunktmäßig die Realisierungsalternativen unter Einbezug von Java betrachtet. Client/Server-Architekturen Protokolle: TCP/IP und HTTP Alle Kommunikationsvorgänge, sowohl im Internet als auch im Intranet, basieren mehr oder minder direkt auf TCP/IP (Transmission Control Protocol/Internet Protocol). Diese Protokollfamilie bildet somit auch die Basis von Web-basierten Data-Warehouses, in denen die beteiligten Komponenten über IP-Nummern angesprochen werden. Im Web wird das HTTP-Protokoll (Hypertext Transmission Protocol) verwendet, das seinerseits wiederum auf TCP/IP aufsetzt. Die TCP/IP-Protokollfamilie Im Rahmen der zunehmenden Verbreitung entfernter Zugriffe auf unterschiedlichste Ressourcen hat sich TCP/IP als Quasi-Standard unter den Protokollen durchgesetzt. Selbst die „WindowsWelt“ hat sich in zunehmendem Maße hierauf eingestellt. TCP/IP ist eine Protokollfamilie, die die Grundlage für unterschiedlichste Dienste im Internet bildet. TCP und IP sind eigentlich zwei unterschiedliche Arten von Protokollen. Obwohl beispielsweise NFS (Network File System) gemeinhin als TCP/IP-Dienst angesehen wird, verwendet dieser Dienst lediglich IP, aber kein TCP [vgl. EVAN95, DICK97]. Einige der bekanntesten TCP/IP-Dienste sind FTP (File Transfer Protocol), RLOGIN (Remote Login), TELNET (Network Terminal Protocol), EMail (Electronic Mail) und HTTP. Das HTTP-Protokoll Mit dem Ziel, ein anwendungsfreundliches, schnelles und dementsprechend schlankes Protokoll für verteilte Hypermedia-Systeme zu entwickeln, wurde Ende der achtziger Jahre am Schweizer Kernforschungszentrum CERN die Entwicklung des HTTP (Hypertext Transfer Protocol) begonnen. Seit 1990 wird das Protokoll eingesetzt. HTTP basiert auf einem zustandslosen Anfrage-/Antwort-Pradigma. Der Client schickt eine Anfrage in Form eines URL (Unified Resource Locator) an den Server. Der URL setzt sich aus dem Übertragungsprotokoll (z.B. HTTP), der Adresse des Servers, der relativen Adresse im Dateisystem des Zielrechners und dem Namen des gewünschten Dokuments zusammen. HTTP versieht diese Anfrage mit verschiedenen Zusatzinformationen bezüglich der Protokoll-Version, einigen ClientInformationen und einigem mehr. Der Server, der Zielrechner, antwortet seinerseits in Form der Übertragung des gewünschten Dokuments an den Client. Anschließend wird die Verbindung seitens des Servers sofort beendet. Client und Server stehen dann nicht mehr in Verbindung [vgl. BERG96]. 2- und 3-stufige Architekturen Grundsätzlich sind zwei Formen von Client/Server-Architekturen zu unterscheiden, 2-stufige (2Tier) und 3-stufige (3-Tier) Architekturen. 2 Traditionelle Client/Server-Systeme, z.B. Datenbank-Client/Server-Systeme basieren auf einer 2stufigen Architektur, Datenbank-Server-Ebene und Client-Ebene. Eine 3-stufige Architektur besteht aus den beiden o.g. Ebenen zuzüglich einer dazwischen befindlichen Schicht, der sogenannten Middleware oder Middle-Tier. Je nach Funkionalität des Gesamtsystems kann diese Schicht unterschiedlichste Aufgaben wahrnehmen. Oftmals wird die Middleware genutzt, um Daten graphisch aufzubereiten oder speziell im Falle von Datenbank-Anwendungen, um für die vom Client angeforderten Daten die für die Datenbank notwendigen SQL-Statements zu generieren. Der Client kann in diesen Fällen sehr schlank und unabhängig vom verwendeten Datenbanksystem gehalten werden [vgl. EVAN95]. Insbesondere bei Web-Applikationen kann sich dieser Umstand positiv auf die Performance des Gesamtsystems auswirken, da das über das Internet zu übertragende Datenvolumen drastisch gesenkt werden kann. Die lokale Netzlast auf der Server-Seite kann sich dagegen steigern [vgl. DICK97]. Bezüglich Web-basierter Datenbank-Anwendungen, die mit Java realisiert und deren Frontends (Applets) in einem Web-Browser ausgeführt werden, bietet eine 3-stufige Architekur weiterhin den Vorteil, daß notwendige Programmodule nötigenfalls in native Code implementiert werden können, ohne das es zu Kollisionen mit der in den Browsern implementierten Java-Security kommt. Die Verwendung einer Middleware bietet zusätzlich den Vorteil größerer Flexibiliät und Skalierbarkeit, wobei die Ebenen Server und Middle-Tier nicht notwendigerweise auf unterschiedlichen Rechnern installiert sein müssen. Wenn die Anforderungen der Applikation nach Performance und Speicherbedarf steigen, kann leichter ein Server aufgerüstet werden, als dies seitens einer möglicherweise unbekannten Anzahl von Clients zuzumuten, geschweige denn problemlos realisierbar wäre. Schießlich lassen sich im Application-Server, der Middleware, zusätzliche Sicherheitsmechanismen implementieren. Datenbanken, die eventuell nicht mit TCP/IP als Übertragungsprotokoll arbeiten, von einem Java-Applet also nicht ansprechbar wären, können integriert werden. Der Application-Server würde dann neben anderen Aufgaben als Protokollumsetzer arbeiten. Die Nachteile einer dritten Ebene sind ein erhöhter Management-, Entwicklungs- und Administrationaufwand. Die Fehlersuche bei der Implementierung wird komplizierter, weil die Lokalisierung der Fehler erschwert wird [vgl. DICK97]. Die Vor- und Nachteile können wie folgt zusammengefaßt werden: Vorteile Nachteile • Entlastung der System-Clients, wodurch die Entwicklung sogenannter Thin-Clients möglich wird • • Entlastung des globalen/lokalen Netzwerks • • Möglichkeit der physischen Trennung von Web- und Datenbank-Server • Möglichkeit des Einsatzes nativer Komponenten auf dem Application-Server • Bessere Skalierbarkeit • Integrationsmöglichkeit zusäzlicher Sicherheitsmechanismen • Zusätzliche Serverlast und somit zuätzlicher Hardwarebedarf seitens des/der Server(s) Erhöhter Entwicklungs-, Management- und Administrationsaufwand Möglicherweise Erhöhung des lokalen Netzwerkverkehrs Tabelle 1: Vor- und Nachteile 3-stufiger Anwendungs-Architekturen 3 Das World Wide Web Abbildung 1 zeigt das Client/Server-Prinzip des Web auf Basis einfacher HTTP-Verbindungen ohne CGI (Common Gateway Interface) und ohne Einbindung von Nicht-HTTP-Protokollen. Hierbei handelt es sich um eine 2-Tier-Architektur. Ein Web-Browser als Client nimmt Verbindung zu einem Web-Server auf, der seinerseits mittels Hyperlinks auf andere Web-Server verweisen kann. Die gewünschten Dokumente werden, wie oben beschrieben, via HTTP an den Client übertragen. Die Verbindung zwischen Client und Server wird nach erfolgter Übertragung der Dokumente abgebaut, eine neue Anfrage seitens des Clients führt dann zu einem erneuten Verbindungsaufbau [vgl. BERG96]. WWW-Client WWW-Client WWW-Browser WWW-Browser HTTP WWW-Server HTTP HTML-Link HTTP WWW-Server Abbildung 1: Das Client/Server-Prinzip des WWW Datenbankarchitekturen Heutige Datenbankanwendungen werden, mit Ausnahme von Desktop-Datenbanken, ähnlich den o.g. Web-Anwendungen nach dem 2-stufigen Client/Server-Prinzip betrieben. Abgesehen von integrierten Lösungen wie z.B. NCA (Network Computing Architecture von ORACLE), das eine 3Ebenen-Architektur realisiert, arbeiten alle Architekturansätze nach dem klassischen Client/ServerPrinzip, also einem Datenbank-Server und den entsprechenden Clients. Abbildung 2 stellt diese Basis-Architektur graphisch dar [vgl. EVAN95]. Der Zugriff eines DB-Clients erfolgt in der Weise, daß die betreffende Datenbankanwendung ihre Anfragen an die beim Client installierte Netzsoftware weiterleitet (z.B. SQL*Net bei ORACLE), welche diese ihrerseits über die jeweilige Betriebssystem-Netzwerksoftware und das Betriebssystem des Clients bzw. des Servers an die Netzwerksoftware und anschließend an das DatenbankManagementsystem weiterleitet. Die Antwort, das Ergebnis der Anfrage, nimmt anschließend den umgekehrten Weg zurück zum Client. Neuere Versionen von Datenbank-Netzwerksoftware (z.B. SQL*Net V2 von ORACLE) bieten noch weiterreichende Möglichkeiten. Hierbei werden Verbindungen über heterogene Netzwerke hinweg protokollunabhängig (TCP/IP, DECNet, SPX/IPX etc.) und medienunabhängig (Ethernet, Token Ring etc.) unterstützt [vgl. ORAC97]. Auch in Bezug auf Web-basierte Data-Warehouses ist dieser Punkt nicht zu unterschätzen. Zwar erfolgt die Anfrage des Clients via HTTP, also TCP/IPbasiert, die Anbindung der Datenbank ist aber sowohl auf Protokollebene als auch physikalisch aus Sicht des Clients unbestimmt oder zumindest variabel. 4 DB-Client Datenbank-Anwendung Datenbank-Netzsoftware Betriebssystem-Netzsoftware Betriebssystem SQLAnfragen Netzwerkprotokoll (z.B. TCP/IP) Daten Betriebssystem Betriebssystem-Netzsoftware Datenbank-Netzsoftware DB-Server Datenbank-Anwendung Abbildung 2: Einfaches DB-Client/Server-System Die folgende Abbildung zeigt ein Szenario (ohne Web) mit drei Clients, die unter Verwendung unterschiedlicher Protokolle auf einen ORACLE-Datenbankserver zugreifen. Da der Datenbankserver mit dem TCP/IP-Protokoll arbeitet, wurde ein MPI (Multi Protocol Interchange von ORACLE), ein Protokollumsetzer, zwischengeschaltet. Das MPI-Modul extrahiert lediglich die tatsächlichen Nutzdaten aus dem jeweiligen Quellprotokoll und überträgt sie in das Zielprotokoll. Der Overhead, der sonst bei einer Kapselung der Datenpakete entstehen würde, enfällt. Dies erhöht die Performance und damit die Datendurchsatzrate im Netz erheblich [vgl. ORAC97]. DB-Client DB-Client SPX/IPX MPI DB-Client DECNet TCP/IP MPI DB-Server Abbildung 3: Protokollkonvertierung mit SQL*Net V2 und MPI Java-Client/Server-Architekturen: Remote Methode Invocation Ein recht intensives Feld der Diskussion und Entwicklung ist die Realisierung verteilter objektorientierter Anwendungen. Als Stichworte seien hier CORBA (Common Object Request Broker Architecture), DCOM (Distributed Component Object Model von Microsoft) und RMI (Remote Method Invocation von Sun) genannt. Das vorrangige Ziel all dieser Bemühungen besteht letztlich darin, die Rechenlast in geeigneter Form auf mehrere Hosts zu verteilen oder bestehende Anwendungen zu einer Gesamtapplikation zu integrieren. 5 Socket-Verbindungen, wenngleich mit Java ohne Probleme realisierbar, haben mit Objektorientierung, dem „Basis-Pradigma“ von Java und somit auch JDBC, nichts gemein, weshalb sie im folgenden nicht weiter betrachtet werden. Das Augenmerk wird auf RMI gerichtet, der Java-eigenen Client/Server-Verbindung. RMI benutzt TCP/IP und ist seit Java 1.1 standardmäßig im Package java.rmi enthalten. Für Java 1.02 gibt/gab es eine eigene Version. RMI ermöglicht die Ausführung und Kommunikation zwischen Java-Programmen in unterschiedlichen Adreßräumen. Die Analogie zu CORBA ist unverkennbar. CORBA ist im Gegensatz zu RMI aber interoperabel, also unabhängig von der verwendeten Programmiersprache, während RMI von einer homogenen Java-Umgebung ausgeht. Java kann selbstverständlich in CORBA-Lösungen eingesetzt werden, RMI erlaubt jedoch eine konsistentere Modellierung, da die „Java-Welt“ nicht verlassen werden muß. Der Nachteil von JavaRMI ist, daß RMI gegenüber CORBA langsamer ist, denn Java lädt die benötigten Klassen via HTTP, während CORBA das schnellere IIOP (Internet Inter-ORB Protocol) benutzt. Gegenüber Socket-Verbindungen abstrahiert RMI von der Stream-Ebene und bietet dem Client die Möglichkeit Methoden auf seiten des Servers anzusprechen. Dies ist einem RPC (Remote Procedure Call) sehr ähnlich. Der wesentliche Unterschied besteht in der Objektorientiertheit der RMI [vgl. SUN98, ORFA98]. Schematisch kann das RMI-Modell wie folgt dargestellt werden [vgl. SUN98]: Applikationen RMISystem Client Server Stubs Skeleton Remote Reference Layer Transport Abbildung 4: Das Java-RMI-System Ein Methoden-Aufruf des Client wandert auf der Seite des Clients herunter bis auf die Transportebene des RMI-Systems, um dann auf der Seite des Server wieder herauf zu wandern und die entsprechende Aktion zu bewirken. Hierzu verwendet der Client einen sogenannten Stub (Stummel), eine Client-seitige Referenz auf das Remote-Objekt. Der Stub ist eine Implementierung des Remote-Interfaces des Remote-Objekts. Ein Stub wird mit dem rmic-Compiler des JDK erzeugt. Der Remote Reference Layer ist für die Interpretation der Semantik der Client-Anforderung sowie der entsprechenden Antwort des Servers verantwortlich. Der Transport Layer kontrolliert den Aufbau, die Unterhaltung sowie den Abbau der Verbindung zwischen Client- und Server-Objekt(en). Das Skeleton bewirkt letzlich den Aufruf der angeforderten Methode beim Server-Objekt. Die Antwort des Servers nimmt dann den umgekehrten Weg zurück zum Client [vgl. SUN98]. Architektur Web-basierter Data-Warehouses In den vorangegangenen Abschnitten wurden die einzelnen Client/Server-Architekturen eines allgemeinen Web-Systems auf Basis von HTTP, eines Datenbanksystems und der Kommunikation verteilter objektorientierter Systeme mit Java und RMI betrachtet. Zur Realisierung eines Web6 basierten Data-Warehouses müssen die Modelle geeignet miteinander kombiniert und zu einem Modell integriert werden. In den genannten Fällen spielte die räumliche Distanz zwischen Client und Server keine modellrelevante Rolle. Im Hinblick auf die Performance eines Web-basierten Data-Warehouses ist die Berücksichtigung der räumlichen Trennung sowie die Berücksichtigung der technischen Überbrükkung derselben mittels unterschiedlicher Netzwerke durchaus relevant. Ähnliches gilt für Flexibiliät der Planung und Implementierung eines nicht mehr modellhaften, sondern konkreten Systems. Bei der Realisierung der Architektur für ein Web-basiertes Data Warehouse geht es im wesentlichen um die Frage, welche Aufgabe an welcher Stelle verrichtet werden und wieviele Datentransfers anfallen. Die Realisierung aller benötigten Server, Web-Server und Datenbank-Server bei 2stufiger Architektur sowie einem Java-Server (Application-Server) bei 3-stufiger Architektur, auf einem gemeinsamen Rechner führt beispielsweise zu einer relativ geringen Netzwerkbelastung zwischen den beteiligten Maschinen (ein Server plus n Clients); doch müßte der Server möglicherweise eine sehr hohe Performance besitzen, um den Anforderungen der Clients zu genügen, je nach Anzahl, Abfragevolumina und Abfragekomplexität der Clients. Daneben spielen Fragen des Aufwands und der Flexibilität bei der Realisierung des Systems, der Verfügbarkeit kommerzieller Werkzeuge und vor allem der Bedarf an Installationen auf den Clients ein wichtige Rolle. Die folgende Liste enthält die wichtigsten Realisierungskriterien: • Performancebedarf bei dem/den Server(n) • Performancebedarf der Clients • Bedarf an Vorinstallationen bei den Clients • Realisierungsaufwand • Flexibilität der Realisierung • Verfügbarkeit kommerzieller Werkzeuge für die gewählte Architektur [vgl. DICK97, ORFA98] Realisierungsalternativen Vor dem Verglich der möglichen Realisierungsalternativen sollen einige Überlegungen angestellt werden, die die Anzahl der vernünftigen realisierbaren Alternativen einschränken: • Der WWW-Client ist in jedem Fall identisch mit dem Java-Client, d.h. es werden nur Java-Applets betrachtet, die im Browser, dem WWW-Client ablaufen. Es gibt somit nur einen Typ von Client des Gesamtsystems, dieser ist sowohl WWWClient als auch Java-Client. Im folgenden wird dieser als System-Client bezeichnet. • Der Java-Server fungiert in einem Web-basierten Data-Warehouse als Datenbank-Client. Der Zugriff auf die Daten und deren Visualisierung soll über Mechanismen des WWW bzw. Java erfolgen. Die Visualisierung der Daten über andere (datenbankspezifische) Werkzeuge setzt zum einen deren Installation auf den Clients voraus, zum anderen sind diese Werkzeuge (z.B. ORACLE Reports oder ORACLE Graphics) im allgemeinen nur für den direkten Zugriff auf den Datenbank-Server ausgelegt [vgl. ORAC97]. Zugriffe via WWW sind mit ihnen nicht möglich. Damit sind die in Abbildung 5 darstellten Komponenten für die Realisierung des Architekturdesigns geeignet zu kombinieren. 7 Client Middle-Tier (Java-Server) WWW-Server DB-Server Abbildung 5: Abläufe beim Systemzugriff eines Clients Ein System-Client stellt eine Anfrage an den Web-Server und erhält ein HTML-Dokument mit einem eingebundenen Java-Applet zurück. Der Browser startet dieses Applet, welches sich entweder direkt an den Datenbank-Server wendet oder aber an den Java-Server, der dann seinerseits die Anfrage an den Datenbank-Server startet. Der Datenbank-Server antwortet gegebenenfalls mit den Abfrageergebnissen entweder direkt an den System-Client (2-stufige Architektur) oder an den JavaServer, welcher die Ergebnisse (möglicherweise in aufbereiteter Form) an den System-Client oder in Form von HTML-Dokumenten an den WWW-Server weiterleitet (3-stufige Architektur). Im letzten Fall müßte der System-Client darüber informiert werden, daß die Abfrageergebnisse in Form eines HTML-Dokuments auf dem Server abgelegt wurden und abgerufen werden können. Dies kann beispielsweise durch das Senden der Java-Methode showDocument() der Klasse java.applet.AppletContext geschehen. Datenbankintegration mit JDBC JDBC steht für Java Database Connectivity. JDBC ist eine Schnittstelle, die entwickelt wurde, um den Bedarf nach einer standardisierten Java-Anwendungsschnittstelle zu relationalen DatenbankManagementsystemen zu befriedigen. JDBC gestattet den Zugriff auf relationale Datenbanken aus Java-Anwendungen heraus. Diese Zugriffsmöglichkeiten sind sowohl von der Plattform, auf der die Anwendung läuft, als auch vom zugrundeliegenden Datenbanksystem unabhängig. Die Spezifikation von JDBC ermöglicht das Erstellen von Java-Code zum Etablieren einer Datenbankverbindung, zum Versenden von SQL-Statements an die Datenbank und zum Retrieval von Abfrageergebnissen, Metadaten und Statusmeldungen der Datenbank. Daneben bietet JDBC Möglichkeiten der Typumwandlung von Java- und SQL-Datentypen, zur Behandlung von SQL-Cursors, zum Transaktionsmanagement, der Verwendung von Stored Procedures und einigem mehr. Die Entwicklung der JDBC-API (API – Application Program Interface) wurde bei JavaSoft, einer zur Java-Weiterentwicklung gegründeten Tochterfirma von Sun, im Januar 1996 initiiert. Eine erste Testversion wurde im März 1996 freigegeben. Seit Version 1.1 ist JDBC fester Bestandteil des JDK (Java Developement Kit) [vgl. SUN98]. JDBC-Treiber JavaSoft unterteilt die JDBC-Treiber in vier Kategorien: • Typ 1 - ODBC Bridge-Treiber (ODBC – Open Database Connectivity): Die ODBC-Bridge-Treiber übersetzen JDBC-Aufrufe in ODBC-Anfragen auf der ClientMaschine. Der Einsatz dieser Treiber hat den Aufruf nativer ODBC-Methoden zur Folge. 8 Die hierzu notwendigen ODBC-Treiber müssen auf der Client-Maschine installiert sein, sie können nicht über das Web geladen werden. • Typ 2 – Native API Partitial Java-Treiber: Dieser Treibertyp übersetzt JDBC-Aufrufe direkt für die Datenbank-spezifischen Aufrufe der jeweiligen Datenbank-API. Es handelt sich hierbei um native Treiber, die auf der Client-Maschine installiert sein müssen. • Typ 3 – Net Protocol All-Java-Treiber: Diese Treiber sind vollständig in Java implementiert, so daß sie vom Web-Client über das Netz geladen werden können. Der Client nimmt in diesem Fall Kontakt zu einem Server auf, der die JDBC-Aufrufe in native Datenbank-Aufrufe übersetzt. Diese Lösung setzt das Vorhandensein entsprechender Middleware voraus. • Typ 4 – Native Protocol All-Java-Treiber: Diese Treiber sind vollständig in Java implementiert, erlauben aber dennoch die direkte Kommunikation mit dem Datenbank-Server. Da die Treiber in Java geschrieben sind, könen sie über das Netz auf den Web-Client geladen werden. Die Treiber-Kategorien 1 und 2 wurden im wesentlichen in der Anfangszeit von JDBC entwickelt, um unter Ausnutzung bestehender Komponenten raschen Zugang zu Datenbanken seitens Java zu ermöglichen, Java also Datenbank-fähig zu machen. JavaSoft bezeichnet die Treiber der Typen 3 und 4 als die geeignetsten Lösungen. Jeder clientseitig verwendete Native-Code unterminiert den einzigartigen Vorteil von Java: die Inter- bzw. Intranetfähigkeit. Nativer Code kann nicht über das Netz geladen werden, er muß vielmehr bei jedem Client installiert werden, was nicht immer gewünscht oder gar möglich ist [vgl. DICK97]. JDBC-Treiber-Architekturen In Abhängigkeit der verwendeten Treiber-Typen ergeben sich für JDBC-Anwendungen unterschiedliche Architekturen. An dieser Stelle sollen lediglich die grundsätzlichen Möglichkeiten diskutiert werden, d.h. ohne Einschluß von Socket- oder RMI-Verbindungen zwischen Applet und JDBC-Treiber-Manager. Eine derartige Verbindung hat zur Folge, das Applet und ApplicationServer, der den JDBC-Treiber-Manager beinhaltet, zu einer Netzwerkverbindung wird, so daß hierbei generell eine 3-stufige Architektur zum Tragen kommt. Dies hat wiederum zur Folge, daß unabhängig vom verwendeten Treiber, keinerlei nativer Code zum Web-Client geladen werden muß. JDBC-Treiber-Architektur mit Typ-1-Treibern Bei Abbildung 6 handelt es sich um eine 2-stufige Achitektur. Die Java-Komponenten werden zum Client geladen, auf dem die restlichen Kompontenten für den Datenbankzugriff installiert sein müssen. Die einzige Netzwerkverbindung besteht zwischen der nativen Datenbank-Bibliothek und dem Datenbank-Server. Die Java-Applikation oder das Java-Applet stehen über den JDBC-Treiber-Manager und die JDBCODBC-Bridge in Verbindung mit einer nativen ODBC-Schnittstelle. Ab hier wird ausschließlich ODBC verwendet. Der ODBC-Treiber-Manager wählt den geeigneten, vorkonfigurierten Treiber aus, der direkt über die native Datenbank-Bibliothek mit der der Datenbank kommuniziert [vgl. DICK97]. Folgende Eigenschaften zeichnen diese Architektur aus: • Der notwendige ODBC-Treiber muß beim Client installiert sein. • Native Datenbank-Bibliotheken müssen auf dem Client installiert sein. 9 • Sämtliche Prozesse mit Ausnahme derer des Datenbank-Servers laufen beim Client ab. Java-Applet JDBC-Treiber-Manager JDBC-ODBC-Bridge ODBC-Treiber-Manager ODBC-Treiber Datenbankserver Abbildung 6: JDBC-Architektur mit Typ-1-Treibern JDBC-Treiber-Architektur mit Typ-2-Treibern Auch hierbei handelt es sich um eine 2-stufige Architektur, bei der alle Prozesse mit Ausnahme derer des Datenbank-Servers beim Client ablaufen. Im Gegensatz zur Architektur mit Typ-1-Treibern sind hier die ODBC-Komponenten durch native Schnittstellen zwischen JDBC-Treiber-Manager und nativer Datenbank-Schnittstelle ersetzt [vgl. DICK97]. Java-Applet JDBC-Treiber-Manager Native API Partitial Java-Treiber Native API Datenbankserver Abbildung 7: JDBC-Architektur mit Typ-2-Treibern Diese Architektur besitzt folgende Eigenschaften: • Es werden keine ODBC-Treiber benötigt. • Native Bibliotheken und JDBC-Treiberschnittstellen müssen lokal auf dem Client installiert sein. 10 • Alle Prozesse mit Ausnahmen derer des Datenbank-Servers laufen auf dem Client ab. JDBC-Treiber-Architektur mit Typ-3-Treibern Bei Treibern vom Typ 3 ist eine Middleware zwingend erforderlich. Auf dem Client wird keine nativer Code verwendet, dieser ist auf den Middle-Tier-Server ausgelagert. Die Verbindung von Middleware und Datenbank wird über native Datenbank-Bibliotheken oder alternativ durch ODBC realisiert [vgl. DICK97]. Java-Applet JDBC-Treiber-Manager Net Protocol All Java-Treiber Middle-Tier (ODBC oder Native API) Datenbankserver Abbildung 8: JDBC-Architektur mit Typ-3-Treibern Folgende Eigenschaften kennzeichnen diese Architektur: • Es handelt sich immer um eine 3-stufige Architektur. • Auf dem Client werden keinerlei native Komponenten benötigt, weshalb auch sogenannte Thin-Clients Verwendung finden können. Alle nativen Komponenten werden auf der Middleware installiert. • Prozesse können geeignet zwischen Client und Middleware aufgeteilt werden. Kriterien für die Aufteilung können bespielsweise die Performance von Client und Server sein. JDBC-Treiber-Architektur mit Typ-4-Treibern Diese Architekur beinhaltet keinen nativen Code, alle Komponenten sind in Java implementiert. Der Zugriff auf die Datenbank erfolgt über deren originäre Schnittstelle. Die Hauptlast liegt beim Client, sofern eine 2-stufige Architektur, wie in Abbildung 9 darstellt, gewählt wird [vgl. DICK97]. Somit läßt sich die Architektur wie folgt charakterisieren: • Es werden keine ODBC-Treiber benötigt. • Alle Komponenten sind in Java implementiert, so daß Applets diese über das Netz laden können. Vorinstallationen auf dem Client entfallen. • Alle Prozesse werden mit Ausnahme derer des Datenbank-Servers beim Cleint ausgeführt. 11 Java-Applet JDBC-Treiber-Manager Net Protocol All Java-Treiber Datenbankserver Abbildung 9: JDBC-Architektur mit Typ-4-Treibern Kommunikation im Web-basierten Data-Warehouse Die im folgenden diskutierte Form der Kommunikation stellt nur eine mögliche Alternative unter vielen dar. Die oben beschriebenen JDBC-Treiber-Architekturen lassen viele Alternativen zu. Betrachtet man nochmals die im Abschnitt „2- und 3-stufige Architekturen“ genannten Vor- und Nachteile 2- bzw. 3-stufiger Architekturen unter dem eingrenzenden Aspekt der Web-Tauglichkeit einer Data-Warehouse-Anwendung, so bietet sich die 3-stufige Architekur als geeignetste Lösung an. • Nativer Code auf dem Client ist nicht notwendig. • Client-seitige Installationen bezüglich Datenbank-Treibern sind nicht erforderlich, unabhängig vom gewählten JDBC-Treiber. Für Web-basierte Anwendungen im Internet ist dies ein großer Vorteil. • Für die Clients entsteht keinerlei Administrationsaufwand, da alle benötigten Komponenten über das Netz geladen werden können. • Die Architektur bietet ein hohes Maß an Flexibilität. Dies geht soweit, daß die Client-Applikation vollkommen von der Datenbank abstrahieren kann, so daß der Wechsel der Datenbank und/oder der JDBC-Treiber keinerlei Einfluß auf die Client-Anwendung besitzt. Dies ist insbesondere unter Wartungsaspekten vorteilhaft. • Durch die Aufteilung der Komponenten, kann die Netzlast in Richtung des Clients gering gehalten werden. Das Applet braucht keine Datenbanktreiber oder ähnliches zu laden. Somit sind auch langsame Netzwerkverbindungen, wie z.B. Modemverbindungen, noch für eine effiziente Data-Warehouse-Nutzung geeignet. • Die Client-Anwendung, das Applet, kann vergleichsweise schlank implementiert werden, da die Zugriffsmechanismen auf die Datenbank auf die Middleware ausgegliedert werden können. Somit ist diese Architektur auch für Thin-Clients geeignet. Nachteilig wirkt sich lediglich der gegenüber einer 2-stufigen Architektur erhöhte Komplexitätsgrad aus, der die Entwicklung und Fehlerbeseitigung komplizierter werden läßt. 12 Client Applet HTTP RMI Middle-Tier (Java-Server) WWW-Server JDBC-Connection Data Warehouse DB-Server Abbildung 10: Kommunikation in einem Web-basierten Data-Warehouse bei 3-stufiger Architektur Die Kommunikation zwischen dem Client und dem WWW-Server findet, wie in Abschnitt „Das World Wide Web“ dargestellt, über das HTTP-Protokoll statt. Der Übergang zu Java erfolgt über Applets, die gemeinsam mit den HTML-Dokumenten übertragen werden. Das Applet stellt den eigentlichen Client in Bezug auf die Data-Warehouse-Anwendung dar, der dann seinerseits mittels Sockets oder RMI1 mit dem Application-Server kommuniziert. Der Application-Server beinhaltet die JDBC-Komponenten, die dann je nach Wahl des Treibertyps, mit dem Datenbankserver kommunizieren. Ob zur Realisierung dieser Architektur auf der Server-Seite ein, zwei oder mehr Maschinen eingesetzt werden, ist abhängig von der Systembelastung und der Leistungsfähigkeit der dort eingesetzten Maschine(n). Bei komplexen Systemen erscheint es sinnvoll, einen Rechner als Java-Server (Application-Server) und einen Rechner als Datenbank-Server einzusetzen. Der WWW-Server kann sich zusätzlich auf dem Datenbank-Server befinden. Die Aufteilung des Systems auf mehr Maschinen ist theoretisch denkbar, logische Gründe, die dies zwingend erfordern, gibt es aber nicht. Eine weitere Verteilung ist vielmehr eine Frage der Leistungsfähigkeit der Server und der gewünschten Ausfallsicherheit des Systems. Insbesondere letztere kann sogenannte Fall-BackServer, die den Ausfall eines anderen Server kompensieren, in dem sie dessen Aufgaben übernehmen, erfordern. JDBC in der Praxis Grundlagen der Verwendung von JDBC JDBC bietet eine reichhaltige Anzahl an Klassen und Schnittstellen, um mit Datenbanken zu kommunizieren, so daß diese hier nicht erschöpfend vorstellt und diskutiert werden können. Die folgenden Darstellungen konzentrieren sich daher auf die notwendigsten Kenntnisse zu JDBC, die für die Realisierung eines Web-basierten Data-Warehouses nötig sind. Die Driver-Schnittstelle JDBC stellt zwei verschiedene Objekte zur Verwaltung von Datenbanktreibern bereit. Das erste Objekt ist die Driver-Schnittstelle. 1 Andere Formen der Kommunikation, z.B. CORBA, sind möglich. Diese werden hier aber nicht betrachtet, da sich die Darstellung auf Java konzentriert. 13 Die Driver-Schnittstelle bietet verschiedene Methoden zur Ermittlung von Informationen über den aktuellen Datenbanktreiber sowie die Bereitstellung der connect()-Methode, die ein Connection-Objekt erzeugt, welches für den Datenbank-Zugriff genutzt werden kann. Die Driver-Schnittstelle bietet folgende Methoden an [vgl. HOBB97, SUN98]: Methode Beschreibung acceptsURL() Gibt einen Booleschen Wert zurück, der anzeigt, ob der Treiber eine Verbindung zur gewünschten Datenbank, die mittels des URL spezifiziert wurde, aufbauen konnte. connect()2 Erstellt eine Verbindung zur Datenbank und gibt ein Connection-Objekt zurück, das in der Anwendung für die weiteren Operationen auf der Datenbank genutzt wird. getMajorVersion() Ermittelt die Hauptversion des Treibers. getMinorVersion() Ermittelt die Unterversion des Treibers. getPropertyInfo()3 Ermittelt die Eigenschaften, die ein Anwender haben muß, um mit dem aktuellen Treiber eine Verbindung zur Datenbank einrichten zu können. jdbcComplient() Gibt einen Booleschen Wert zurück, welcher anzeigt, ob das aktuelle Driver-Objekt JDBC-konform ist, also der von Sun definierten Schnittstellenbeschreibung folgt. Tabelle 2: Methoden der Driver-Schnittstelle Das DriverManager-Objekt Die Klasse DriverManager stellt Dienste für die Verwaltung von Driver-Objekten bereit. Hierzu wird auf die Systemeigenschaft jdbc.driver zurückgegriffen. Damit können für verschiedene Anwendungen unterschiedliche Treiber zur Laufzeit spezifiziert werden. Die folgende Tabelle zeigt die Methoden des DriverManager-Objekts [vgl. HOBB97, SUN98]: Methode 2 3 Beschreibung deregisterDriver() Entfernt ein Driver-Objekt aus der Liste der Treiber. getConnection() Richtet eine Verbindung zur Datenbank ein. getDriver() Ermittelt ein Driver-Objekt, das eine Verbindung zum angegebenen URL herstellt. getDrivers() Gibt ein Array mit allen aktuell beim Manager registrierten Driver-Objekten zurück. Diese Methode kann zum Aufbau einer Datenbankverbindung genutzt werden, obwohl die Methode getPropertyInfo() nicht immer unterstützt wird. (siehe Fußnote zur Methode getPropertyInfo()). getPropertyInfo() wird nicht immer unterstüzt, beispielsweise vom ODBC-Treiber für Access 97. Bei den hier verwendeten Entwicklungsumgebungen konnte folgendes festgestellt werden. Das SDK 3.0 für Java von Microsoft liefert trotz mangelnder Unterstützung des Treibers keine Ausnahme, es wird lediglich ein leeres Feld mit Eigenschaften geliefert. Das JDK 1.1.6 von Sun liefert jedoch eine entsprechende Ausnahme, die die fehlende Unterstützung des Treibers bemängelt. 14 Methode Beschreibung getLoginTimeout() Ermittelt, wie lange ein Treiber auf die Etablierung einer Verbindung wartet. getLogStream() Ermittelt den vom Manager verwendeten Log-Stream. println() Stellt den angegebenen String in den Log-Stream. registerDriver() Registriert den angegebenen Treiber beim Manager. setLoginTimeout() Bestimmt, wie lange der Treiber auf die Etablierung einer Verbindung warten soll. setLogStream() Setzt den Log-Stream. Tabelle 3: Methoden des DriverManager-Objekts Verbindung zur Datenbank: Die Connection-Schnittstelle Das Connection-Objekt ist das wichtigste Objekt für die Einrichtung einer DatenbankVerbindung. Die Connection-Schnittstelle von JDBC stellt Methoden für die Verwaltung der Transaktionsverarbeitung, zum Erzeugen von Objekten für die Ausführung von SQL-Anweisungen sowie zum Erstellen von Objekten für die Ausführung von Stored Procedures bereit. Darüber hinaus bietet sie grundlegende Methoden zur Behandlung von Fehlern, die in Verbindung mit der Datenbank-Kommunikation auftreten können. Ein Connection-Objekt kann sowohl mit den Methoden der Driver-Klasse, als auch mit dem DriverManager erzeugt werden (siehe oben und Beispiele im Anhang). Die folgende Tabelle zeigt die Methoden des Connection-Objekts [vgl. HOBB97, SUN98]: Methode 4 Beschreibung clearWarnings() Löscht aktuelle Warnungen bezüglich der Verbindung. close() Schließt eine Datenbank-Verbindung. commit() Veranlaßt ein COMMIT. – Nur sinnvoll, wenn AutoCommit auf false steht. createStatement() Erzeugt ein Statement-Objekt, welches zur Ausführung von SQLs verwendet werden kann. getAutoCommit() Gibt den Status von AutoCommit wieder (true oder false). getCatalog()4 Gibt eine String zurück, der den aktuellen Katalog der Verbindung wiedergibt. getMetaData() Dient zur Ermittlung von Meta-Informationen zur Verbindung. getTransactionIsolation() Gibt den Isolation-Level/Modus einer Transaktion für die aktuelle Verbindung zurück. Diese Methode kann nur auf Datenbanken angewendet werden, die das Konstrukt des Kataloges unterstützen (z.B. dbAnywhere). Oracle besitzt beispielsweise keine Kataloge. 15 Methode Beschreibung getWarnings() Gibt die aktuellen Warnungen zurück. isClosed() true, wenn die Verbindung geschlossen wurde, sonst false. isReadOnly() Gibt true zurück, wenn die Verbindung Read-Only ist oder nicht aktualisiert werden kann, sonst false. nativeSQL() Gibt SQL-Anweisungen so zurück, wie sie der JDBC-Treiber der Datenbank präsentiert. prepareCall() Gibt eine CallableStatement-Objekt zurück, welches zur Ausführung gespeicherter Prozeduren verwendet wird. prepareStatement() Gibt eine PreparedStatement-Objekt zur Ausführung dynamischer SQL-Anweisungen zurück. rollback() Bewirkt einen Rollback. – Nur sinnvoll, wenn AutoCommit auf false gesetzt ist, da sonst nicht möglich bzw. wirkungslos. setAutoCommit() Setzt den Status/Modus für AutoCommit. setCatalog()5 Setzt den aktuelle Katalog für die Verbindung. setReadOnly() Setzt eine Verbindung in den Modus Read-Only, d.h. es sind keine Aktualisierungen erlaubt. setTransactionIsolation() Setzt den Isolation-Level für eine Transaktion der aktuellen Verbindung. Tabelle 4: Methoden der Connection-Schnittstelle Projekt: Abenteuer und Freizeit Projektbeschreibung Im folgenden soll anhand einer prototypischen Web-Anwendung die bislang erläuterte Theorie umgesetzt werden. Als Beispiel soll ein Outdoor-Unternehmen, „Abenteuer und Freizeit“, dienen, welches in verschiedenen Ländern Niederlassungen unterhält. Innerhalb eines Applets sollen die Umsatzdaten (Umsatz, Kosten) für alle Produkte gemäß einem Land oder mehrerer zu spezifizierender Länder ausgewertet werden. Die Anwendung wird bewußt einfach gehalten, es wird lediglich eine Abfragemöglichkeit realisiert, bei der Umsätze und Kosten bezüglich eines oder mehrerer interessierender Länder vom Anwender spezifiziert werden können. Die Darstellung der Abfrageergebnisse erfolgt in Form einer einfachen Liste, die die zurückgelieferten Datensätze enthält. Graphische Features werden nicht implementiert. Damit erfüllt der Prototyp zwar nicht die Anforderungen an Data-WarehouseAnwendungen, die wesentlichen Charakteristika der Implementierung einer entsprechenden Anwendung werden aber deutlich. Um eine „echte“ Data-Warehouse-Anwendung daraus zu entwikkeln, müssen lediglich die Abfrage-Möglichkeiten sowie die Ergebnisaufbereitung erweitert werden. Die wesentlichen Komponenten zur Realisierung einer 3-stufigen Architektur sind vorhanden. Um die Flexibilität der Architektur zu testen, soll die Anwendung mit verschiedenen Datenbanken, ORACLE 7.3.3 und Microsoft Acces 976, realisiert und getestet werden. 5 Siehe Fußnote 4 16 Anwendungsarchitektur „Abenteuer und Freizeit“ realisiert aufgrund der herausgearbeiten Vorteile eine 3-stufige Anwendungsarchitektur (siehe Abbildung 10: Kommunikation in einem Web-basierten Data-Warehouse bei 3-stufiger Architektur, Seite 13). Das Frontend, das Applet, kann so unabhängig von der verwendeten Datenbank implementiert werden. Treiber für die Datenbank sind somit auf der Seite des Clients nicht erforderlich, müssen also weder vorinstalliert noch zur Laufzeit geladen werden. Das Frontend kann damit auf jedem Rechner, der über eine entsprechende TCP/IP-Verbindung zum Web-Server und einen Java-fähigen Web-Browser verfügt, ausgeführt werden. Die Netzwerkbelastung ist aufgrund der Unabhängigkeit von der Datenbank und eventuell damit verbundenen Treibern minimal. Abfragen richtet das Applet via RMI an einen ebenfalls in Java realisierten Applikations-Server. Dieser verfügt über die notwendigen JDBC-Treiber, die zur Kommunikation mit der Datenbank erforderlich sind. Er generiert das erforderliche SQL-Statement, schickt dieses an die Datenbank und bereitet das Resultat auf. Das aufbereitete Ergebnis wird dann an das anfordernde Applet versendet. Der Applikations-Server ist so gestaltet, daß für jede Anfrage ein einzelner Thread gestartet wird. Der Server ist somit Mehrbenutzer-fähig. Wenn verschiedene Anwender Anfragen an den Server schicken, so bearbeitet dieser alle Anfragen parallel, so daß kein Anfrager auf die Abarbeitung einer Anfrage eines anderen Anwenders warten muß. Relationales Datenmodell Das Datenmodell ist entspricht einem (sehr) einfachen Star-Schema, welches auch komplexere Auswertungen zuläßt, als sie im Applet des Prototypen realisiert werden. Abbildung 11: Datenbank-Design des Prototypen „Abenteuer und Freizeit“ Die Tabelle UMSAETZE stellt die Faktentabelle dar. Sie ist insoweit denormalisiert, als sie die Zeitdimension mit der Hierarchie BESTELLDATUM-MONAT-QUARTAL-JAHR enthält. Als weitere hierarchische Dimensionen sind LAENDER (LAND-REGION) und PRODUKTE (PRODUKT-PRODUKTREIHE-PRODUKTTYP) realisiert. Wie bereits oben erläutert, macht der implementierte Prototyp von den Auswertungsmöglichkeiten, 3-dimensionale Analyse über unterschiedliche Aggregationsstufen mit entsprechenden Drill-Ups/Downs, des Datenmodells keinen Gebrauch. Java-Klassen-Design Das Klassen-Design ist entsprechend den geringen Anforderungen einfach gehalten. Das Applet kommuniziert mit dem Java-Server, welcher für jede Anfrage einen Thread (AUFInfoThread) generiert. 6 Microsoft Access 97 ist zwar keine Server-Datenbank, ermöglicht aber die Demonstration des Prototypen in der Seminarveranstaltung, weshalb es hier als Datenbank verwendet wird. 17 Für die Kommunikation mit der Datenbank wurde die Klasse DBConnection implementiert, die zum einen den Aufbau, Abbau der Datenbankverbindung und die notwendige Kommunikation mit dieser realisiert, zum anderen aber auch mittels der Klasse Protocol alle Aktivitäten aufzeichnet. Letzteres ist insbesondere in der Entwicklungs- und Test-Phase eine nicht zu unterschätzende Hilfe bei der Fehlersuche. Die folgende Abbildung zeigt die implementierten Klassen in Zusammenhang mit der relaisierten Architektur: AUFApplet.class RMI HTTP AUFServerImp.class AUFInfoThread.class WWW-Server Protokoll DBConnection.class Protocol.class JDBC-Connection = Java Data Warehouse DB-Server Abbildung 12: Klassendesign im Zusammenhang mit der Architektur des Prototypen Bewertung des Prototypen Im Vergleich zur Realisierung einer Data-Warehouse-Anwendung mit den üblichen (nicht Webfähigen) Werkzeugen, übersteigt der Aufwand der hier vorgestellten Java-Implementierung den ersteren um ein vielfaches, ohne auch nur annähernd die Funktionalitäten erreichen zu können. Der Grund hierfür liegt in der (noch) mangelnden Unterstützung Data-Warehouse-spezifischer Konstrukte wie z.B. Kreuztabellen, Drill-Up und Drill-Down und ähnlichem mehr. Würden diese in Form von Bibliotheken zur Verfügung stehen, so könnte der Entwicklungsaufwand drastisch verringert werden. Dennoch bleibt zu erwarten, daß solange keine echte Werkzeug-Unterstützung (z.B. durch entsprechende CASE-Tools) gegeben ist, die Entwicklungsaufwendungen und somit die Kosten vergleichsweise hoch bleiben werden. Inwieweit dies ökonomisch vertretbar ist, muß im Einzelfall anhand vergleichender Kalkulationen ermittelt werden. Mangels „gleicher“ Anwendungen, die mit unterschiedlichen Technologien entwickelt wurden, kann eine Bewertung der Performance nicht durchgeführt werden. Ein mit PowerPlay von der Firma Cognos, Inc. entwickelter Prototyp, der zudem noch deutlich mehr an Funktionalität bietet (Traversieren der Hierarchien, wahlfreie Selektion interessierender Attribute, verschiedene graphische Auswertungen etc.) arbeitet jedoch erheblich schneller7. Die Ergebnisaufbereitung wird unterhalb einer Sekunde erreicht, während der hier vorgestellte Prototyp etwa 3 Sekunden für eine einfache Abfrage benötigt. Dieser Vergleich ist jedoch insofern unfair, als die PowerPlay-Variante nur im Intranet eingesetzt werden kann, eine Verwendung in Zusammenhang mit dem Web ist nicht möglich. Weiterhin wäre die Leistung des Prototypen steigerbar, wenn nicht nach jeder Abfrage die Verbindung zur Datenbank abgebaut und somit bei jeder neuen Anfrage erneut aufgebaut werden müßte. ORFALI und HARKEY, die ausgedehntere Experimente durchgeführt haben, bescheinigen der 3-stufigen Architektur mit Java aber eine sehr gute Performance, die anderen Implementierungstechnologien, insbesondere CGI, überlegen ist. Allerdings empfehlen sie den Einsatz von CORBA statt RMI, da CORBA schneller ist. Sie weisen aber darauf hin, daß Java noch nicht aus- 7 Screenschot siehe Anhang S. 38 18 gereift genug ist, um diesen Vergleich als entgültig betrachten zu können. Zukünftige JavaEntwicklungen können hier noch aufholen [vgl. ORF98]. Eines hat sich aber gezeigt, durch die Wahl der 3-stufigen Architektur konnte bei der Implementierung des Frontends komplett von der zugrundeliegenden Datenbank abstrahiert werden. Der Application-Server konnte durch Modifikation des verwendeten JDBC-Treibers sowie der Anpassung des URL ohne weitere Änderungen an die verwendeten Datenbanken angepaßt werden. Durch eine elegantere Implementierung, durch Verwendung von Kommandozeilenparametern oder Properties, kann eine Sourcecode-Modifikation sogar komplett vermieden werden. Durh die Wahl von Java für die Implementierung ist das System Plattform-unabhängig und Webtauglich. Dies wäre mit den klassischen Werkzeugen, sieht man von den aktuell auf dem Markt erscheinenden ab, die ebenfalls auf Java basieren, nicht möglich gewesen. Schlußbetrachtung und Ausblick Die Diskussion über die Anwendungs-Architekturen hat deutlich gezeigt, daß 3-stufige Architekturen für Data-Warehouse-Applikationen am geeignetsten sind. Java unterstützt die Architektur-Form hervorragend, da alle notwendigen Klassen, z.B. für die Netzwerkkommunikation, zum JavaStandard gehören. Dank der Implementierung in Java könen Data-Warehouse-Anwendungen unabhängig von Rechnerplattformen entwickelt werden. Durch den Einsatz von Web-Technologie entfällt das Distributionsproblem der Anwendungen. Allerdings ist der Implementierungsaufwand nicht unerheblich. Insbesonder die Realisierung graphischer Benutzeroberflächen ist außerordentlich aufwendig. In Bezug auf die im DataWarehousing gewünschte Flexibilität der Datenauswahl, -auswertung und -aufbereitung fehlt es an geigneten Klassen und Konstrukten. Ohne eine entsprechende Klassenbibliothek muß alles notwendige von Hand implementiert werden. Für größere Projekte oder Firmen, die Data-Warehouse-Lösungen in Form von Dienstleistungen erstellen, ist es somit anzuraten, eine Bibliothek zu entwickeln oder anzuschaffen, die über die im Data-Warehousing erforderlichen Klassen verfügt. Ansonsten besteht die Gefahr, daß die erhöhten Entwicklungskosten die genannten Vorteile überkompensieren, so daß der Einsatz von Java ökonomisch nicht mehr sinnvoll ist. Derzeit werden derartige Bibliotheken auf dem Markt nicht angeboten, so daß Eigenentwicklungen erforderlich sind, die sich aber nur für Beratungsfirmen oder ähnliches rentieren dürften. Hier besteht noch Handlungsbedarf seitens der Software- bzw. Werkzeug-Anbieter. Die Verwendung von Web-Clients macht zudem eine Integration unternehmens-externer Informationsnachfrager möglich. Dies war bislang nur eingeschränkt möglich, da die bisherige ClientSoftware den externen Informationsnachfragern nur selten zur Verfügung stand. Insgesamt bleibt festzuhalten, das Java bzw. Werkzeuge, die Java nutzen, interessante Perspektiven für die Entwicklung von Data-Warehouse-Anwendungen bieten. Sie eröffnen die Möglichkeit, ein und dieselbe Anwendung sowohl intern (Intranet) als auch extern (Internet) verfügbar und nutzbar zu machen. In keinem Fall sind Vorinstallationen auf den Clients erforderlich, so daß die bislang existierende Distributionsproblematik der Software entfällt. Literaturverzeichnis [BERG96] Bergmann, U.: WWW – Anbieten und Nutzen, Hanser Verlag, 1996 [DICK97] Dicken, Hans: JDBC – Internet-Datenbankanbindung mit Java, Thomson Publishing, 1997 [EVAN95] Evans, C., Lacey, D., Harvey, D., Gibbons, D., Krasum, D.: Client/Server, Prentice Hall, 1995 19 [ORFA98] Orfali, R., Harkey, D.: Client/Server Programming with Java and CORBA, John Wiley & Sons, Inc., Second Edition, 1998 [HOBB97] Hobbs, A.: Teach Yourself Database Programming with JDBC in 21 Days, SAMS Publishing, 1997 [KIMB96] Kimball, R.: The Data Warehouse Toolkit, John Wiley & Sons, Inc. 1996 [ORAC97] unbekannter Verfasser: Online-Dokumetation zu ORACLE 7.3.3, 1997 [SUN98] unbekannter Verfasser: HTML-Dokumentation zum JDK 1.1.6 von Sun Microsystems, Inc., 1998 [THIE98] Thiemann, Uwe: Das Wesentliche an den Daten, erschienen in Byte, August 1998, S. 64-71. [WHIT98] White, Colin: Information at your Browser-Click, erschienen in Byte, August 1998, S. 57-63. Anhang Hinweise zu den Programmbeispielen Neben dem Prototypen, „Abenteuer und Freizeit“, werden im folgenden werden 2 Anwendungen vorgestellt, die die Kommunikation über Sockets und RMI demonstrieren. Die Anwendungen zur Verwendung von JDBC sowie zum Web-basierten Data-Warehouse verwenden die JDBC-ODBCBridge in Verbindung mit Microsoft Access 97. Durch Abwandlung der verwendeten JDBCTreiber sowie den damit verbundenen Änderungen an den URLs können die Beispiele aber mit beliebigen Datenbanken, für die JDBC-Treiber vorliegen, genutzt werden. 20 Client-/Server-Kommunikation via Sockets Das folgende Beispiel demonstriert die Netzwerk-Kommunikation über Sockets. Ein Applet schickt eine Zeichenkette an einen Server, welcher als Antwort die empfangene Zeichenkette in modifizierter Form an das Applet zurücksendet. SocketServer.java /* * SocketServer * * Einfaches Beispiel für einen Socket-Server * * Copyright (c) 1998 by Karsten Brodmann */ import java.io.*; import java.net.*; class SocketServer { public static void main(String args[]) throws IOException { int timeout = 2000; // Timeout nach 2000 Sekunden int port = 1234; // irgendein freier Port Socket socket; // Socket-Instanz // ein paar Statusmeldungen String gestartet = "Socket-Server gestartet..."; String warte = "... lausche auf Port: " + port + "."; ServerSocket serversocket = new ServerSocket(port, timeout); System.out.println(gestartet); System.out.println(warte); // ...warten auf eine Client-Anfrage via Socket while (true) { socket = serversocket.accept(); // I/O-Streams akzeptieren try { PrintWriter out = new PrintWriter(socket.getOutputStream()); BufferedReader in = new BufferedReader(new InputStreamReader( socket.getInputStream())); String anfrage = in.readLine(); // Client hat Verbindung aufgenommen out.println("Anfrage:" + anfrage); out.flush(); // Verbindung schließen socket.close(); } catch (IOException ioex) { System.err.println("IOException: " + ioex); } } } } SocketClient.java /* * SocketClient * * Einfaches Beispiel für einen Socket-Client * * Copyright (c) 1998 by Karsten Brodmann (22.12.1998) */ import java.applet.*; import java.awt.*; import java.io.*; import java.net.*; import SocketClientFrame; import DlgSocketClient; 21 public class SocketClient extends Applet { DlgSocketClient String int Socket BufferedReader PrintWriter ctrls; host, anfrage, antwort; port; socket = null; in; out; // auf Senden-Button reagieren public boolean action(Event evt, Object objekt) { if (objekt.equals("Senden")) { // prüfen, ob alle Felder ausgefüllt sind... if ((!ctrls.IDC_SERVER.getText().equals("")) && (!ctrls.IDC_PORT.getText().equals("")) && (!ctrls.IDC_ANFRAGE.getText().equals(""))) { host = ctrls.IDC_SERVER.getText(); port = java.lang.Integer.parseInt( ctrls.IDC_PORT.getText()); anfrage = ctrls.IDC_ANFRAGE.getText(); // Anfrage via Socket versenden und empfangen try { socket = new Socket(host, port); in = new BufferedReader( new InputStreamReader( socket.getInputStream())); out = new PrintWriter( socket.getOutputStream()); // Anfrage senden und Anwort abwarten out.println(anfrage); out.flush(); antwort = in.readLine(); ctrls.IDC_ANTWORT.setText(antwort); } catch(UnknownHostException uex) { ctrls.IDC_ANTWORT.setText("unknown host"); } catch(IOException ioex) { ctrls.IDC_ANTWORT.setText("IO-Error"); } } // ... sonst Aufforderung diese auszufüllen else ctrls.IDC_ANTWORT.setText( "Bitte alle Felder ausfüllen"); return true; } // Ereignis wurde nicht behandelt return false; } private Thread m_SocketClient = null; // STANDALONE APPLICATION SUPPORT: // m_fStandAlone will be set to true if applet is run standalone //-------------------------------------------------------------------------private boolean m_fStandAlone = false; public static void main(String args[]) { SocketClientFrame frame = new SocketClientFrame("SocketClient"); // Must show Frame before we size it so insets() will return valid values //---------------------------------------------------------------------frame.show(); frame.hide(); frame.resize(frame.insets().left + frame.insets().right + 400, frame.insets().top + frame.insets().bottom + 200); SocketClient applet_SocketClient = new SocketClient(); frame.add("Center", applet_SocketClient); applet_SocketClient.m_fStandAlone = true; 22 applet_SocketClient.init(); applet_SocketClient.start(); frame.show(); } public SocketClient() { } public String getAppletInfo() { return "Name: SocketClient\r\n" + "Author: Karsten Brodmann\r\n" + "Created with Microsoft Visual J++ Version 1.1"; } public void init() { //resize(232, 102); ctrls = new DlgSocketClient (this); ctrls.CreateControls(); } public void destroy() { } public void paint(Graphics g) { } } SocketClientFrame.java import java.awt.*; class SocketClientFrame extends Frame { public SocketClientFrame(String str) { super (str); } public boolean handleEvent(Event evt) { switch (evt.id) { case Event.WINDOW_DESTROY: dispose(); System.exit(0); return true; default: return super.handleEvent(evt); } } } DlgSocketClient.java //-----------------------------------------------------------------------------// DlgSocketClient.java: // Implementation for container control initialization class DlgSocketClient // // WARNING: Do not modify this file. This file is recreated each time its // associated .rct/.res file is sent through the Java Resource Wizard! // // This class can be use to create controls within any container, however, the // following describes how to use this class with an Applet. For addtional // information on using Java Resource Wizard generated classes, refer to the // Visual J++ 1.1 documention. // // 1) Import this class in the .java file for the Applet that will use it: 23 // // import DlgSocketClient; // // 2) Create an instance of this class in your Applet's 'init' member, and call // CreateControls() through this object: // // DlgSocketClient ctrls = new DlgSocketClient (this); // ctrls.CreateControls(); // // 3) To process events generated from user action on these controls, implement // the 'handleEvent' member for your applet: // // public boolean handleEvent (Event evt) // { // // } // //-----------------------------------------------------------------------------import java.awt.*; import DialogLayout; public class DlgSocketClient { Container m_Parent = null; boolean m_fInitialized = false; DialogLayout m_Layout; // Control definitions //-------------------------------------------------------------------------Button IDSENDEN; Label IDC_STATIC1; Label IDC_STATIC2; Label IDC_STATIC3; Label IDC_STATIC4; TextField IDC_SERVER; TextField IDC_PORT; TextField IDC_ANFRAGE; TextField IDC_ANTWORT; // Constructor //-------------------------------------------------------------------------public DlgSocketClient (Container parent) { m_Parent = parent; } // Initialization. //-------------------------------------------------------------------------public boolean CreateControls() { // Can only init controls once //---------------------------------------------------------------------if (m_fInitialized || m_Parent == null) return false; // Parent must be a derivation of the Container class //---------------------------------------------------------------------if (!(m_Parent instanceof Container)) return false; // Since there is no way to know if a given font is supported from // platform to platform, we only change the size of the font, and not // type face name. And, we only change the font if the dialog resource // specified a font. //---------------------------------------------------------------------Font OldFnt = m_Parent.getFont(); if (OldFnt != null) { Font NewFnt = new Font(OldFnt.getName(), OldFnt.getStyle(), 8); m_Parent.setFont(NewFnt); } // All position and sizes are in Dialog Units, so, we use the // DialogLayout manager. //---------------------------------------------------------------------- 24 m_Layout = new DialogLayout(m_Parent, 232, 102); m_Parent.setLayout(m_Layout); m_Parent.addNotify(); Dimension size = m_Layout.getDialogSize(); Insets insets = m_Parent.insets(); m_Parent.resize(insets.left + size.width + insets.right, insets.top + size.height + insets.bottom); // Control creation //---------------------------------------------------------------------IDSENDEN = new Button ("Senden"); m_Parent.add(IDSENDEN); m_Layout.setShape(IDSENDEN, 169, 81, 50, 14); IDC_STATIC1 = new Label ("Server:", Label.LEFT); m_Parent.add(IDC_STATIC1); m_Layout.setShape(IDC_STATIC1, 13, 7, 29, 8); IDC_STATIC2 = new Label ("Port:", Label.LEFT); m_Parent.add(IDC_STATIC2); m_Layout.setShape(IDC_STATIC2, 13, 23, 16, 8); IDC_STATIC3 = new Label ("Anfrage:", Label.LEFT); m_Parent.add(IDC_STATIC3); m_Layout.setShape(IDC_STATIC3, 13, 39, 28, 8); IDC_STATIC4 = new Label ("Antwort:", Label.LEFT); m_Parent.add(IDC_STATIC4); m_Layout.setShape(IDC_STATIC4, 13, 60, 27, 8); IDC_SERVER = new TextField (""); m_Parent.add(IDC_SERVER); m_Layout.setShape(IDC_SERVER, 53, 7, 65, 14); IDC_PORT = new TextField (""); m_Parent.add(IDC_PORT); m_Layout.setShape(IDC_PORT, 53, 23, 22, 14); IDC_ANFRAGE = new TextField (""); m_Parent.add(IDC_ANFRAGE); m_Layout.setShape(IDC_ANFRAGE, 53, 39, 166, 14); IDC_ANTWORT = new TextField (""); m_Parent.add(IDC_ANTWORT); m_Layout.setShape(IDC_ANTWORT, 53, 60, 166, 14); IDC_ANTWORT.setEditable(false); m_fInitialized = true; return true; } } DialogLayout.java // This is a part of the Microsoft Visual J++ library. // Copyright (C) 1996 Microsoft Corporation // All rights reserved. import import import import import import import import import // // // // // // // java.util.Hashtable; java.awt.LayoutManager; java.awt.Component; java.awt.Container; java.awt.Dimension; java.awt.Rectangle; java.awt.FontMetrics; java.awt.Insets; java.awt.Label; class DialogLayout DialogLayout is a API calls "dialog coordinates which mapping from DLUs simple layout manager which works with what the Win32 logical units" (DLUs). DLUs are resolution independent work well for laying out controls on a dialog box. The to pixels is based on the font in use in the dialog box. 25 // // // // // // // // // // // // // An x-coordinate DLU is described as 1/4 (.25) of the of the average character width of the font used in the dialog. A y-coordinate DLU is described as 1/8 (.125) of the character height used in the dialog. One tricky issue to note: The average character width is not the average of all characters -rather it is the average of all alpha characters both uppercase and lowercase. That is, it is the extent of the string "a...zA...Z" divided by 52. This class allows you to associate a Rectangle (x, y, width, height) with a Component in a Container. If called upon to layout the container, this layout manager will layout based on the translation of dialog units to pixels. public class DialogLayout implements LayoutManager { protected Hashtable m_map = new Hashtable(); protected int m_width; protected int m_height; // DialogLayout methods public DialogLayout(Container parent, int width, int height) { Construct(parent, width, height); } public DialogLayout(Container parent, Dimension d) { Construct(parent, d.width, d.height); } public void setShape(Component comp, int x, int y, int width, int height) { m_map.put(comp, new Rectangle(x, y, width, height)); } public void setShape(Component comp, Rectangle rect) { m_map.put(comp, new Rectangle(rect.x, rect.y, rect.width, rect.height)); } public Rectangle getShape(Component comp) { Rectangle rect = (Rectangle)m_map.get(comp); return new Rectangle(rect.x, rect.y, rect.width, rect.height); } public Dimension getDialogSize() { return new Dimension(m_width, m_height); } // LayoutManager Methods public void addLayoutComponent(String name, Component comp) { } public void removeLayoutComponent(Component comp) { } public Dimension preferredLayoutSize(Container parent) { return new Dimension(m_width, m_height); } public Dimension minimumLayoutSize(Container parent) { return new Dimension(m_width, m_height); } public void layoutContainer(Container parent) { int count = parent.countComponents(); Rectangle rect = new Rectangle(); int charHeight = getCharHeight(parent); int charWidth = getCharWidth(parent); Insets insets = parent.insets(); FontMetrics m = parent.getFontMetrics(parent.getFont()); 26 for (int i = 0; i < count; i++) { Component c = parent.getComponent(i); Rectangle r = (Rectangle)m_map.get(c); if (r != null) { rect.x = r.x; rect.y = r.y; rect.height = r.height; rect.width = r.width; mapRectangle(rect, charWidth, charHeight); if (c instanceof Label) { // Adjusts for space at left of Java labels. rect.x -= 12; rect.width += 12; } rect.x += insets.left; rect.y += insets.top; c.reshape(rect.x, rect.y, rect.width, rect.height); } } } // Implementation Helpers protected void Construct(Container parent, int width, int height) { Rectangle rect = new Rectangle(0, 0, width, height); mapRectangle(rect, getCharWidth(parent), getCharHeight(parent)); m_width = rect.width; m_height = rect.height; } protected int getCharWidth(Container parent) { FontMetrics m = parent.getFontMetrics(parent.getFont()); String s = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; int width = m.stringWidth(s) / s.length(); if (width <= 0) width = 1; return width; } protected int getCharHeight(Container parent) { FontMetrics m = parent.getFontMetrics(parent.getFont()); int height = m.getHeight(); return height; } protected void { rect.x rect.y rect.width rect.height } mapRectangle(Rectangle rect, int charWidth, int charHeight) = = = = (rect.x (rect.y (rect.width (rect.height * * * * charWidth) charHeight) charWidth) charHeight) / / / / 4; 8; 4; 8; } 27 Remote Method Innvocation Das folgende Beispiel verwendet RMI zur Kommunikation zwischen Client und Server. Der Server lädt eine ihm als Parameter übergebene Datei als „Datenbank“ in den Hauptspeicher. Über den Client kann der Anwender dann eine Suche nach einem Datensatz innerhalb der Datenbank initiieren, der einem anzugebenden Suchmuster entspricht. Der Server schickt dann den ersten Datensatz, welcher das Suchmuster enthält, an den Client zurück. Lookup.java /* * Lookup - Interface * * Einfaches Beispiel für einen RMI-Server * * Copyright (c) 1998 by Karsten Brodmann (22.12.1998) */ import java.rmi.*; public interface Lookup extends Remote { public String findInfo(String info) throws RemoteException; } LookupServer.java /* * LookupServer - Interface * * Einfaches Beispiel für einen RMI-Server * * Copyright (c) 1998 by Karsten Brodmann (22.12.1998) */ import import import import java.io.*; java.util.*; java.rmi.*; java.rmi.server.*; public class LookupServer extends UnicastRemoteObject implements Lookup { private Vector save = new Vector(); public LookupServer(String db) throws RemoteException { // einmal "Datenbank" in den Speicher laden try { FileReader fr = new FileReader(db); BufferedReader br = new BufferedReader(fr); String s = null; while ((s = br.readLine()) != null) save.addElement(s); fr.close(); } catch (Throwable e) { System.err.println("exception"); System.exit(1); } } public String findInfo(String info) { if (info == null) return null; info = info.toLowerCase(); int n = save.size(); for (int i = 0; i < n; i++) { String dbs = (String)save.elementAt(i); if (dbs.toLowerCase().indexOf(info) != -1) return dbs; } return null; 28 } public static void main(String args[]) { try { RMISecurityManager security = new RMISecurityManager(); System.setSecurityManager(security); String db = args[0]; LookupServer server = new LookupServer(db); Naming.rebind("LookupServer", server); System.err.println("LookupServer ready..."); } catch (Throwable e) { System.err.println("exception: " + e); System.exit(1); } } } LookupClient.java /* * LookupClient - Interface * * Einfaches Beispiel für einen RMI-Client * * Copyright (c) 1998 by Karsten Brodmann (22.12.1998) */ import java.rmi.*; import java.rmi.server.*; public class LookupClient { public static void main(String args[]) { try { RMISecurityManager security = new RMISecurityManager(); System.setSecurityManager(security); String host = "trabant"; String server = "LookupServer"; String name = "rmi://" + host + "/" + server; Lookup look_obj = (Lookup)Naming.lookup(name); String results = look_obj.findInfo(args[0]); if (results == null) System.err.println("** nicht gefunden **"); else System.out.println(results); } catch (Throwable e) { System.err.println("exception: " + e); System.exit(1); } } } Abenteuer und Freizeit Der folgende Programmcode zeigt die Implementierung des im Text beschriebenen Prototypen, „Abenteuer und Freizeit“. Es wird eine 3-stufige Architektur realisiert. 29 Abbildung 13: Screenshot "Abenteuer und Freizeit" AUFServer.java /* * @(#)AUFServer.java 1.0 99/01/07 * * AUFServer - Schnittstelle zur Realisierung von Datenbankzugriffen * für die Beispielanwendung "Abenteuer und Freizeit" im * Seminar "Web-Publishing", WS 98/99 * * Copyright (c) 1999 by Karsten Brodmann */ import java.util.*; /** * AUFServer stellt die Schnittstelle zur Realisierung von Datenbankzugriffen * für die Demo-Anwendung "Abenteuer und Freizeit" im Seminar "Web-Publishing" * im WS 98/99 bei Prof. Dr. O. Vornberger dar. * <P> * @version 1.0 * @author Karsten Brodmann */ public interface AUFServer extends java.rmi.Remote { /* Methodenangebot * --------------*/ /** * Diese Methode selektiert Umsatzdaten zu den in <i>countries</i> angegebenen Ländern. * * @param usr - Login des Anwenders * @param passwd - Paßwort des Anwenders * @param countries - Vektor mit Auswahlkriterien für die Selektion * @return Vector - Ergebniszeilen, die den Auswahlkriterien entsprechen * @exception RemoteException - Verbindungsfehler */ Vector getAUFInfo(String usr, String passwd, Vector countries) throws java.rmi.RemoteException; } 30 AUFServerImp.java /* * @(#)AUFServerImp.java 1.0 99/01/07 * * AUFServerImp - Implementierung des Applikationsservers für die * Beispielanwendung "Abenteuer und Freizeit" im * Seminar "Web-Publishing", WS 98/99 * * Copyright (c) 1999 by Karsten Brodmann */ import import import import import import import java.io.*; java.net.*; java.util.*; java.sql.*; java.rmi.*; java.rmi.server.UnicastRemoteObject; java.rmi.registry.LocateRegistry; class AUFServerImp extends UnicastRemoteObject implements AUFServer { AUFServerImp() throws RemoteException {} /* * Methode zur Ermitttlung einiger Umsatzdaten anhand * ausgewählter Länder. * * @param user - Datanbank-Login des Anwenders * @param passwd - Paßwort des Anwenders * @param countries - Vector der in Frage kommenden Länder * @return Vector - Liste der Umsatzdaten */ public Vector getAUFInfo (String usr, String pwd, Vector countries) throws RemoteException { // Informationen aus der Datenbank holen. Vector result = null; AUFInfoThread aufInfoThread = new AUFInfoThread(usr, pwd, countries); // Thread erzeugen und bis zu Ende ablaufen lassen. Thread auf = new Thread (aufInfoThread); auf.start(); try { auf.join(); System.out.println ("Abfrage-Thread laeuft..."); } catch (java.lang.InterruptedException e){ e.printStackTrace(); } // Ergebnisse auswerten. result = aufInfoThread.getResults(); return result; } public static void main(String args[]) { int port = 1099; System.out.println ("AUF-Server wird gestartet..."); System.setSecurityManager (new RMISecurityManager()); try{ System.out.println ("Starte Server."); LocateRegistry.createRegistry(port); System.out.println ("Registry erzeugt..."); AUFServerImp aufs = new AUFServerImp(); System.out.println ("Server-Objekt erzeugt..."); Naming.rebind ("//:1099/AUFServerImpl", aufs); System.out.println ("Server in der Registry gebunden."); System.out.println ("Warte auf Anfragen auf Port " + port + "..."); 31 } catch (Exception e) { System.out.println ("Fehler in AUF-Server" + e); e.printStackTrace(); } } } AUFInfoThread.java /* * @(#)AUFInfoThread.java 1.0 99/01/07 * * AUFInfoThread - Klasse zur Durchführung von Abfragen in einer * Mehrbenutzerumgebung. Je Anfrage wird ein * separater Thread erzeugt. * * Beispielanwendung "Abenteuer und Freizeit" im * Seminar "Web-Publishing", WS 98/99 * * Copyright (c) 1999 by Karsten Brodmann */ import java.io.*; import java.sql.*; import database.DBConnection; import java.util.*; class AUFInfoThread implements Runnable { String usr; String pwd; Vector countries; Vector aufVector; /* * Einfacher Konstruktor, erzeugt ein neues Objekt. * * @param usr - Login * @param pwd - Paßwort * @param countries - Liste der gewünschten Länder */ public AUFInfoThread(String usr, String pwd, Vector countries) { this.usr = usr; this.pwd = pwd; this.countries = countries; } /* * Ueberdefinieren der Standard-run()-Methode. */ public void run(){ try { // Datenbank-Verbindung herstellen. DBConnection dbc = new DBConnection("jdbc:odbc:AUF", usr, pwd, "com.ms.jdbc.odbc.JdbcOdbcDriver", true); if (dbc.getConnection() != null) { Enumeration countriesEnum = countries.elements(); String whereClause = " WHERE LAND IN ("; while (countriesEnum.hasMoreElements()) { whereClause = whereClause + "'" + countriesEnum.nextElement() + "'"; if (countriesEnum.hasMoreElements()) { whereClause = whereClause + ","; } } whereClause = whereClause + ")"; // Abfrage vervollständigen und abschicken. Statement stmnt = dbc.getConnection().createStatement(); ResultSet rs; 32 rs = stmnt.executeQuery ("SELECT * FROM UMSAETZE_JE_LAND_UND_PRODUKT" + whereClause); // Anfrageergebnisse auswerten. Vector resultVec = handleResult (rs); this.aufVector = resultVec; dbc.close(); } else { System.out.println ("Benutzer " + usr + " versuchte erfolglos, eine SQL-Anweisung " + "auszufuehren."); } } catch (ClassNotFoundException clnfex) { System.out.println ("Klasse nicht gefunden - CLASSPATH pruefen."); } catch (SQLException sqlex) { System.out.println ("Benutzer " + usr + " hat erfolglos versucht, " + "eine Datenbank-Verbindung herzustellen."); } } /* * Auslesen der Anfrageergebnisse * * @return Vector - Liste der Umsatzdaten */ public Vector getResults (){ return this.aufVector; } /* * Auslesen des ResultSets in einen Vector. */ private Vector handleResult (ResultSet rs) { String recentRow = ""; Vector resVector = new Vector(1,1); String hersteller; String modell; String baujahr; System.out.println("Werte Resultset aus..."); try { boolean more = rs.next(); while (more) { recentRow = recentRow + rs.getString (1) + ", "; recentRow = recentRow + rs.getString (2) + ", "; recentRow = recentRow + "U: "; recentRow = recentRow + rs.getString (3); recentRow = recentRow + ", K: "; recentRow = recentRow + rs.getString (4); resVector.addElement(recentRow); recentRow = ""; more = rs.next(); } } catch (SQLException e){ e.printStackTrace(); } return resVector; } } AUFApplet.java /* * @(#)AUFApplet.java 1.0 99/01/07 * * AUFApplet - Frontend für Beispielanwendung * "Abenteuer und Freizeit" im * Seminar "Web-Publishing", WS 98/99 33 * * Copyright (c) 1999 by Karsten Brodmann */ import import import import import java.awt.*; java.util.*; java.io.*; java.net.*; java.rmi.*; public class AUFApplet extends java.applet.Applet { AUFServer as = null; /* * Standard init() Methode durch SuperCede ueberdefiniert. */ public void init() { SuperCedeInit(); } /* * Zurücksetzen aller Komponenten. */ protected void removeSelections() { usrField.setText(""); pwdField.setText(""); int numberOfCountries = countryList.countItems(); for (int i=1;i<=numberOfCountries;i++) { if (countryList.isSelected(i)) { countryList.deselect(i); } } resultArea.appendText("Eingabe zurückgesetzt.\n"); } /* * Button "Eingabe zurücksetzen" wurde geklickt. */ protected void ResetButtonClicked( Event event ) { // Button 'Eingabe zurücksetzen' geklickt. // Zuruecksetzen aller Komponenten. removeSelections(); } /* * Button "Anfrage abschicken" wurde geklickt. */ protected void OkButtonClicked( Event event ) { // mind. ein Land ausgewählt? int numberOfCountryItems = countryList.countItems(); int numberOfSelectedItems = 0; for (int i=0;i<=numberOfCountryItems;i++) { // 1-0 if (countryList.isSelected(i)) numberOfSelectedItems++; } // Vollständigkeit der User-Daten prüfen if (usrField.getText().equals("") || pwdField.getText().equals("")) resultArea.appendText ("Bitte Login und Paßwort vollständig " + "eingeben.\n"); else if (numberOfSelectedItems < 1) resultArea.appendText ("Bitte mindestens ein " + "Land auswählen.\n"); else { // Angaben vollständig => Daten aufbereiten Vector selectedCountries = new Vector (1,1); int numberOfCountries = countryList.countItems(); for (int k=0;k<=numberOfCountries;k++) { // 1-0 if(countryList.isSelected(k)) { 34 selectedCountries.addElement(countryList.getItem(k)); } } String login = usrField.getText(); String password = pwdField.getText(); // Verbindung zum Server herstellen. resultArea.setText ("Stelle Verbindung zum Java-DB-Server her. " + "Bitte etwas Geduld...\n"); try { // Instanz der Remote-Klasse erzeugen. as = (AUFServer)Naming.lookup("rmi://" + getCodeBase().getHost() + "/AUFServerImpl"); resultArea.setText("Server gefunden, rufe nun Informationen aus " + "der Datenbank ab...\n"); // Daten vom Server abrufen Vector queryResult = new Vector (1,1); queryResult = as.getAUFInfo (login, password, selectedCountries); //Anfrageergebnisse ausgeben. resultArea.setText("\n"); Enumeration resEnum = queryResult.elements(); while (resEnum.hasMoreElements()) { resultArea.appendText(resEnum.nextElement() + "\n"); } } catch (Exception e){ resultArea.setText("Fehler:" + e.getMessage() + "\n"); } } } /* * Überdefinieren einiger Standard-Applet-Methoden. */ public boolean handleEvent( Event event ) { return SuperCedeEvent( event ); } public void start() { SuperCedeStart(); } public void stop() { SuperCedeStop(); } /* * Initialisierung (insbesondere Definition und Konfiguration der * benutzten GUI-Komponenten. */ private final void SuperCedeInit() { setLayout(null); setFont( new Font("Helvetica", Font.BOLD, 11)); setBackground(new Color(192, 192, 192)); addNotify(); resize((insets().left + insets().right + 455), (insets().top + insets().bottom + 459)); label1 = new Label("Authentisierung", Label.LEFT); label1.setFont( new Font("Helvetica", Font.BOLD, 13)); add(label1); 35 label1.reshape((insets().left + 15), (insets().top + 9), 186, 16 ); label2 = new Label("Login", Label.LEFT); add(label2); label2.reshape((insets().left + 24), (insets().top + 46), 100, 15); usrField = new TextField(""); usrField.setEditable(true); add(usrField); usrField.reshape((insets().left + 200), (insets().top + 40), 225, 20); label3 = new Label("Paßwort", Label.LEFT); add(label3); label3.reshape((insets().left + 24), (insets().top + 78), 100, 15); pwdField = new TextField(""); pwdField.setEditable(true); pwdField.setEchoCharacter('*'); add(pwdField); pwdField.reshape((insets().left + 200), (insets().top + 72), 225, 20); label5 = new Label("Länder (Mehrfachauswahl möglich)", Label.LEFT); label5.setFont(new Font("Helvetica", Font.BOLD, 13)); add(label5); label5.reshape((insets().left + 15), (insets().top + 113), 410, 16); // nicht gerade datengetrieben ;-( countryList = new List(2, true); // countryList.addItem("Alle"); countryList.addItem("Australien"); countryList.addItem("Belgien"); countryList.addItem("Deutschland"); countryList.addItem("Frankreich"); countryList.addItem("Großbritannien"); countryList.addItem("Hongkong"); countryList.addItem("Japan"); countryList.addItem("Kanada"); countryList.addItem("Mexiko"); countryList.addItem("Schweden"); countryList.addItem("Singapur"); countryList.addItem("Spanien"); countryList.addItem("USA"); add( countryList); countryList.reshape((insets().left + 24), (insets().top + 148), 401, 61); okButton = new Button("Anfrage abschicken"); add(okButton); okButton.reshape((insets().left + 24), (insets().top + 230), 177, 20); resetButton = new Button("Eingabe zurücksetzen"); add(resetButton); resetButton.reshape((insets().left + 248), (insets().top + 230), 177, 20); label6 = new Label("Suchergebnis", Label.LEFT); label6.setFont(new Font( "Helvetica", Font.BOLD, 13)); add(label6); label6.reshape((insets().left + 15), (insets().top + 270), 410, 16); resultArea = new TextArea(""); resultArea.setEditable(false); add(resultArea); resultArea.reshape((insets().left + 15), (insets().top + 290), 410, 145); super.init(); } 36 /* * Allgemeine Festlegungen des Event-Handling. */ private final boolean SuperCedeEvent( Event event ) { if ((event.target == okButton) && (event.id == Event.ACTION_EVENT)) { OkButtonClicked( event ); return true; } if ((event.target == resetButton) && (event.id == Event.ACTION_EVENT)) { ResetButtonClicked( event ); return true; } return super.handleEvent( event ); } /* * Start-Methode und Stop-Methode des AWT-Generators. */ private final void SuperCedeStart() {} private final void SuperCedeStop() {} /* * Allgemeine Deklarationen. */ CheckboxGroup filialSelect; Label label1; Label label2; TextField usrField; Label label3; TextField pwdField; Label label5; List countryList; Button okButton; Button resetButton; Label label6; TextArea resultArea; } 37 Konventielles Data-Warehouse-Frontend Die hier abgenildete Applikation wurde mit PowerPlay von Cognos, Inc. erstellt. Abbildung 14: Konventielles Frontend zu "Abenteuer und Freizeit" 38