
Der erste Teil des Tutorials hat sich vornehmlich mit der Syntax und Funktionsweise der Java-Server-Page-Technik beschäftigt. Ein einfacher Pizza-Online-Dienst diente als Basis, die verschiedenen JSP-Tags zu erläutern und mögliche Problemfelder zu zeigen.
Im Verlauf des ersten Teils wurde deutlich, welche Vorteile JSP gegenüber anderen Skriptsprachen mit sich bringt. Es zeigte sich aber, dass die Verwendung von JSPs allein nicht immer zum gewünschten Ziel, nämlich der Trennung von Businesslogik und Design, führen muss.
Genau an dieser Stelle setzt der zweite Teil an. Durch eine Kombination von JSP, Servlets und JavaBeans entsteht eine modulare und skalierbare Webanwendung auf Basis einer Model-View-Controller-Architektur (MVC). Darüber hinaus sorgt die Anbindung einer Java-Datenbank zur Speicherung der Benutzer- und Bestelldaten für deutlich mehr Komfort.
Hinsichtlich der bisher vorgestellten Lösung fällt auf, dass die Daten einer Bestellung nicht gespeichert werden. Der Kunde muss seine Daten jedes Mal neu eingeben. Diesen offensichtlichen Mangel beseitigt eine Registrierung des Kunden vor der Auswahl. Den bisherigen Verlust der Kundschaft nach jedem Neustart des Servers vermeidet das Speichern der Objekte in einer Datenbank - via JDBC. Damit der Aufwand für Installation und Administration so niedrig wie möglich bleibt, kommt InstantDB zum Einsatz (siehe Kasten).
Eine direkte JDBC-Programmierung ist aufwändig und wartungsintensiv. Für die zu speichernden Businessobjekte ist ein einfaches Datenbank-Framework daher gerade das Richtige (siehe Tabelle).
Die Klasse DatabaseConnection kapselt die JDBC-Verbindung mit den zugehörigen Parametern und ermöglicht es, die Verbindung jederzeit auf- und abzubauen. Hier könnte auch ein JDBC-ConnectionPool verborgen werden, um die Leistungsfähigkeit mit vielen Nutzern nicht mit einer hohen Anzahl von teuren Datenbankverbindungen zu bezahlen. Der DatabaseRetriever nimmt SQL und eine Objektfabrik, die die Schnittstelle I_DatabaseBinder implementiert (siehe PizzaBinder.java). Kunden und Bestellungen werden vor der Speicherung in die Datenbank in DatabaseGenericObject verwandelt. Die Klasse DatabaseGenericModifier kann für diese Art von generischen Objekten die SQL-Anweisungen für insert, update und delete erzeugen und ausführen.
Wer den ersten Teil des Tutorials aufmerksam verfolgt hat, kann sich vorstellen, dass der dort gezeigte Architektur-Ansatz bei größeren Applikationen zu einer Reihe von Problemen führt. Dies gilt insbesondere für die Schlagwörter Modularität, Skalierung und Wiederverwendbarkeit.
Darüber hinaus kommt der große Vorteil der JSP-Technik - die Trennung von Design und Businesslogik - im bisherigen Modell nur ansatzweise zum Tragen. Der Grundgedanke, die einzelnen Aufgaben des Entwicklungsprozesses auf verschiedene Personen beziehungsweise Rollen zu verteilen, ist durch die enge Verknüpfung von HTML und Javacode zurzeit nur bedingt möglich.
|
Model-View-Controller Paradigma: Die hier dargestellte Architektur lehnt sich stark an das von Sun Microsystems vorgeschlagene Application Programming Model (APM, siehe java.sun.com/j2ee/docs.html) an und kann dort im Detail nachgelesen werden (Abb. 1). |
Ziel einer verbesserten Architektur muss es sein, eine stärkere Trennung von Design und Businesslogik zu realisieren. Darüber hinaus soll die Anwendung die Informationen kapseln. Diese Gedanken entsprechen genau dem Model-View-Controller-Ansatz. Während die Ablaufsteuerung und Implementierung der Businesslogik mit Hilfe des/der Controller vollzogen wird, sorgt die so genannte View für die Darstellung der Information. Die dafür benötigten Daten werden in Modellen gehalten und können sowohl von der View als auch von den Controllern aus angesprochen werden.
Für das Beispiel bedeutet dies, dass vor allem die Businesslogik aus den JSPs herausgezogen und in entsprechende Controller-Komponenten verlagert werden muss. Auf Seiten der Modelle dagegen sind nur geringe Änderungen notwendig, da bereits der erste Teil eine Auslagerung dieser Daten vorsah.
|
Update: der modifizierte und ergänzte Ablauf einer Bestellung (Abb. 3) |
Im Wesentlichen unterscheidet sich die MVC-Architektur von dem bisherigen Ansatz durch die folgenden Komponenten (siehe auch Abbildung 3):
Front Component bildet den zentralen Zugang zur Webapplikation. Sie dient einerseits zur geregelten Zugriffskontrolle und andererseits als eine Art globale Ablaufsteuerung. So garantiert sie zum Beispiel, dass alle innerhalb der Anwendung benötigten Klassen zum richtigen Zeitpunkt als gültige Instanz vorliegen. Zusätzlich kann die Front Component auch dazu dienen, einen unerlaubten Zugriff auf die Applikation via Bookmarks zu verhindern. Generell wird eine Seite dabei nicht mehr direkt über ihren Namen aufgerufen, sondern mit Hilfe eines zusätzlichen Parameters namens doAction, dessen Wert die aufzurufende Seite genauer spezifiziert.
Request Processor bildet den Kern der neuen Architektur. Seine Methoden implementieren (beinahe) die komplette Businesslogik der Anwendung. Er nimmt Requests entgegen, überprüft die zugehörigen Parameter auf Vollständigkeit und Korrektheit und führt anschließend die notwendigen Berechnungen zur Umsetzung der Businesslogik durch.
ScreenFlow Manager kann in Abhängigkeit vom aktuellen Request und zusätzlichen Randbedingungen, wie den vorangegangenen Berechnungen des Request Processor, die nächste darzustellende HTML-/JSP-Seite berechnen. Die Komplexität des ScreenFlow Manager hängt dabei stark von den Kriterien zur Berechnung des Applikations-Workflow ab.
|
UML-Sequenzdiagramm: Bearbeitung eines FrontComponent-Request PizzaServiceFC.jsp (Abb. 2). |
Model Manager kann als eine Art erweitertes Session-/Applikations-Clipboard gelten. Mit seiner Hilfe erfolgt ein kontrollierter Zugriff auf alle relevanten Daten innerhalb der Anwendung. Liegt ein angefordertes Objekt im Moment der Anfrage noch nicht vor, sorgt der ModelManager für dessen Instanziierung und stellt es anschließend zur Verfügung. Das vermeidet Probleme wie Nullpointer Exceptions oder ungültige Objektreferenzen. Der eben geschilderte Ablauf spiegelt sich auch im zugehörigen Sequenzdiagramm (Abbildung 2) wider.
Wie der bisherige Verlauf des Tutorials gezeigt hat, liegt einer der großen Vorteile der Java-Server-Pages-Technik in der Möglichkeit, bereits vorhandene Ressourcen zu verwenden. Beliebige Klassen lassen sich einbinden und instanziieren. Man kann Methoden aufrufen und deren Ergebnisse auf einfache Weise in die JSPs einbinden. Durch diesen relativ trivialen Mechanismus sind JavaServer Pages in der Lage, die komplette Java-Welt mit ihren unzähligen APIs zu nutzen.
Neben den eben beschriebenen expliziten - durch den Entwickler deklarierte und instanziierte - Objekten stehen zusätzlich eine Reihe sogenannter impliziter Objekte zur Verfügung (siehe Tabelle).
Alle notwendigen Schritte zur Nutzung dieser Objekte vollzieht die verwendete JSP-Engine automatisch während des Übersetzungsvorganges einer JSP. RefID138817_7:Listing 1 zeigt beispielhaft den durch Tomcat 3.1 generierten Quellcode einer beliebigen JSP für den Bereich der impliziten Objekte.
Generell lassen sich vier Arten dieser Objekte unterscheiden:
Mit einer Ausnahme (siehe unten: request) können dabei alle impliziten Objekte genau einem dieser vier Bereiche zugeordnet werden. Was sich hinter ihnen verbirgt und welche Anwendung die einzelnen impliziten Objekte innerhalb des Pizza-Dienstes finden, zeigen die nächsten Abschnitte.
Die zwei Seitenobjekte page vom Typ javax.servlet.jsp.HttpJspPage und config vom Typ javax.servlet.ServletConfig repräsentieren die JSP beziehungsweise das zugehörige generierte Java-Servlet.
Mit Hilfe des impliziten Objekts page erhält der Entwickler Zugriff auf alle Methoden und Variablen des Servlets. Das page-Objekt ist somit mit this gleichzusetzen. In der Praxis wird page innerhalb einer JSP selten verwendet, da ein Programm ohnehin direkt auf alle Methoden und Variablen zugreifen kann. page dient nur dazu, Beans für diese eine Seite zu speichern. Damit kann eine spezielle Bean nur auf einer Seite für alle requests auf dieser Seite bereitgestellt werden. Es entsteht eine wiederverwendbare Komponente für verschiedene Situationen und Projekte, als würde die JSP mit Methoden und Attributen erweitert.
Auch das config-Objekt kommt in der JSP-Praxis selten vor. Seit der Java-Servlet-2.0-Spezifikation besteht die Möglichkeit, eine Servlet-Instanz zu parametrisieren, wobei config den Zugriff auf diese Parameter ermöglicht. Gerade bei größeren Applikationen bietet es sich an, Parameter für die Initialisierung einer JSP und die in ihr enthaltenen Beans in einem separaten Web Deployment Descriptor abzulegen, um so einen Teil der Umgebungseinstellungen deklarativ vornehmen zu können. Genaueres ist der Servlet-Spezifikation zu entnehmen.
Im Gegensatz zu page und config handelt es sich bei den drei Kontextobjekten application, pageContext und session um in der Praxis häufig verwendete Objekte. Kontextobjekte stellen einer JSP Informationen bezüglich ihrer Ablaufumgebung (Kontext) zur Verfügung. Darüber hinaus bieten sie die Möglichkeit, Informationen und Daten mit ihrer Umgebung (anderen JSP, Java-Klassen et cetera) auszutauschen. Mit Hilfe von application (Typ javax.servlet.ServletContext) kann eine JSP auf Daten und Informationen anderer JSPs und Servlets innerhalb derselben Anwendung zugreifen. Unter einer Applikation versteht man in diesem Zusammenhang eine Gruppe von JSPs und Servlets, die in der Regel eine gemeinsame URL bestimmt. So würden die beiden JSPs
www.heise.de/ix/jsp-tut/order.jsp www.heise.de/ix/jsp-tut/choice.jsp
innerhalb einer Applikation ablaufen, die beiden JSPs
www.heise.de/jsp-tut_01/order.jsp www.heise.de/jsp-tut_02/choice.jsp
laufen seit dem Servlet-API 2.2 in der Default-app ab. Darüber hinaus können die Gruppierungen der Applikationen in der Regel auch mit Hilfe einer nicht standardisierten Beschreibung des Servlet Container gesteuert werden. application stellt eine Reihe von Methoden zur Verfügung, die sich in vier Bereiche unterteilen lassen:
Im Tutorial dient application dazu, die Pizza- und Zutatenliste innerhalb einer Applikation nur einmal allen Sessions zur Verfügung zu stellen.
Sobald der erste Aufruf eines Kunden die FrontComponent PizzaServiceFC.jsp passiert, wird automatisch eine Instanz des ModelManager für die aktuelle Session des Benutzers erzeugt. Diese Instanz wiederum regelt den Zugriff auf die gemeinsamen Objekte der Session sowie den Zugriff auf die sessionübergreifenden Objekte der Anwendung.
Beispielsweise stellt der Aufruf, den RefID138817_8:Listing 2 enthält, bereits während der Initialisierung des ModelManager eine Verbindung zur Pizza-Datenbank her und stellt mit Hilfe von application allen anderen Objekten und Teilnehmern derselben Applikation zur Verfügung.
Sucht das Programm/Servlet nach der Liste der Pizzen und der Zutaten, prüft es zunächst, ob sie bereits im application-Objekt hinterlegt wurde. Ist dies nicht der Fall, liest das bereits erzeugte Objekt vom Typ PizzaAccessor sie ein und macht sie durch Hinterlegen im application-Objekt allen anderen Teilnehmern zugänglich (RefID138817_9:Listing 3).
pageContext erlaubt durch seine Methoden Zugriff auf alle anderen impliziten Objekte einer JSP. Dieses Objekt stellt somit ein ideales Austauschobjekt zwischen verschiedenen Tags der JSPs dar. Darüber hinaus bietet das pageContext-Objekt eine Reihe von Methoden zur Verwaltung von key/value-Paaren an, wobei sich mittels Angabe eines Scope angeben lässt, welche Attribute durchsucht oder geändert werden sollen. Im dritten Teil wird der Einsatz vertieft. Gültige Scope-Werte sind
wobei die Namen selbsterklärend sind. Das dritte und letzte implizite Objekt im Bereich der Kontextobjekte - session - erlaubt den Zugriff auf Sessioninformationen des aktuellen Nutzers. Bedingt durch seine Bedeutung steht dieses Objekt nur innerhalb von JSPs zur Verfügung, deren Sichtbarkeitsbereich durch die Page Direktive auf die aktuelle Session gesetzt wurde (Standardeinstellung).
Die verschiedenen Methoden des session-Objekts können unter anderem die ID oder den Erzeugungszeitpunkt der Session erfragen. Darüber hinaus kann die Methode invalidate() die aktuelle Session löschen.
Streng genommen gehört gemäß der obigen Definition neben den drei bisher beschriebenen Objekten auch request (Typ javax.servlet.http.HttpServletRequest) in den Bereich der Kontextobjekte. Es dient dazu, Informationen in Form von JavaBeans für folgende JSPs beziehungsweise Servlets zu setzen oder zu löschen.
In- und Output-Objekte befassen sich, was niemanden überraschen dürfte, mit der Ein- und Ausgabe einer JSP. Während das implizite Objekt request diejenigen Daten beinhaltet, die in eine JSP eingehen, repräsentiert das response-Objekt die ausgehenden Daten.
Innerhalb einer JSP mit der Skriptsprache Java bietet request Zugriff auf die Request-Parameter, Header-Informationen, die aufrufende URL, Cookies und etliche andere Daten. Darüber hinaus lassen sich mit Hilfe von request - im Zusammenhang mit dem <jsp:setProperty ... />-Tag - die Eigenschaften einer Bean setzen und löschen, wodurch es ebenfalls zu einem Kontextobjekt wird.
Verwendung findet request innerhalb des Tutorials unter anderem im ScreenFlowManager und innerhalb des RequestProcessor. Während ersterer das request-Objekt zur Berechnung der Folgeseite heranzieht, führt letzterer die notwendigen Berechnungen durch (siehe RefID138817_11:Listing 5).
Das Gegenstück zu request bildet das implizite Objekt response. Es setzt Header- und Cookie-Informationen des HTTP-Response, kodiert URLs und bestimmt den MIME-Type. Eine typische Anwendung für das response-Objekt innerhalb einer JSP stellt RefID138817_12:Listing 6 dar (siehe auch PizzaServiceFC.jsp), das ein Caching der HTML-Ausgabe innerhalb eines Browsers verhindern soll.
Ein weiteres implizites Objekt für den Bereich der Ein- und Ausgabe ist out. Es repräsentiert den Ausgabestrom einer JSP und dient innerhalb von Scriptlets hauptsächlich dazu, HTML zu erzeugen. Darüber hinaus nutzt die JSP-Engine das out-Objekt während der Generierung des Servlets, um die HTML-Bestandteile einer JSP auszugeben.
RefID138817_14:Listing 8 zeigt einen gekürzten Ausschnitt aus dem generierten Servlet der JSP choice.jsp, das eine einfache HTML-Tabelle erzeugt. RefID138817_13:Listing 7 zeigt denselben Ausschnitt aus der Original-JSP. Bei der Betrachtung des Listings wird mehr als deutlich, welchen Vorteil die Einführung von JSPs und die damit verbundene Trennung von Design und Businesslogik mit sich bringt. Man stelle sich vor, es gäbe keine Java-Server-Pages-Technik und sämtlicher in RefID138817_14:Listing 8 dargestellter Quellcode wäre manuell einzutippen ...
In den Bereich der Fehlerobjekte fällt lediglich ein Objekt namens exception. Im Unterschied zu den meisten anderen Objekten ist dieses implizite Objekt nicht in jeder JSP erreichbar. Lediglich JSPs, die durch die Seitendirektive <%@ page isErrorPage="true" %> explizit als Fehlerseiten ausgewiesen wurden, können darauf zugreifen. RefID138817_15:Listing 9 demonstriert eine typische Verwendung des exception-Objekts innerhalb der Fehlerseite des Pizza-Service.
Während der Anwender nur eine nichts sagende Fehlermeldung erhält, wird die eigentliche versteckt als HTML-Kommentar ausgegeben; dadurch kann der Entwickler oder Call-Center-Agent über die HTML-Quellen die Meldung einsehen.
Obwohl schon viele Verbesserungsansätze gegenüber der ersten Version aufgezeigt wurden, bleiben für die Implementierung einer Real-World-Application noch eine Menge offener Punkte und potenzieller Problemfelder zu lösen, die aber den Rahmen eines dreiteiligen Tutorials sprengen.
So müsste eine echte Applikation zusätzlich über einen Warenkorb und eine ausgereifte Exception-Behandlung verfügen. Darüber hinaus ist es sinnvoll, die Beschreibung der Request-Parameter zu verfeinern und deklarativ zu handhaben. Ein potenzieller Ansatz hierfür ist die Verwendung einer XML-Datei, die für jeden Request die zugehörigen Parameter sowie eine genauere Spezifikation deren Typs, Wertebereichs et cetera enthält. Auch ein deklaratives Mapping zwischen Request-Parametern und Modellattributen wäre denkbar, sodass die Zuweisung von Request-Parametern und Attributen einer JavaBean sich teilweise automatisieren ließe.
Darüber hinaus müsste die Implementierung der Businesslogik in größeren Applikationen aus Gründen der Modularität und Wartbarkeit aus dem Request Processor herausgezogen und auf separate Controller verteilt werden. Der Request Processor würde somit nur noch die Rolle eines zentralen Verteilers übernehmen, die Abarbeitung der Businesslogik dagegen wäre Aufgabe spezieller Controller-Module.
Nachdem geklärt ist, was nicht Bestandteil des dritten Teils sein wird, stellt sich natürlich die Frage, welche Themenbereiche er enthalten wird. Die Antwort ist so kurz wie simpel: TagLibs.
Es ist zwar durch die Verwendung der MVC-Architektur bereits teilweise gelungen, Businesslogik, Datenhaltung und Design voneinander zu trennen, trotzdem wird der hohe Java-Anteil innerhalb der JSPs sicherlich bei dem einen oder anderen HTMLer noch zu gewissen Schwierigkeiten und Unklarheiten führen. Genau hier setzten die TagLibs an, indem komplizierte Java-Ausdrücke durch relativ einfache, HTML-ähnliche Tags ersetzt werden. Als Bonbon zeigt der dritte Teil darüber hinaus, wie sich eine Bestellung in Form von XML via E-Mail versenden lässt.
Lars Röwekamp
ist als IT-Berater mit den Schwerpunkten Neue Technologien und OO/Java für seine Firma openKnowledge tätig.
Peter Rossbach
ist freiberuflicher IT-Berater für die Konzeption, Organisation und Entwicklung von Java-Web-Anwendungen.
Literatur
[1] Lars Röwekamp, Peter Roßbach; Web-Programmierung; Quattro Stagioni; JSP-Tutorial, Teil 1: Grundlagen der Java Server Pages
[2] Peter Roßbach, Hendrik Schreiber; Java Server und Servlets Bonn (Addison-Wesley) 1999 und 2000
[3] Dunae K. Fields, Mark A. Kolb; Web Development with JavaServer Pages; Greenwich, CT (Manning) 2000
[4] Volker Turau; Java Server Pages; Dynamische Generierung von Webdokumenten; Heidelberg (dpunkt) 2000
[5] Pekowsky, Larne; Java Server Pages; Reading, MA (Addison-Wesley) 2000
| iX-TRACT |
|
Dieser Text ist der Zeitschriften-Ausgabe 08/2000 von iX entnommen.
Parallelprogrammierung - die Kunst der Multi-Core-Nutzung
Agile ALM - agile Praktiken im Application Lifecycle Management
Webentwicklung - Applikationen für mobile Clients
HTML5, CSS3, WebGL: Das iX-Sonderheft zum Thema Webdesign fasst die wichtigsten Neuerungen der aktuellen und kommenden Webstandards zusammen.