Tonno e cipolla

Dass dynamische Dokumente im World Wide Web kein Garant für gute Anwendungen sind, weiß jeder, dessen Requests auf ‘angesagten’ Seiten schon einmal im Nirwana gelandet sind. Neben dem rein technischen Aspekt bedarf es einer gut durchdachten Software-Architektur, um dem Ansturm der Surfergemeinde standhalten zu können.

In Pocket speichern vorlesen Druckansicht 8 Kommentare lesen
Lesezeit: 20 Min.
Von
  • Lars Röwekamp
  • Peter Roßbach
Inhaltsverzeichnis

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.

Mehr Infos

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 "Java-Datenbanksystem InstantDB").

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).

Wichtige Klassen des DB-Frameworks
Funktion Klasse
Verbindung zur Datenbank herstellen DatabaseConnection
JDBC-ResultSet in Java-Objekte verwandeln DatabaseRetriever und I_DatabaseBinder
Java-Objekte in der DB ändern DatabaseGenericObject und DatabaseGenericModifier

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.

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).

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.

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
  • Request Processor
  • ScreenFlow Manager
  • Model Manager

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).

Implizite Objekte innerhalb einer JSP
Name Java Klasse/Interface Beschreibung
application javax.servlet.ServletContext Daten, die programmweit im Zugriff sind
config javax.servlet.ServletConfig Konfigurationsdaten des Servlets
exception java.lang.Throwable nicht verarbeitete Exception
out javax.servlet.jsp.JspWriter Output Stream
page javax.servlet.jsp.HttpJspPage Servlet-Instanz der JSP (this)
pageContext javax.servlet.jsp.PageContext Kontextdaten einer JSP für Tags
request javax.servlet.http.HttpServletRequest Request-Objekt inklusive Parameter
response javax.servlet.http.HttpServletResponse Response-Objekt
session javax.servlet.http.HttpSession Session-spezifische Daten

Alle notwendigen Schritte zur Nutzung dieser Objekte vollzieht die verwendete JSP-Engine automatisch während des Übersetzungsvorganges einer JSP. Listing 1 zeigt beispielhaft den durch Tomcat 3.1 generierten Quellcode einer beliebigen JSP für den Bereich der impliziten Objekte.

Mehr Infos

Listing 1: Automatisch generierter Java-Quellcode

package jspTutorial;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;4
import javax.servlet.jsp.tagext.*;
import java.io.PrintWriter;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.Vector;
import org.apache.jasper.runtime.*;
import java.beans.*;
import org.apache.jasper.JasperException;

public class _0002fjspTutorial_0002forder_0002ejsporder_jsp_0 extends
HttpJspBase {

static {
}
public _0002fjspTutorial_0002forder_0002ejsporder_jsp_0( ) {
}
private static boolean _jspx_inited = false;
public final void _jspx_init() throws JasperException {
}
public void _jspService(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
JspFactory _jspxFactory = null;
PageContext pageContext = null;
HttpSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
String _value = null;
try {
if (_jspx_inited == false) {
_jspx_init();
_jspx_inited = true;
}
_jspxFactory = JspFactory.getDefaultFactory();
response.setContentType("text/html;charset=8859_1");
pageContext = _jspxFactory.getPageContext(this, request,
response,
"/jspTutorial/errorpage.jsp", true, 8192, true);
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
.
.
.
}
}

Generell lassen sich vier Arten dieser Objekte unterscheiden:

  • Seitenobjekte
  • Kontextobjekte
  • Input-/Output-Objekte
  • Fehlerobjekt(e)

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:

  • Servlet Container Information: Methoden, die Informationen zu dem zugehörigen Servlet Container liefern. Dabei handelt es sich um den Namen des Containers und die Version des Servlet-API.
  • Server-Side Ressources: Methoden zum Zugriff auf Ressourcen wie Dateinamen oder URLs (siehe Listing 2).
  • Logging: Methode, die ein Aufzeichnen von ‘normalen’ Vorgängen und Java Exceptions ermöglichen.
  • Initialization Parameter: Methoden zum Zugriff auf Parameter zur Initialisierung der Applikation.
Mehr Infos

Listing 2: ModelManager

<jsp:useBean
id = "pizzaModelManager"
class = "de.ix.jspTutorial.controller.ModelManager"
scope = "session"
>
<%
pizzaModelManager.init(config.getServletContext(), session);
%>
</jsp:useBean>

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 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 (Listing 3).

Mehr Infos

Listing 3: Verwendung des Objekt application im ModelManager

package de.ix.jspTutorial.controller ;
import ... ;
public class ModelManager implements
de.ix.jspTutorial.constants.SessionConstants{
private ServletContext application;
private HttpSession httpSession;
public void init(ServletContext application,HttpSession httpSession) {
this.httpSession = httpSession;
this. application = application;
getPizzaAccessor();
}
public PizzaAccessor getPizzaAccessor() {
PizzaAccessor pizzaAccessor = null ;
synchonized(application) {
pizzaAccessor = (PizzaAccessor)
application.getAttribute(PIZZA_ACCESS);
if (pizzaAccessor == null) {
pizzaAccessor = new PizzaAccessor(application);
application.setAttribute(PIZZA_ACCESS, pizzaAccessor);
}
}
return pizzaAccess;
}
// ...
}
Mehr Infos

Listing 4: Verwendung des Objekt application im PizzaList.java

public void readList(ServletContext application) {

PizzaAccessor theAccessor = (PizzaAccessor)
application.getAttribute(PIZZA_ACCESSOR ) ;
if(theAccessor != null) {
application.log("Read Pizza's from JDBC") ;
pizzas = theAccessor.readPizzaList(application) ;
} else
new RuntimeException ("Can't Access Database for pizza") ;
}

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

  • PAGE_SCOPE
  • REQUEST_SCOPE
  • SESSION_SCOPE
  • APPLICATION_SCOPE,

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 Listing 5).

