Aus Linux-Magazin 05/2003

Die JSP Standard Tag Library trennt Code und Darstellung

Die saubere Trennung von Programmlogik (Servlets) und Anzeige (Java Server Pages) ist nur in sehr einfachen Server-Anwendungen streng durchzuhalten, in der Praxis aber meist unmöglich. Taglibs bieten hierfür eine elegante Lösung.

Das Beispiel aus den letzten Coffee-Shop-Folgen (siehe [1] und [2]) verwendet das MVC-Paradigma (Model, View, Controller), um die Programmlogik sauber vom HTML-Code zu trennen. Spätestens bei der Ausgabe von Suchergebnissen ist diese saubere Trennung aber nicht mehr möglich, da die Ergebnisseite eine variable Anzahl von Treffern in einer Tabelle anzeigen muss.

Die klassische Lösung des Problems mit JSP-Mitteln sieht dann ähnlich aus wie in Listing 1. Scriptlets innerhalb der JSP-Seite sind ein mächtiges Mittel, sie erlauben den Einsatz aller Java-Features. Aber selbst bei einfachen Problemen wird die JSP-Seite schnell kompliziert und sowohl die HTML-Struktur als auch der Code geraten unübersichtlich.

Tag-Libraries, meist kurz Taglibs genannt, sind schon seit der ersten JSP-Version die Lösung des Problems. Sie kapseln die Programmlogik innerhalb einer JAR-Bibliothek. Der Webautor verwendet dann nur noch die definierten Tags – und den Rest erledigt die Bibliothek im Hintergrund.

Diese Folge des Coffee-Shops stellt das Konzept von Taglibs, ihre Installation und Konfiguration vor. Insbesondere die JSP Standard Tag Library (JSTL) steht dabei im Mittelpunkt. In der nächsten Folge geht es dann um die Programmierung eigener Taglibs.

Was ist ein Tag?

Auszeichnungssprachen wie HTML oder SGML und ihre Nachkommen kennen den Begriff Tag, um Dokumente zu strukturieren. In HTML stellt zum Beispiel das »<h1>«-Tag eine Überschrift erster Ordnung dar. Jede HTML-Version definiert die verfügbaren Tags und ihre Beziehung zueinander.

HTML ist im Gegensatz zu XML sehr restriktiv: Eigene Tags sind nicht erlaubt. Der Browser-Krieg wurde aber lange Zeit mit dem unlauteren Mittel der proprietären Erweiterung geführt. Neue oder erweiterte Tags gaben den Webautoren mehr Mittel in die Hand, um ihre Seiten zu gestalten. Der Benutzer hatte das Nachsehen, er war auf den Browser des Herstellers X angewiesen, andernfalls stellte sein Browser die Seite falsch oder gar nicht dar. Dabei zahlen auch die Webautoren und ihre Auftraggeber die Zeche: Je nach Browser müssen sie unterschiedliche Seiten bauen, der Kunde ist schließlich König.

Taglibs erlauben ebenfalls die Definition eigener Tags. Sie sollen aber nicht die Kompatibilität weiter untergraben, sondern werden letztlich zu normalem HTML aufgelöst. Ein Webautor könnte also ein Firmenlogo in eine JSP-Seite entweder über

<img src="firma.gif"/>

oder über

<logo/>

einbinden. Die hinter dem »<logo/>«-Tag stehende JAR-Bibliothek erzeugt dann den klassischen HTML-Code. Eigene Tags können auch Attribute enthalten, auch Tags mit Kindelementen (» <mein Tag>Text</mein Tag>«) sind natürlich möglich.

Das Logo-Beispiel ist etwas trivial und trifft den Kern nur zum Teil, da es nicht darum geht, HTML-Code wieder in Java zurückzuverlagern, sondern vielmehr komplizierte Logik zu kapseln. Ein Beispiel in dieser Richtung könnte ein Chart eines Aktienkurses sein:

<chart wpkn="abcd"/>

Hier werden aus einer Datenbank die Daten über die Wertpapierkennung abgerufen, dann wird on the fly eine Grafik erzeugt und mit einem normalen Image-Tag dargestellt.

Die Standard Tag Library

Auch wenn aus Sicht des Browsers eigene Tags unsichtbar bleiben, so begibt sich doch der Webautor in eine neue Abhängigkeit. Ob die Taglibs vom Hersteller der Entwicklungsumgebung oder gar der Servlet-Engine stammen: Aus der Sicht des Autors sind sie eine proprietäre Erweiterung. Java wäre aber nicht Java, wenn es hier nicht zu einer Standardisierung gekommen wäre.

