Frutti di Mare

Webanwendungen haben ein Niveau erreicht, bei dem es nicht mehr ausreicht, ihre technische Seite zu verstehen. Die Akzeptanz von Informationen im WWW hängt heutzutage von ansprechendem Design ab. Webautoren sollten sich dabei voll auf ihre Rolle konzentrieren können und sich nicht mit technischen Details herumärgern müssen.

In Pocket speichern vorlesen Druckansicht 1 Kommentar lesen
Lesezeit: 19 Min.
Von
  • Lars Röwekamp
  • Peter Roßbach
Inhaltsverzeichnis

In den ersten beiden Teilen dieses JSP-Tutorials ging es zunächst darum, einen Pizza-Dienst mit Hilfe von JavaServer Pages aufzubauen und zu verfeinern. Zu letzterem gehörten die Anbindung an eine Datenbank sowie die Verwendung des Model-View-Controller-Ansatzes. Während der Webdesigner sich auf die Darstellung der Informationen konzentieren kann (View), kümmert sich der Java Entwickler um deren Beschaffung und Bearbeitung (Model und Controller).

Dieser Ansatz verlangt vom Designer noch eine Menge an Java-Kenntnissen. Schließlich muss er in der Lage sein, die entsprechenden Quellen einzubinden und korrekt zu verwenden. Die gewünschte Aufteilung des Entwicklungsprozesses auf verschiedene Rollen kann somit nur bedingt stattfinden. Abhilfe können hier die so genannten TagLibs schaffen.

Welchen Vorteil TagLibs bei der Erstellung JSP-basierter Webanwendungen mit sich bringen können, soll in diesem dritten Teil des Tutorials Thema sein. Darüber hinaus sollen XML und Java Mail verdeutlichen, warum gerade die Programmiersprache Java - mit all ihren APIs - bestens dafür geeignet ist, Webapplikationen auf Basis bestehender Module und Techniken zu implementieren.

Mehr Infos

Eine wirkliche Revolution für die Erzeugung von dynamischem Content auf dem Server ist die Integration von TagLibs in JSP 1.1. JSPs sind nun durch eigene Tags und Funktionen erweiterbar. Der in den ersten Versionen mögliche Zugriff auf JavaBeans bezog sich auch auf Properties. Mit den TagLibs lassen sich Elemente für Funktionen ebenfalls integrieren. Die Tags sind XML-konforme Erweiterungen der bisherigen Standard-Tags. Diese Syntax erleichtert die Zusammenarbeit zwischen HTML-Designer und Entwicklern. Es können leicht spezielle Vereinfachungen für die dynamischen Inhalte der Site zur Verfügung gestellt werden, ohne dass Hunderte von Zeilen ‘komischen’ Codes oder mehrere include-Anweisungen in die Seite einzubauen wären. Der Entwickler kann endlich eine neue Eigenschaft oder einen Fehler korrigieren und das Ergebnis sofort in allen Seiten aktivieren, ohne sie zu modifizieren. Eine Unterstützung der gesamten Architektur des GUI-Layers ist durch geschickte Templates und entsprechende View-Modelle erreichbar.

Im Folgenden seien als Beispiele die Entwicklung von if-, foreach- und currency-Tags vorgestellt. Wesentliche Motivation für TagLibs ist: wenig Java-Code in die JSPs einzubauen und die Designer mit bestimmten Standard-Funktionen zu versorgen. Statt der Integration einer Funktion durch <%! public String formatCurrency(...) ... %> in verschiedenen JSPs (siehe Teil 1) kann nun der currency-Tag einen Preis formatieren.

<%@ taglib uri="/ixtaglib" prefix="jsptut" %>
...
<B>Pizzapreis</B> DM
<jsptut:currency value="<%=pizza.getBasePrice()%>"/>
...

Dazu muss in der jeweiligen JSP die ixtaglib eingebunden sein - und currency ist verfügbar. Die <%@taglib ..%>-Direktive bewirkt, dass der Tab Library Deskriptor (TLD) die TagLib adressiert und alle seine Tags in der Seite mit dem Tag-Präfix jsptut bereitstellt. Ein URI in der taglib-location-Festlegung definiert in Listing 1 den tatsächlichen Ort der Tag-Bibliothek.

Mehr Infos

Listing 1: Wo die TagLib sich befindet

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
<web-app>
<display-name>Web Development with JSP</display-name>

<servlet>
<servlet-name>JspInit</servlet-name>
<servlet-class>de.ix.jspTutorial.controller.JspTutorialStartServlet</servlet-class>
<load-on-startup>1<load-on-startup>
</servlet>