Mehr Infos

Listing 5: Das Objekt request im Einsatz

package de.ix.jspTutorial.controller;
import ...
/**
* Klasse zur Abarbeitung aller Request, die in den globalen
* Eintrittspunkt der Applikation eingegangen sind.
*/
public class RequestProcessor
implements de.ix.jspTutorial.constants.RequestParameterConstants {
private HttpSession httpSession;
private ServletContext application;
private ModelManager pizzaModelManager;
/**
* Methode zur Initialisierung aller privaten Attribute der
* gegebenen <code>RequestProcessor</code> Instanz
*
* @param servletContext actual Servlet Context
* @param httpSession actual HTTP Session
*/
public void init(ServletContext application, HttpSession httpSession) {
this.httpSession = httpSession;
this.application = application;
pizzaModelManager =
(ModelManager)httpSession.getValue(SessionConstants.MODEL_MANAGER);
}

/**
* Methode zum Request bzw. zur Bearbeitung aller Http Request
*
* @param request actual HttpServletRequest
*
*/
public void processRequest(HttpServletRequest request)
throws MissingInputParamsException
{
String doAction ="";
try {
doAction = (String)request.getParameter(DO_ACTION);
} catch(Exception ex) {
doAction = "";
}
checkParameters(doAction,request);
handleEvent(doAction,request);
}
// ...
}

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 Listing 6 dar (siehe auch PizzaServiceFC.jsp), das ein Caching der HTML-Ausgabe innerhalb eines Browsers verhindern soll.

Mehr Infos

Listing 6: Header-Manipulation

<%      
response.setDateHeader("Expires",0);
response.setHeader("Pragma","no-cache");
response.setHeader("Cache-Control","no-cache,must-revalidate");
%>

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.

Listing 8 zeigt einen gekürzten Ausschnitt aus dem generierten Servlet der JSP choice.jsp, das eine einfache HTML-Tabelle erzeugt. 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 Listing 8 dargestellter Quellcode wäre manuell einzutippen ...

Mehr Infos

Listing 7: Ausschnitt aus choice.jsp

<!-- Table for choice of ingredients -->     
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0">

<TR BGCOLOR="#406E6E">
<TD WIDTH="150"><FONT FACE="Arial,Helvetica" SIZE="2"
COLOR="#FFFFFF">&nbsp;Name</TD>
<TD WIDTH="100"><FONT FACE="Arial,Helvetica" SIZE="2"
COLOR="#FFFFFF">&nbsp;Preis</TD>
<TD WIDTH="50"><FONT FACE="Arial,Helvetica" SIZE="2"
COLOR="#FFFFFF">&nbsp;</TD>
</TR>

<%
Set entrySet2 = ingredientList.getIngredients().entrySet();
for (Iterator iter2 = entrySet2.iterator(); iter2.hasNext();) {
Map.Entry entry2 = (Map.Entry)iter2.next();
Ingredient ingredient = (Ingredient)entry2.getValue();
%>

<TR>
<TD><FONT ... >&nbsp;<%=ingredient.getName()%></TD>
<TD><FONT ... >&nbsp;DM&nbsp;<%=formatCurrency(ingredient.getPrice())%></TD>
<TD><FONT ... ><INPUT NAME="ID_INGREDIENTS" TYPE="checkbox"
VALUE="<%=ingredient.getId()%>">
</TD>
</TR>
<%
}
%>

</TABLE>
Mehr Infos

Listing 8: Das JSP-Ausgabeobjekt out