Der Java Community Process (JCP) diskutiert und erweitert sowohl Java als Sprache als auch die zentralen Standards rund um Java. Die Arbeitsgruppe JSR-052[3] definierte die JSP Standard Tag Library (JSTL). Zurzeit ist die Version 1.0 aktuell. Die JSTL-Referenzimplementation wird – nicht besonders überraschend – innerhalb des Jakarta-Projekts gepflegt und ist somit frei verfügbar. Auf diese Weise ist auch ein perfektes Zusammenspiel mit dem Tomcat-Server garantiert.

Das Jakarta-Taglib-Projekt[4] geht aber über die JSTL noch hinaus. Das Projekt ist in folgende Kategorien aufgeteilt:

  • JCP Standardized Tag Libraries – also die JSTL-Tags
  • Supported Tag Libraries – weitere Tags, die offiziell vom
    Jakarta-Taglibs-Projekt unterstützt werden
  • Tool Extensions – Erweiterungen für
    Web-Entwicklungsumgebungen
  • Sandbox – das sind neue Bibliotheken, die noch nicht offiziell
    unterstützt werden

Da sich Taglibs ganz hervorragend als wieder verwendbare Komponenten eignen, hat sich schon eine große Liste von verfügbaren Taglibs angesammelt. Ein Nachteil ist die wachsende Anzahl von JAR-Bibliotheken, die für eine Anwendung in der richtigen Version zusammengestellt werden müssen.

Tabelle
1: Core-Tags von JSTL

 

Name

Beschreibung

<c:catch>

Abfangen von Exceptions

<c:choose>

Bedingte Verarbeitung, zusammen mit <c:when> und
<c:otherwise> (Beispiel im Haupttext). Die Testbedingung wird
als Attribut im <c:when>-Tag formuliert.

<c:forEach>

Auswertung der eingeschlossenen Tags über eine durch eine
Variable übergebenen Menge (Beispiel im Haupttext)

<c:forTokens>

Analog zu <c:forEach> wird hier über Tokens eines
Strings iteriert

<c:if>

Ausgabe des eingeschlossenen Textes, wenn die Bedingung wahr
ist. Ein Else-Zweig existiert nicht. Falls dieser notwendig ist,
muss <c:choose> verwendet werden.

<c:import>

Einbinden von internen (analog <jsp:include>) oder
externen Ressourcen

<c:otherwise>

Siehe <c:choose>

 

<c:out>

Ausgabe von Text

<c:param>

Hinzufügen von Request-Parametern zu URLs. Wird zusammen
mit <c:import>, <c:redirect> und <c:url>
verwendet.

<c:redirect>

Senden einer Redirect-Response an den Client
»redirect«

<c:remove>

Entfernen einer Variablen aus einem Gültigkeitsbereich
(Scope: Page, Request, Session, Application)

<c:set>

Setzen einer Variablen oder eines Attributs eines Objekts (Map
oder Bean)

<c:url>

Encodiert und konvertiert URLs

<c:when>

Siehe <c:choose>

 

Mehr als neue Tags

Die JSTL hat vier Teile: Core enthält die zentralen, oft verwendeten Tags. XML Processing unterstützt die XML-Verarbeitung, etwa Transformationen oder den Zugriff auf einzelne Elemente. Der I18N-Teil ist für die Formatierung von lokalisierten Texten zuständig und mit SQL ist der Lese- und Schreibzugriff auf relationale Datenbanken möglich.

Da eine ausführliche Einführung in alle Tags den Rahmen eines Coffee-Shops sprengen würde, soll im Folgenden nur von wenigen Tags der Core-Bibliothek die Rede sein. Tabelle 1 zeigt eine Übersicht der Core-Tags.

Neben den eigentlichen Tags enthält die JSTL außerdem noch die so genannte Expression Language (EL). Attribute von Tags können über die EL gewissermaßen dynamisch berechnet werden. Ein Beispiel: Das »value«-Attribut des Core-Tags »out« wird über

<core:out value="1+2 = ${1+2}" />

gesetzt. Die Ausdrücke der EL stehen immer zwischen den Zeichen »${« und »”}«. Der wichtigste Anwendungsfall ist der Zugriff auf Variablen, insbesondere auf Beans. Soll beispielsweise der Nachname einer Person ausgegeben werden, ist das mit der EL ganz einfach zu bewerkstelligen:

<core:out value="Nachname: ${p.lastName}" />

Die Instanz »p« ist dabei ein Objekt der Bean-Klasse »Person« mit dem Attribut »lastName«. Typumwandlungen finden automatisch statt.

Die EL ist (zurzeit) nicht Teil des JSP-Standards. Deshalb ist die EL nur mit solchen Tags einsetzbar, die explizit die EL unterstützten. Auch die JSTL gibt es in einer zweiten Inkarnation (RT steht für Request Time). Diese Variante hat zwar dieselbe Funktionalität, die Attributwerte werden allerdings über Scriptlets gesetzt:

<core_rt:out value=
'<%= "Nachname: " + p.getLastName() %>' />

In diesem Fall ist es der Container, der den Attributwert berechnet, nicht der Java-Code hinter dem Tag. Die RT-Version hat deshalb eine höhere Performance, führt aber auch zu unübersichtlicherem Code.

Listing 1: Java
innerhalb einer JSP-Seite

01: <%@ page language="java" contentType="text/html" %>
02: <html>
03: <body>
04:
05: <h1>Eine Tabelle</h1>
06:
07:   <p>
08:   <form method="GET"
09:         action="process.action"
10:    name="queryResultsForm">
11:     <table border="1" width="100%">
12:       <th>Select</th>
13:       <th>Number</th>
14:
15:       <% for (int i=0; i<10; ++i) { %>
16:         <tr>
17:           <td align="center"><input type="checkbox"
18:               name="row"
19:               <%= "value = " + i %> />
20:           <td>Row: <%= i %></td>
21:         </tr>
22:       <% } %>
23:     </table>
24:   </form>
25:   </p>
26:
27: </body>
28: </html

Die JSP-Sicht

Mit einer so genannten Taglib-Directive innerhalb der JSP-Seite deklariert man einen Namensraum für die Tags einer Taglib:

<%@ taglib uri=
"http://java.sun.com/jstl/core" prefix="c" %>

Verwendet ein JSP-Autor dann ein Tag der Taglib, setzt er einfach das Präfix vor den Tag-Namen:

<c:out value="1+2 = ${1+2}" />

Durch das Präfix werden Namenskollisionen vermieden. Auch wenn das Präfix frei wählbar ist (im Beispiel weiter oben etwa »core« statt »c«), haben sich die Präfixe »c«, »x«, »fmt« und »sql« für die vier Teile Core, XML, I18N und SQL der JSTL eingebürgert. Die RT-Version der JSTL verwendet dieselben Präfixe, jeweils ergänzt um »_rt«.

Die URI ist dagegen nicht frei wählbar, da sie die Taglib identifiziert (der Autor der Taglib vergibt die URI). Für die URI ist jeder eindeutige String möglich und auch wenn ein »http« darin vorkommt, bedeutet das noch nicht, dass die JSP-Seite dann auf irgendwelche Webseiten zugreifen muss.

Listing 2: Ausgabe
von Suchergebnissen

13: <%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
14:
15: <jsp:useBean id="queryResults"
16:              scope="request"
17:              class="java.util.ArrayList"
18: />
19:
20: <html>
21:
22: <%@ include file="headerPanel.jsp" %>
23:
24: <!-- start of body -->
25:
26: <%@ include file="navigationPanel.jsp" %>
27:
28: <h1>Result of query:</h1>
29:
30: <c:choose>
31:  <c:when test="${not empty queryResults}">
32:   <form method="GET"
33:         action="<c:url value="process.action" />"
34:         name="queryResultsForm">
35:     <table border="1" width="100%">
36:       <th>Select</th>
37:       <th>First Name</th>
38:       <th>Last Name</th>
39:       <th>Email Address</th>
40:       <c:forEach items="${queryResults}" var="current">
41:         <tr>
42:           <td align="center"><input  type="checkbox"
43:               name="row"
44:               value="<c:out  value="${current.websheetId}"/>" />
45:           <td><c:out value="${current.firstName}"/></td>
46:           <td><c:out value="${current.lastName}"/></td>
47:           <td><c:out value="${current.emailAddress}"/></td>
48:         </tr>
49:       </c:forEach>
50:     </table>
51:
52:     <p>
53:       <input type="hidden" name="next"  value="false"/>&nbsp;
54:       <input type="submit" name="delete"  value="delete"/>&nbsp;
55:       <input type="submit" name="modify" value="modify"/>&nbsp;
56:       <input type="submit" name="details" value="show details"/>&nbsp;
57:       <input type="reset" value="reset"/>
58:     </p>
59:   </form>
60:  </c:when>
61:
62:  <c:otherwise>
63: <p>Sorry, no hits!</p>
64:  </c:otherwise>
65: </c:choose>
66:
67: <!-- end of body -->
68:
69:
70: <%@ include file="footerPanel.jsp" %>
71:
72: </html>