<taglib>
<taglib-uri>/ixtaglib</taglib-uri>
<taglib-location>/WEB-INF/tlds/ixtaglib_1_0.tld</taglib-location>
</taglib>
</web-app>

Eine Sammlung von TLDs ist ein XML-Dokument das zur Beschreibung der verfügbaren Tags dient. Versionsnummer und eine Inhaltsangabe definieren die gesamte Bibliothek; anschließend folgt die Beschreibung der eigentlichen Tags. Jeder besteht aus einem Namen, der Handler-Klasse und eventuell einer Liste von Attributen Listing 2.

Mehr Infos

Listing 2: Tag Library Descriptor (Auszug)

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib
PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
"http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">

<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>1.1</jspversion>
<shortname>jsptut</shortname>
<info>Utility Tags for JSP.</info>

<tag>
<name>currency</name>
<tagclass>de.ix.jspTutorial.taglib.FormatCurrency</tagclass>
<bodycontent>EMPTY</bodycontent>
<info>Simple German money formatter</info>
<attribute>
<name>value</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<!-- weitere Tags -->
</taglib>

currency als Element ist immer leer (<bodycontent>). Der Geldbetrag entspricht dem Wert des Attributs value, muss vorhanden sein (<required>) und lässt sich durch eine JSP-Expression (<%=...%>) übergeben (<rtexprvalue>).

Aufgabe des currency-Tags ist es, einen Wert mit exakt zwei Nachkommastellen als String auszugeben. Im Konstruktor dient dazu ein DecimalFormat-Objekt, das der formatCurrency()-Methode die Formatierung ermöglicht. Der aktuelle zu formatierende Wert steckt im Attribut myValue.

formatCurrency erbt von BodyTagSupport und dies bedeutet, dass sie in andere Tags wie if oder foreach enthalten sein kann. Das TagLib-API unterscheidet zwischen einfachen Tags, die von TagSupport abgeleitet sind, und solchen, die die Ausgabe ihres Body steuern oder schachteln können. Zuerst wird einem Tag-Handler das PageContext-Objekt zur Kommunikation mit der JSP zugeordnet (Listing 4). Wenn der Tag in einem anderem Handler geschachtelt ist, erhält das Parent-Attibut einen Wert. Im Fall des <jsptut:currency>-Tag bekommt das Attribute value den Wert des Pizza-Grundpreises zugewiesen. Nun ist die Initialisierung abgeschlossen und doStartTag startet die Verarbeitung des Tag-Handler. Jetzt kann der Handler entscheiden, wie die Verarbeitung seines Body weiter verläuft. currency gibt den formatierten Geldbetrag aus und veranlasst keine weitere Verarbeitung des Body (siehe formatCurrency.doAfterBody()).

Mehr Infos

Listing 3: currency

package de.ix.jspTutorial.taglib;

import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import java.io.* ;
import java.util.* ;
import java.text.* ;

public class FormatCurrency extends BodyTagSupport
{
private double myValue;
private DecimalFormat myFormat ;

public FormatCurrency()
{
myFormat = new DecimalFormat();
myFormat.setGroupingUsed(true);
myFormat.setMinimumFractionDigits(2);
myFormat.setMaximumFractionDigits(2);
}

public double getValue() { return myValue ; }

public void setValue(String aValue)
{
this.myValue = new Double(aValue).doubleValue();
}

public void setValue(double aValue)
{
this.myValue = (double) aValue;
}

public void setValue(Double aValue)
{
this.myValue = (double) aValue.doubleValue();
}

public int doAfterBody()
throws JspException
{
try
{
getPreviousOut().print(formatCurrency(myValue)) ;
}
catch(java.io.IOException e)
{
throw new JspException("IO Error: " + e.getMessage());
}

return SKIP_BODY;
}

protected String formatCurrency(double aCurrencyValue) {
return myFormat.format(aCurrencyValue) ;

}
}
Mehr Infos

Listing 4: currency-Tag aus choice.jsp

...
/* ---- jsptut:currency ---- */
de.ix.jspTutorial.taglib.FormatCurrency _jspx_th_jsptut_currency_2 =
new de.ix.jspTutorial.taglib.FormatCurrency();
_jspx_th_jsptut_currency_2.setPageContext(pageContext);
_jspx_th_jsptut_currency_2.setParent(_jspx_th_jsptut_foreach_2);
_jspx_th_jsptut_currency_2.setValue(pizza.getBasePrice());
try {
int _jspx_eval_jsptut_currency_2 = _jspx_th_jsptut_currency_2.doStartTag();
if (_jspx_eval_jsptut_currency_2 == Tag.EVAL_BODY_INCLUDE)
throw new JspTagException(
"Since tag handler class de.ix.jspTutorial.taglib.FormatCurrency
implements BodyTag, it can't return Tag.EVAL_BODY_INCLUDE");
if (_jspx_eval_jsptut_currency_2 != Tag.SKIP_BODY) {
try {
if (_jspx_eval_jsptut_currency_2 != Tag.EVAL_BODY_INCLUDE) {
out = pageContext.pushBody();
_jspx_th_jsptut_currency_2.setBodyContent((BodyContent) out);
}
_jspx_th_jsptut_currency_2.doInitBody();
do {
} while (_jspx_th_jsptut_currency_2.doAfterBody() == BodyTag.EVAL_BODY_TAG);
} finally {
if (_jspx_eval_jsptut_currency_2 != Tag.EVAL_BODY_INCLUDE)
out = pageContext.popBody();
}
}
if (_jspx_th_jsptut_currency_2.doEndTag() == Tag.SKIP_PAGE)
return;
} finally {
_jspx_th_jsptut_currency_2.release();
}
...
Mehr Infos

Listing 5: foreach-Durchlauf

...
<jsptut:foreach item="Pizza" list="<%= pizzaList.getPizzas() %>" >
<%
Pizza pizza = (Pizza)pageContext.findAttribute("Pizza");
%>
<TR>
<TD><FONT FACE="Arial,Helvetica" SIZE="2">&nbsp;
<jsp:getProperty name="Pizza" property="name" /></TD>
<TD><FONT FACE="Arial,Helvetica" SIZE="2">&nbsp;
<jsp:getProperty name="Pizza" property="size" /></TD>
<TD><FONT FACE="Arial,Helvetica" SIZE="2">&nbsp;DM&nbsp;
<jsptut:currency value="<%=pizza.getBasePrice()%>"/></TD>
<TD><FONT FACE="Arial,Helvetica" SIZE="2">
<INPUT NAME="ID_PIZZA" TYPE="radio"
VALUE="<jsp:getProperty name="Pizza" property="id" />"></TD>
</TR>
</jsptut:foreach>
...

Die Methode doEndTag() kann die Ergebnisse eines Tag-Handler in die Ausgabe schreiben. Und release() versetzt den Handler und seine Umgebung wieder in den Ursprungszustand.

Für den Pizza-Bringdienst hat besonderes der foreach-Tag für eine Vereinfachung der Programmierung gesorgt. In choice.jsp können nun die Pizzen und Zutaten dargestellt werden, wie Listing 5 zeigt. Der foreach-Tag stellt jedes Element der Pizza-Liste in pageContext unter item zur Verfügung. Dadurch ist es möglich, mittels <jsp:getProperty .../> auf die Werte der Pizza zuzugreifen und nicht mehr eine JSP-Expression nutzen zu müssen. Leider zeigten sich einige kleine Schwächen bei der Entwicklung der TagLib, die verhinderten, dass noch weniger Java-Code erforderlich ist (siehe den Kasten "Kleine Schwächen").

Mehr Infos

Kleine Schwächen

Als kleine Schwäche in der JSP- und TagLib-Programmierung sind die meist nicht deutlichen Fehlermeldungen der Servlet-Container-Anbieter eine ärgerliche Tatsache. Als wirkliches Problem stellte sich die unterschiedlich konfiguierten XML-Parser für die TLDs raus. Für die Tutorial-TagLib hat es Tage gekostet, die Anwendung auf Tomcat zur Verfügung zu stellen, da der Parser sich weigerte, den TLD zu lesen. Erst nachdem der neu geschrieben war, vermochte Tomcat das. Ein Unix-diff konnte keinen Unterschied der Dateien hervorbringen. Die Codegenierung der Servlet-Container ist ebenfalls unterschiedlich. ServletExec und JRun 3.0 generieren für jede TagHandler-Variable eine lokale Variable in der JSP. Im Tomcat kann dies folgender Code selbst hinzufügen:

<jsptut:foreach item="Pizza"
list="<%= pizzaList.getPizzas() %>" >
<% Pizza pizza =
(Pizza)pageContext.findAttribute("Pizza"); %>
...
</jsptut:foreach>

Bei der Übergabe von Attributwerten kann man momentan nur zwischen Konstanten und JSP-Ausdrücken wählen. Es ist nicht möglich, direkt eine Bean-Property mit <jsp:getProperty /> zu übergeben. Dies wäre zur Übergabe an das currency-Tag hilfreich gewesen.

Eine Unterstützung der Ausgabe einer HTML-Tabelle, die clientseitige Validierung von HTML-Form-Eingabeparameter, ein Standard für Navigation oder gar die gesamte Definition des Site Layouts sind denkbare Erweiterungen. In der nächsten Zeit ist hier einiges an Vorschlägen zu erwarten. Direkte Datenbankverbindungen und -anweisung in die JSP aufzunehmen, scheint uns allerdings nicht geraten. Dazu eigenen sich besser spezialisierte Controller und eine klare Trennung zwischen Datenbeschaffung und Formatierung (siehe MVC-Architektur im Teil 2). In der JSP-Spezifikation 1.2 soll es eine Standard-TagLib geben. Für tatkräftige Unterstützung wirbt neu das Jakarta-TagLib-Unterprojekt der Tomcat-Referenzimplementierung.

Nicht nur TagLibs sprechen dafür, Web-Applikationen auf Basis von JSP und Java zu implementieren; das sollen die restlichen Abschnitte dieses Tutorials verdeutlichen. Anhand der Java-APIs für die Bearbeitung von XML und Mail ist erkennbar, mit wie wenig Aufwand sich relativ komplexe Funktionen in bestehende Anwendungen einbinden lassen.

Bestandteil des ersten Teil des Tutorials war, dass eine der großen Stärken der JavaServer Pages in der Programmiersprache Java selbst liegt. Diese Tatsache stellt insbesondere dann einen Vorteil dar, wenn die zu entwickelnde Web-Applikation Funktionen implementieren soll, die in Form eines entsprechenden Java API vorliegen.

Schaut man sich die Vielzahl der bisher veröffentlichen APIs an, kann sich jeder Leser leicht vorstellen, dass es kaum einen Bereich gibt, für den nicht eine mehr oder weniger hilfreiche Schnittstelle existiert - oder in Sicht ist. Um diesen Vorteil hervorzuheben, soll der Pizza-Service um einige grundlegende Funktionen erweitert werden.

Nach dem großen Erfolg der ersten beiden Teile dieses Tutorials kann ein einziger Pizza-Lieferant der vielen eingehenden Bestellungen in Zukunft nicht mehr Herr werden. Deshalb ist es notwendig, zusätzliche virtuelle Filialen zu eröffnen, die alle dasselbe Interface nutzen.

Die Grundidee besteht darin, die am Webserver eingegangenen Kundenbestellungen nach Speicherung in der Datenbank an den ‘zuständigen Pizzabäcker’ via E-Mail weiterzuleiten. Als Austauschformat für die Bestellung ist XML vorgesehen, da der Pizzabäcker die Bestellung in diesem Fall mühelos auf Korrektheit und Vollständigkeit überprüfen kann. Es lässt sich deshalb bei der geplanten Erweiterung von einer - zugegebenermaßen kleinen - XML basierten B2B-Lösung sprechen.

Welcher Pizzabäcker letztendlich eine bestimmte Bestellung bearbeiten soll, könnte in der Realität anhand der Lieferadresse des Empfängers bestimmt werden (Stichwort: Lokalisierung). Hier sei auf diese Fallunterscheidung verzichtet.

Wie es scheint, kommen bedingt durch die oben beschriebene Problemstellung zwei völlig neue Aufgabenbereiche hinzu: erstens das Erzeugen einer XML-Version der Bestellung, zweitens automatisches Versenden der XML-Bestellung via E-Mail an den zuständigen Pizzabäcker.

Genau diese beiden Features liegen in Form von Java APIs vor. Hier folgt, wie sich unter Verwendung verschiedener APIs aus einem Objekt der Klasse Order automatisch eine XML-Repräsentation der Bestellung dynamisch erzeugen lässt. Auf Details aus dem Bereich XML selbst soll dabei nur am Rande eingegangen werden, da dies den Rahmen des Tutorials sprengen und genügend Stoff für ein eigenes liefern würde.

Einer der Vorteile von XML-Dateien liegt darin, ihren Inhalt bis zu einem gewissen Grad auf Vollständigkeit und Korrektheit überprüfen zu können. Im Falle der Online-Bestellung kann man automatisch feststellen, dass der Preis der Pizza oder die Größenangabe fehlen.

Generell ist es möglich, zu jedem Dokumenttyp eine so genannte Document Type Definition (DTD) zu erstellen. Darin findet sich eine Beschreibung zum strukturellen Aufbau der Dokumente.

Ohne zugehörige DTD kann ein entsprechender XML Parser lediglich entscheiden, ob das Dokument ‘well-formed’ ist (syntaktische Korrektheit). Beinhaltet das XML-Dokument dagegen einen Verweis auf eine DTD, ist zusätzlich eine Validierung des Dokumentes machbar.

Eine ‘valid’ XML Bestellung erfordert demnach eine Order-DTD. Die sollte den Aufbau einer Bestellung dahingehend beschreiben, dass alle wichtigen Elemente enthalten sind. Darüber hinaus sollte die Möglichkeit gegeben sein, die Bestellungen mehrerer Kunden in einem Dokument zusammenzufassen und den einzelnen Bestellungen eine optionale Beschreibung beizufügen. Auf dem FTP-Server der iX wird eine solche DTD zu finden sein.

Wenn sie für eine Online-Bestellung vorliegt, stellt sich die Frage, wie eine Bestellung aussehen kann. Durch den relativ simplen Aufbau der DTD sind nicht viele Variationsmöglichkeiten für die Gestaltung einer Bestellung gegeben. Jedes Element muss genau einmal in der vorgegebenen Reihenfolge vorkommen. Ausnahmen sind <!ELEMENT List-of-Ingredients (Ingredient+) > und <!ELEMENT Pizza-Online-Service (Description?, Order+) >, wobei das ‘+’ hinter den Elementen Ingredient und Order angibt, dass diese Elemente mindestens einmal vorkommen müssen, aber mehrmals vorkommen dürfen. Dies macht durchaus Sinn, wenn man bedenkt, dass einerseits Sammelbestellungen erlaubt sein sollten und anderseits eine Pizza mit mehreren Zutaten bestückt werden kann. Dass eine Pizza nicht zwingend Zutaten benötigt, drückt das Fragezeichen nach dem Elementnamen aus.

Für die dynamische Erzeugung bietet Java eine Reihe von APIs an, die Sun und einige Drittanbietern umgesetzt haben. Hier kommt die Implementierung der Apache Group namens Xerces zum Tragen. Sie bietet im Gegensatz zu Suns Referenzimplementierung einige nützliche Features an, die bei einen professionellen Einsatz von XML unverzichtbar sind.

Da hier keines dieser Features benutzt wurde, könnte auch jede andere Implementierung verwendet werden, die der Spezifikation entspricht. Der Kasten ‘Java-XML-APIs’ gibt einen kurzen Überblick über die vorhandenen APIs sowie deren Grundfunktionen an.

Mehr Infos

Java-XML-APIs

SAX: Die Simple API for XML stellt ein Event-basiertes Framework zum Parsen von XML-Dokumenten zur Verfügung. Ein SAX-Parser arbeitet das XML-Dokument sequentiell ab, wobei je nach angetroffenem Element unterschiedliche Events ausgelöst werden. Durch die Implementierung entsprechender Event Handler erhält der Entwickler somit die Möglichkeit, auf das Vorkommen spezieller Tags zu reagieren.

DOM: ist im Gegensatz zu SAX ein API, die das XML-Dokument als Document Object Model (Baum-Objekt) darstellt. Dies ermöglicht zusätzlich die Generierung und Manipulation von XML-Dokumenten.

JAXP: kann als eine Art Zusatz-API angesehen werden, die einen einheitlicheren und vor allem herstellerneutralen Zugriff auf die beiden eben beschriebenen APIs ermöglichen soll. So genannte Builder und Factories verbergen die herstellerspezifische Implementierung vor dem API-Nutzer. Werden lediglich die spezifizierten API-Methoden genutzt, lassen sich die eines Herstellers leicht durch die eines anderen austauschen.

JDOM: JDOM ist ein relativ neues OpenSource-API, das nach einer Spezifikation von Jason Hunter und Brett McLaughlin entstanden ist - unter Mithilfe von James Duncan Davidson, dem Autor der JAXP-Spezifiaktion. Ziel von JDOM ist es unter anderem, die teilweise recht umständlichen Methoden zur Generierung und Manipulation eines DOM zu vereinfachen. Darüber hinaus ist JDOM, anders als die DOM API, speziell auf die Sprache Java zugeschnitten und somit in einigen Bereichen stark optimiert.

Der Pizza-Online-Dienst soll dynamischen Inhalt erstellen, deshalb scheidet die Verwendung der SAX API (Simple API for XML) aus. Mit ihrer Hilfe lässt sich zwar ein bestehendes XML-Dokument analysieren und auf seine Korrektheit prüfen, die Manipulation oder Generierung eines XML-Dokumentes dagegen ist nicht vorgesehen.

Was bleibt, ist die DOM-API (Document Object Model). Zusätzlich wird die JAXP-API verwendet, die ein höheres Abstraktionslevel bietet und somit eine herstellerunabhängige Sicht auf die Klassen ermöglicht (siehe Kasten ‘Java-XML-APIs’).

Schon in den ersten beiden Teilen des Tutorials setzt sich eine Online-Bestellung aus mehreren Komponenten zusammen. Da wäre zunächst die Basis-Pizza. Hinzu kommen optional eine fast beliebige Anzahl von Zutaten. Auch die Lieferadresse darf nicht fehlen.

Was liegt da näher, als eine Implementierung zu wählen, bei der jede der einzelnen Komponenten beziehungsweise jedes Java-Objekt selbstständig in der Lage ist, sich als XML-Knoten eines Document Object Models zu repräsentieren. Bei dieser Art der Implementierung rufen zusammengesetzte Objekte lediglich die entsprechenden Methoden ihrer einzelnen Teile auf und delegieren somit den ‘Auftrag’ zur XML-Generierung an die untergeordneten Elemente.

Innerhalb eines DOM wird zunächst jeder XML Tag durch ein Objekt vom Typ Element repräsentiert, das weitere Elemete beinhalten kann. Zur Erzeugung eines Elements ist das zugehörige DOM vom Typ Document nötig. Für den Header der XML-Generierungsmethode ergibt sich daher folgende Deklaration:

public Element generateXML(Document document)

Die Deklaration ist in einem Java-Interface namens XMLModelInterface hinterlegt, das alle Klassen implementieren, die eine XML-Darstellung ihrer Instanzen anbieten. Ein nur online vorhandenes Listing zeigt die Generierung eines XML Elements für die Klasse Customer.

Was fehlt ist das Zusammenfügen der einzelnen Knoten sowie eine zentrale Stelle, die das initiale Dokument erzeugt und verwaltet. Genau diese Aufgaben übernimmt die Klasse XMLOrderBuilder im Package de.ix.jspTutorial.xml unter Verwendung der Java-DOM- sowie der JAXP-API (Listing 6).

Mehr Infos

Listing 6: XMLOrderBuilder

/**
* Build a new DOM containing all important
* information of a given order
*
* @param order order, to be represented
* @return new DOM document
*/
public Document createXMLOrder(Order order) {

Document document = null;

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try {

DocumentBuilder builder = factory.newDocumentBuilder();
document = builder.newDocument();

// add root element
Element root = (Element) document.createElement(XML_ROOT_TAG);
document.appendChild(root);

// add order tag and all sub tags
Element orderElement = order.generateXML(document);
root.appendChild(orderElement);

} catch (ParserConfigurationException ex) {
// Fehlerbehandlung ...
}

return document;
}

Mit Hilfe der DocumentBuilderFactory, einem Bestandteil des JAXP-API, wird zunächst ein neues XML Document erzeugt, wobei das Factory-Pattern die implementierungsspezifischen Details des Herstellers verbirgt:

DocumentBuilder builder = factory.newDocument Builder();
document = builder.newDocument();

Anschließend wird dem XML-Dokument ein Root-Element, in diesem Fall ein <Pizza-Online-Service>-Tag, hinzugefügt.

// add root element
Element root = (Element) document.createElement (XML_ROOT_TAG);
document.appendChild(root);

Im letzten Schritt braucht aufgrund des oben beschriebenen Verfahrens zur rekursiven XML-Generierung lediglich die Methode generateXML(...) des aktuellen Order-Objekts aufgerufen und das zurückgelieferte Objekt vom Typ Element unter das Root-Element gehängt zu werden.

// add order tag and all sub tags
Element orderElement = order.generateXML (document);
root.appendChild(orderElement);

Leider gibt es bei der Spezifikation der XML-APIs ‘Lücken’, die zusätzlichen Implementierungsaufwand bedeuten, wenn es um die dynamische Erzeugung/Ausgabe eines Dokuments geht. Da diese Thematik aber nicht Teil des vorliegenden Tutorials sein soll, werden die Problemfelder nur kurz angesprochen und lediglich einfache Lösungen aufgezeigt, die in der Praxis nicht unbedingt die beste Wahl darstellen.

Einem neu erzeugten XML-Dokument kann dynamisch keine DTD zugeordnet werden. Es existiert zwar ein Knotentyp DTD, dieser kann aber nur für bestehende Dokumente ausgelesen werden. Es gibt innerhalb der Spezifikation leider keine Möglichkeit, einen solchen DTD-Knoten dynamisch zu erzeugen und diesen einem XML-Dokument zur Laufzeit hinzuzufügen. Somit bleibt nur, eine eigene Klasse dieses Typs zu schreiben, die die entsprechenden Informationen enthält. Um welche es sich dabei handelt, kann man dem entsprechenden Interface namens DocumentType entnehmen. Ein weiteres Manko der aktuellen API stellt das Fehlen einer standardisierten Ausgabe dar. Befindet sich ein XML-Dokument als DOM innerhalb des Speichers, gibt es laut Spezifikation keine Methode, daraus direkt eine XML-Datei oder einen entsprechenden String zu erzeugen, der dieses DOM repräsentiert. Einzelne Anbieter der XML Parser haben diese Problematik erkannt und in der Regel ihre APIs um entsprechende Methoden erweitert. Nutzt man sie, macht man sich automatisch abhängig von einer speziellen Implementierung.

Online zeigt ein Listing einen Ausschnitt aus einer ‘Alternative’ zur Verwendung herstellerspezifischer Ausgabemethoden. Die in diesem Listing gezeigte Methode generateXMLString(...) wandert rekursiv durch ein XML-Dokument und konstruiert Stück für Stück eine String-Repräsentation des DOM. Diese Methode ist in Anlehnung an eine ähnliche Methode in [#lit2 [2]] entstanden.

Neben dem Erzeugen eines XML-Dokuments soll das Versenden einer E-Mail als zweites Beispiel für das Einbinden vorhandener Java-APIs dienen. Auch an dieser Stelle geht es wieder weniger um die technischen Details an sich, als vielmehr darum zu zeigen, mit wie wenig Aufwand sich relativ mächtige Funktionen in JSP- und Servlet-basierte Applikationen integrieren lassen.

Eine neue Klasse namens MailSender ist als eine Art E-Mail implementiert, die selbstständig in der Lage ist, sich zu versenden. Neben einigen privaten Attributen, die zur genaueren Beschreibung der E-Mail dienen, besteht die Klasse hauptsächlich aus einem Konstruktor zur Erzeugung und Initialisierung einer MailSender-Instanz, sowie der zentralen Methode sendMail(), die sich um das Versenden der enthaltenen Mail kümmert.

Listing 7 zeigt den Aufbau der Methode sendMail(). Nachdem die einzelnen Attribute wie Sender, Empfänger, Text, et cetera innerhalb der Methode sendMail() gesetzt sind, reicht ein Aufruf der statischen Methode send(...) der Klasse javax.mail.Transport, um die Mail tatsächlich zu verschicken. Was genau dabei im Hintergrund passiert, lässt sich erahnen, wenn man die Debugging-Ausgabe der Transport.send(...) Methode betrachtet (siehe Abb. 2). Es zeigt sich wieder, dass das vorhandene Java-API beziehungsweise deren Klassen und Methoden dem Entwickler eine Menge Zeit und Arbeit ersparen.

Mehr Infos

Listing 7: E-Mail leicht gemacht

/** 
* send included mail
*/
public void sendMail() {

Properties props = new Properties();
props.put("mail.smtp.host", this.getSmtpServer());
Session session = Session.getDefaultInstance(props, null);
session.setDebug(MAIL_DEBUG);

try{
Message message = new MimeMessage(session);
MimeBodyPart mimeBodyPart = new MimeBodyPart();
Multipart multiPart = new MimeMultipart();
InternetAddress addressFrom =
new InternetAddress(this.getSender());
InternetAddress[] addressTo = {new InternetAddress(MAIL_RECIPIENT)};

message.setFrom(addressFrom);
message.setRecipients(Message.RecipientType.TO, addressTo);
message.setSubject(this.getSubject());

mimeBodyPart.setText(this.getMessageText());
multiPart.addBodyPart(mimeBodyPart);

Vector attachFiles = this.getAttachments();

// attach files
if (! attachFiles.isEmpty() ) {
...
}

message.setContent(multiPart);
Transport.send(message);

} catch(javax.mail.MessagingException ex){
System.out.println("Messaging Fehler: " + ex);
} catch(Exception ex) {
System.out.println("General Exception: " + ex);
}
}

Der erweiterte Workflow für die Bestellung einer Pizza zieht einige Änderungen in der Schaltzentrale RequestProcessor nach sich Listing 8. Für die Erzeugung der XML-Bestellung sind - bedingt durch die Kapselung der Funktionen - lediglich zwei zusätzliche Zeilen notwendig. Das Versenden der Mail benötigt kaum mehr Zeilen.

Mehr Infos

Listing 8: Neuer Order Workflow

/**
* Handler-method for order request. Saves the current order and
* changes also the current customer if necessary.
*
* Additional create XML order and send this to via eMail to
* pizza online service.
*
* @param request Current httpRequest
*/
public void handleOrderEvent(HttpServletRequest request) {
...
Order order = pizzaAccessor.newOrder(newCustomer,personalPizza);
...
// create xml order
XMLOrderBuilder xmlOrder = new XMLOrderBuilder();
Document xmlDocument = xmlOrder.createXMLOrder(order);

// mail order
if (USE_EMAIL) {

// set mail values like sender, message text, et cetera
...
String messageText = xmlOrder.generateXMLString(
(Node)xmlDocument, "");

MailHelper mailHelper = new MailHelper( sender,
recipients, smtpServer, subject, messageText, attchFiles);
mailHelper.sendMail();
}
}

Um interessierte Leser ohne permanenten Internetanschluss nicht automatisch von dem dritten Teil des Tutorials auszuschließen, wird die Verwendung des Mail-Features über das Flag USE_MAIL gesteuert. Dieses Flag kann mit dem Interface MailConstants im Package de.ix.jspTutorial.Constants gesetzt werden. Ebenfalls in diesem Interface enthalten sind die Grundeinstellungen für den zu verwendenen SMTP-Server zum Versenden der Mail, sowie die E-Mail-Adresse des virtuellen Pizzabäckers. In der Praxis würde es sich anbieten, diese Werte in Property- oder XML-Dateien zu hinterlegen, die die Applikation zu Anfang einliest.

Wiederum ein geänderter Workflow: Die generierte Bestellung in XML und die E-Mail an den Pizzabäcker sind hinzugekommen.

Mit diesem letzten Teil ist das Ende des JSP-Tutorials erreicht. Ausgehend von einfachen JSPs, über den Aufbau einer Model-View-Controller Architektur bis hin zur Einbindung von TagLibs und vorhandener Java APIs wie Java Mail und XML wurde deutlich, welche Problme bei der Planung und Realisierung umfangreicher Webprojekte berücksichtigt werden sollten. Es zeigte sich, dass JSPs in Kooperation mit der Programmiersprache Java und all ihren Features eine gute Wahl darstellen und die Aufteilung der Entwicklungsarbeit auf mehrere spezialisierte Personen/Rollen ermöglichen.

Theorie und Praxis müssen nicht unbedingt immer übereinstimmen. Besonders bei der Verwendung von TagLibs treten große Probleme hinsichtlich der Kompatibilität beim Einspielen in verscheidene JSP-/Servlet-Engines auf, was nicht zuletzt darin begründet liegt, dass viele der grundlegenden Spezifikationen sich noch im Fluss befinden.

Es bleibt deshalb abzuwarten, wie sich diese Bereiche zukünftig entwickeln und sich die Wartezeit bis dahin mit der einen oder anderen Pizza zu vertreiben. Guten Appetit.

Lars Röwekamp
ist als IT-Berater mit den Schwerpunkten Neue Technologien und OO/Java für seine Firma openKnowledge tätig.

Peter Roßbach
ist freiberuflicher IT-Berater für die Konzeption, Organisation und Entwicklung von Java-Web-Anwendungen.

[1] Brett McLaughlin, Mike Loukides; Java and XML; Sebastopol (O’Reilly) 2000

[2] Lars Röwekamp, Peter Roßbach; Web-Programmierung; Quattro Stagioni; JSP-Tutorial, Teil 1: Grundlagen der Java Server Pages

[3] Lars Röwekamp, Peter Roßbach; Web-Programmierung; Tonno e cipolla; JSP-Tutorial, Teil 2: Model-View-Controller und Datenbankintegration

Mehr Infos

iX-TRACT

  • Dieser dritte Teil des ‘JavaServer Pages’-Tutorials widmet sich vor allem den TagLibs, die die Zusammenarbeit zwischen Webdesigner und Entwickler erleichtern beziehungsweise ihre Aufgaben trennen.
  • Weitere Bestandteile sind eine XML-Version der Online-Pizza-Bestellung und Mail-Versand an den ‘Pizzabäcker’.
  • Der Workflow des Pizza-Online-Dienstes muss durch die Ergänzungen eine weitere Veränderung erfahren.

(hb)