out.print("<!-- Table for choice of ingredients -->\t\r\n\r\n \t\t\t
<TABLE BORDER=\"0\" CELLPADDING=\"0\"
CELLSPACING=\"0\">\r\n\t\t\t\r\n\t\t\t<TR
BGCOLOR=\"#406E6E\">\r\n\t\t\t\t<TD WIDTH=\"150\"><FONT
FACE=\"Arial,Helvetica\" SIZE=\"2\"
COLOR=\"#FFFFFF\">&nbsp;Name</TD>\r\n\t\t\t\t<TD
WIDTH=\"100\"><FONT FACE=\"Arial,Helvetica\" SIZE=\"2\"
COLOR=\"#FFFFFF\">&nbsp;Preis</TD>\r\n\t\t\t\t<TD
WIDTH=\"50\"><FONT FACE=\"Arial,Helvetica\" SIZE=\"2\"
COLOR=\"#FFFFFF\">&nbsp;</TD>\t\t\r\n\t\t\t</TR>\r\n\t\t\t\r\n\t\t\t\r\n\t\t\t");

Set entrySet2 = ingredientList.getIngredients().entrySet();
for (Iterator iter2 = entrySet2.iterator(); iter2.hasNext();) {
Map.Entry entry2 = (Map.Entry)iter2.next();
Ingredient ingredient = (Ingredient)entry2.getValue();
out.print("\t\t\t\r\n\t\t\t\t<TR>\r\n\t\t\t\t\t<TD><FONT
FACE=\"Arial,Helvetica\"
SIZE=\"2\">&nbsp;");
out.print(ingredient.getName());
out.print("</TD>\r\n\t\t\t\t\t<TD><FONT
FACE=\"Arial,Helvetica\" SIZE=\"2\">&nbsp;DM&nbsp;");
out.print(formatCurrency(ingredient.getPrice()));
out.print("</TD>\r\n\t\t\t\t\t<TD><FONT
FACE=\"Arial,Helvetica\" SIZE=\"2\">
<INPUT NAME=\"ID_INGREDIENTS\" TYPE=\"checkbox\" VALUE=\"");
out.print(ingredient.getId());
out.print("\"></TD>\t\r\n\t\t\t\t</TR>\r\n\t\t\t");
out.print("\t\t\r\n\t\t\t\t\r\n\t\t\t</TR>\t\t\r\n\t\t\t</TABLE>

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. Listing 9 demonstriert eine typische Verwendung des exception-Objekts innerhalb der Fehlerseite des Pizza-Service.

Mehr Infos

Listing 9: Fehlerseite des Pizza-Service

<%--
% This Javer Server Page is part of the iX JSP tutorial.
% Error Pages
--%>

<%-- Setting up page as error page --%>
<%@ page isErrorPage="true" %>
<%@ page import="java.io.*" %>
<%
ByteArrayOutputStream theStream = new ByteArrayOutputStream();
if(exception != null)
exception.printStackTrace(new PrintWriter(theStream,true));
%>
<!--
Der Grund des Fehlers ist "<%=exception.getMessage() %>".
Der Fehler trat an folgende Stelle auf:
"<%=theStream.toString()%>"
-->
<HTML>
<HEAD>
<TITLE>PIZZA SERVICE ONLINE - ERROR</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" LINK="#406E6E" ALINK="#406E6E"
VLINK="#406E6E">
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0">
<TR VALIGN="top">
<TD WIDTH="115"><A HREF="index.jsp">
<IMG SRC="logo.gif" WIDTH="115"
HEIGHT="115" ALT="" BORDER="0"></A></TD>
<TD WIDTH="50">&nbsp;</TD>
<TD WIDTH="500"><FONT FACE="Arial,Helvetica" SIZE="2">
<FONT SIZE="5"
COLOR="#406E6E"><B><I>Fehler</I></B></FONT><BR>

<BR>
Leider ist ein Fehler aufgetreten.
<BR>
<BR>
<A HREF="index.jsp">Startseite</A>
<BR>
<BR>
<BR>
<%@ include file="/jspTutorial/includes/copyright.html"%>
</FONT></TD>
</TR>
</TABLE>
</BODY>
</HTML>

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.

[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

Mehr Infos

iX-TRACT

  • Dies dreiteilige Tutorial führt in die Programmierung der JavaServer Pages ein.
  • Im Vergleich zum ersten Teil sorgt in diesem eine Java-Datenbank für mehr Komfort bei der Speicherung von Benutzer- und Bestelldaten.
  • Durch eine Kombination von JavaServer Pages, Servlets und JavaBeans entsteht eine modulare Webanwendung auf Basis einer Model-View-Controller-Architektur.