Download und Installation

Die Referenzimplementation ist auf der Jakarta-Webseite[4] verfügbar, aktuell ist hier die Version 1.0.3. Informationen sind auch über die JSTL-Homepage bei Sun[5] erhältlich, doch wer sich die Links ansieht, stellt schnell fest, dass diese oftmals ihrerseits nur auf die Jakarta-Seiten zeigen.

Wie es bei Jakarta-Software üblich ist, beschränkt sich die Installation auf ein simples Auspacken, zum Beispiel unterhalb »/usr/local«. Wieder bietet es sich dabei an, einen symbolischen Link von »/usr/local/jstl« auf »/usr/local/jakarta-taglibs/standard-1.0.3« zu setzen. Das erleichtert spätere Updates, ohne jedes Mal die Pfade in den Build-Dateien anpassen zu müssen.

Die Einbindung der JSTL in eigene Applikationen ist seit dem JSP-Standard 1.2 sehr einfach geworden. Die JAR-Bibliotheken der Taglib landen im »WEB-INF/lib«-Verzeichnis der jeweiligen Anwendung. Mehr ist nicht nötig. Im Rahmen des Build-Prozesses passiert dies genau ein Mal, typischerweise im »prepare«-Task.

Was ist drin?

Ohne XML geht es auch bei Taglibs nicht. Der Inhalt einer Taglib (also verfügbare Tags, Attribute, URI und so weiter) muss beschrieben sein, damit der JSP-Compiler den richtigen Java-Code verwenden kann. Die XML-Datei heißt Tag Library Descriptor und wird mit TLD abgekürzt. Wenn sich die TLD-Datei im »META-INF«-Verzeichnis der JAR-Datei befindet, ist – wie oben für die JSTL-Taglibs beschrieben – weiter nichts zu tun. Ansonsten muss man die TLD-Datei noch ins »WEB-INF«-Verzeichnis der Anwendung kopieren.

Obwohl sich die JSTL-Bibliotheken an den Standard halten, liegen die TLD-Dateien trotzdem nochmals getrennt im Unterverzeichnis »tld« der JSTL-Distribution vor. Das ist einerseits nützlich, da sie gewissermaßen die letztinstanzliche Referenz der Taglib-Eigenschaften darstellen und andererseits die Installation der Taglibs für Anwendungen erleichtern, die in einem JSP-1.1-Container laufen sollen. Wie hier die Einbindung in die Applikation aussieht, ist in dem Kasten “Taglibs in 1.1-Containern” näher beschrieben.

Abbildung 1: Die Ausgabe von Suchergebnissen.

Abbildung 1: Die Ausgabe von Suchergebnissen.

Ein Beispiel

Nach so viel Theorie zurück zu der Websheet-Beispielapplikation aus den beiden letzten Folgen des Coffee-Shops, der Quellcode ist wie immer über[6] verfügbar. Einer der Anwendungsfälle ist die Suche nach Datensätzen und die Anzeige der Suchergebnisse. In Listing 2 ist der entsprechende JSP-Code der Ergebnisseite zu sehen, Abbildung 1 zeigt die Umsetzung in einem Browser.

Die Seite verwendet eine Reihe der Core-Tags. »<c:choose>« zusammen mit »<c:when>« und »<c:otherwise>« implementiert ein einfaches If-then-else, wie es aus Programmiersprachen bekannt ist (es gibt auch noch ein »<c:if>«, jedoch ohne Else-Zweig). In dem Beispiel wird die Ergebnistabelle nur ausgegeben, wenn tatsächlich Ergebnisse vorliegen.

Innerhalb der Ergebnistabelle erfolgt die Ausgabe der Tabellenzeilen mittels »<c:forEach>« (Zeilen 40 bis 49). Das ist ein sehr mächtiges Tag, da es als »items«-Attribut verschiedenste Objektmengen (zum Beispiel Collections, Strings, Arrays) erlaubt. Der Zugriff auf die einzelnen Objekte erfolgt über die im »var«-Attribut genannte Variable. Sind die Objekte selbst Beans, kann auf die Objekt-Attribute wieder mit der EL zugegriffen werden (Zeilen 44 bis 47). Das Core-Tag »<c:out>« wird hier für die Ausgabe verwendet.

URL-Rewriting ist mit der Core-Bibliothek ebenfalls einfach geworden. Zeile 33 schreibt die Action-URL um, damit die Anwendung auch ohne Cookies funktioniert.

finally{}

Schon das hier gezeigte kleine Anwendungsbeispiel zeigt die Mächtigkeit von Taglibs. Insbesondere in Vergleich zu Listing 1 fällt die deutlich bessere Lesbarkeit auf. Wer sich intensiver mit der JSTL beschäftigen möchte, sei auf die Online-Texte[4],[5] oder[7] verwiesen. Auch das Buch von Hans Bergsten[8] ist empfehlenswert.

In eigenen Projekten funktionieren Taglibs allerdings nur, wenn Webautoren und Java-Programmierer eng zusammenarbeiten. Gerade wenn Anwendungen wie so oft unter hohem Zeitdruck entstehen, bieten sich Quick-and-dirty-Lösungen mit Javacode innerhalb von JSP-Seiten geradezu an – manchmal sind sie tatsächlich die einzige Lösung, um enge Termine zu halten. Dabei sind Taglibs sehr einfach zu implementieren. Wie das im Einzelnen funktioniert, beschreibt die nächste Folge an einem kleinen Beispiel. (uwo)

Taglibs in
1.1-Containern

Um die JSTL in eine Webapplikation für 1.1-Container einzubinden, sind drei Schritte notwendig. Zuerst kopiert man wie üblich die JAR-Dateien aus dem »lib«-Unterverzeichnis der JSTL-Distribution nach »WEB-INF/lib«. Ähnlich bei den TLD-Dateien. Diese liegen im »tld«-Unterverzeichnis der Distribution und landen direkt im »WEB-INF«-Verzeichnis. Im Entwicklungsprozess bietet sich eine Ant-Task für die Durchführung der notwendigen (einmaligen) Kopierschritte an.

Die Verknüpfung mit den Taglib-Directiven in den JSP-Seiten erfolgt dann über Taglib-Einträge in der Anwendungs-Konfigurationsdatei »web.xml«. Letztlich muss der Servlet-Container ja wissen, in welcher Klasse er die Implementation der Tags suchen muss. Listing 3 zeigt dies für die Core-Bibliothek.

Die Taglib-URI muss identisch zum URI-Attribut der Taglib-Directive sein (und sollte auch der URI aus der TLD entsprechen). Das Taglib-Location-Tag (Zeile 27 in Listing 3) verweist auf die entsprechende TLD-Datei.

JSP-1.2-Container sind etwas intelligenter. Sie durchsuchen alle JAR-Bibliotheken nach TLD-Dateien (innerhalb des jeweiligen »META-INF«-Verzeichnisses) sowie das »WEB-INF«-Verzeichnis der Anwendung. Die Zuordnung Tag zu Java-Klasse erfolgt dann über die Kette Tag-Präfix -> URI der Taglib-Directive -> URI innerhalb der TLD-Datei -> Zuordnung Java-Klasse je Tag-Name innerhalb der TLD-Datei.

Infos

[1] Coffee-Shop, “Ein starkes Paar – Java Server Pages im Zusammenspiel mit Servlets”: Linux-Magazin 03/03, S. 106

[2] Coffee-Shop, “Dreh-Moment – Objektpersistenz mit dem Torque-Framework”: Linux-Magazin 04/03, S. 104

[3] JSTL-Standardisierung: [http://www.jcp.org/jsr/detail/52.jsp]

[4] Jakarta-Taglib-Projekt: [http://jakarta.apache.org/taglibs/]

[5] Die JSTL-Homepage bei Sun: [http://java.sun.com/products/jstl/]

[6] Quellcode von Websheet: [http://www.bablokb.de/LinMag/websheet.tgz]

[7] Dreiteiliger JSTL-Artikel auf dem O\’Reilly Network: [http://www.onjava.com/pub/a/onjava/2002/08/14/jstl1.html]

[8] Hans Bergsten: “Java Server Pages”, O\’Reilly 2002

Der
Autor

Bernhard Bablok arbeitet bei der Allianz Versicherungs AG im Bereich Data-Warehouse-Systeme. Wenn er nicht Musik hört, mit dem Radl oder zu Fuß unterwegs ist, beschäftigt er sich mit Themen rund um Objektorientierung. Er ist unter [coffee-shop@bablokb.de] zu erreichen.

LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben