Vorlagen mit Java drechseln
Artikel erschienen in Swiss IT Magazine 2007/05
Stellt sich in einem Projekt die Anforderung, HTML, SQL, JavaScript oder anderen textuellen Output zu generieren, wird der Code einfach durch Zusammenfügen von Strings sozusagen in «traditioneller» Servlet-Manier erzeugt. Dies ist beispielsweise immer noch häufig beim Erstellen von SQL-Scripts zu beobachten. Es stellt sich dabei aber sehr schnell heraus, dass diese Methode nicht nur äusserst mühsam, sondern auch noch sehr fehleranfällig ist. Weiter ist der Code schlecht zu lesen und schlecht zu warten – der Einsatz von reinem JSP ist ähnlich problematisch.
Besser ist es oft, einen auf Templates basierenden Ansatz zu wählen. Dabei trennt man Programmcode und «Mustercode» beispielsweise für die Code-Generierung von HTML oder SQL. Im Programm wird dann der Mustercode geladen und die vorbereiteten Stellen zur Laufzeit mit den gewünschten Daten gefüllt.
Die gute Nachricht ist, dass man hier nicht selbst Hand anlegen muss, weil es bereits eine Anzahl leistungsfähiger Template-Engines gibt, die einem eine Menge Arbeit abnehmen. Für Java wären dies beispielsweise Apache Velocity, Freemarker oder Webmacro.
Apache Velocity ist ein Top-Level-Apache-Projekt und entstand vor allem aus der Anforderung, Muster-basierte HTML-Seiten erstellen zu können.
Zwar existierte mit Webmacro schon ein recht leistungsfähiges Tool, das jedoch den eminenten Nachteil hatte, unter der GPL zu stehen. Denn eine Bibliothek unter der GPL ist eine sehr kritische Angelegenheit. Diese Lizenz verträgt sich einerseits kaum mit anderen Open-Source-Lizenzen und ist andererseits viral, das heisst, das gesamte Projekt, das auf Webmacro basiert, wäre dann unter die GPL zu stellen.
Da dies für die meisten Anwendungsfälle in der Praxis unannehmbar ist, wurde das Velocity-Projekt gestartet. Dieses wird nun unter der bekannten Apache-Lizenz veröffentlich. Mittlerweile scheint Velocity auch über die deutlich aktivere Community zu verfügen.
Einer der Haupteinsatzbereiche von Velocity ist die Generierung von HTML-Seiten. Dies kann sowohl statisch oder auch im Web-Applikations-Umfeld geschehen. Velocity kann auch in verschiedenen Web-Frameworks als alternatives «Ausgabetool» verwendet werden.
Weiter wird Velocity gerne im Bereich der Code-Generierung verwendet. Beispielsweise kann man SQL-Statements für verschiedene Anwendungsfälle oder Datenbanken vorbereiten und dann zur Laufzeit konfigurieren und erstellen lassen. Ein anderes Beispiel von Code-Generierung ist das Erstellen von Java-Code. Daher trifft man Velocity in einer Vielzahl von Projekten an:
Um mit Velocity Code erstellen zu können, muss zunächst ein Template erstellt werden. Kasten 1 zeigt ein einfaches, rein Text-basierendes Beispiel. Die hart codierten Zeichenketten werden 1:1 ausgegeben, sodass es, wie gesagt, keine Rolle spielt, ob es sich wirklich um Text oder um etwas anderes handelt. Die Steuerung der Vorlagen erfolgt mit der einfach zu erlernenden Velocity Template Language (VTL). Sie stellt im wesentlichen Befehle (Direktiven), die mit einer Raute beginnen (#) beginnen, und Referenzen, die mit einem Dollarzeichen ($) gekennzeichnet werden, bereit. Im Beispiel erkennt man in der ersten Zeile die Referenz $firstname. Dies führt auch schon unmittelbar zum nächsten wesentlichen Velocity-Konzept, dem Kontext. Über den Velocity-Kontext können Daten in Form von Java-Objekten zwischen der Java-Anwendung und dem Velocity-Generator ausgetauscht werden. Im konkreten Beispiel bedeutet dies: Damit Velocity die Referenz $firstname auflösen und durch den gewünschten Vornamen ersetzen kann, muss dieser als Variable in den Kontext übergeben werden.
Kasten 2 zeigt den entsprechenden Java-Code. Zunächst werden Velocity initialisiert und der Kontext erstellt, dann werden die entsprechenden Werte in den Kontext geschrieben und Velocity aufgerufen, das Template und der zu verwendende Kontext bekanntgegeben und die Generierung gestartet.
Damit wäre die einfachste Verwendung von Velocity auch schon erklärt: Es können beliebige Referenzen, die in der Vorlage verwendet werden, zur Laufzeit mit textuellen Inhalten gefüllt werden.
Das Velocity-Konzept ist jedoch breiter angelegt. In den Kontext können grundsätzlich beliebige Java-Objekte gelegt werden. Wird auf das Objekt einfach mit $objekt referenziert, so wird tatsächlich die String-Repräsentation des Objektes an dieser Stelle eingesetzt. Kasten 1 zeigt aber auch, dass man innerhalb des Templates auf Methoden des Objektes zugreifen kann und somit einen granularen Zugriff auf Daten hat.
Neben den Referenzen existieren noch die bereits erwähnten Direktiven. Damit können beispielsweise Wertzuweisungen innerhalb des Template gemacht werden. Noch wichtiger ist aber, dass damit Iterationen und Bedingungen formuliert werden können. So wird im Beispiel-Template durch eine Liste iteriert. Diese Liste ist ein List Interface aus den Java Collections und wird ebenfalls vom Programm an den Kontext übergeben.
Schliesslich kann man noch einfache «Makros» definieren, die oft wiederkehrende Dinge automatisieren, beispielsweise das Erstellen einer Tabellenzeile in HTML.
Neben Velocity bestehen noch andere auf Vorlagen basierende Bibliotheken, Webmacro und Freemarker wurden schon genannt. Daneben gibt es natürlich auch konzeptionell sehr verschiedene Ansätze.
Abbildung 1 zeigt drei verschiedene Möglichkeiten. Die erste Variante, die schon erwähnt wurde, ist konzeptionell die einfachste: Es werden von einer Anwendung Daten geladen und innerhalb der Anwendung direkt die Ausgabe erstellt.
Typische Ansätze hier sind «traditionelle» Servlets und JSP. Von dieser Vorgehensweise ist jedoch aus schon oben genannten Gründen dringend abzuraten.
Der zweite Ansatz ist ebenfalls sehr beliebt, vor allem im XML-Umfeld: XML-Daten «triggern» ein XSLT-Stylesheet, auf dessen Basis ein XSLT-Prozessor eine Ausgabe erstellt – typischerweise ein anderes XML-Dokument, dass dann weiter zu HTML, PDF oder einem anderen Zielformat verarbeitet werden kann, auch zu Java-Code oder SQL.
XSLT ist eine zeitgemässe Variante, hat jedoch auch seine Probleme. Einerseits fällt es manchen Programmierern schwer, die funktionale Logik von XSLT richtig einzusetzen. Konzeptionell ist XSLT eben nicht Ausgabeformat-orientiert, sondern erlaubt die Definition von Mustern und entsprechenden Transformations-Regeln.
Möchte man eine Website erstellen, so gibt es oftmals die funktionale Trennung zwischen Programmierern und Designern. Designer arbeiten typischerweise mit grafisch orientierten Werkzeugen wie Photoshop und Dreamweaver oder sie können zwar HTML erstellen, haben aber oft Schwierigkeiten mit XSLT.
Variante 3 hingegen ist die hier beschriebene Möglichkeit. Der Vorteil liegt dabei darin, dass das Template im Prinzip mit dem Ausgabeformat identisch ist. Der einzige Unterschied zwischen Vorlage und Ausgabe besteht darin, dass noch keine Werte eingetragen sind. Da eine saubere Trennung zwischen Logik und Template besteht, kann auch ein Designer mit Velocity-Templates arbeiten, ohne die darin enthaltene minimale Logik zu zerstören.
Auch wenn bei der Verarbeitung von XML die Verwendung von XSLT naheliegend ist, ist sie noch lange nicht zwingend. Denn auch mit Velocity und vor allem Freemarker kann auf XML-Daten im Template zugegriffen werden. So kann bei Freemarker als Datenquelle direkt eine XML-Datei angegeben und auf diese Daten im Template zugegriffen werden. Grundsätzlich ist dies mit Velocity auch möglich, wenn auch etwas umständlicher.
Die Template-Variante funktioniert gut, solange die Daten im XML-Dokument eher «Datenbank»-orientiert, also starr strukturiert sind. Sind die XML-Daten jedoch eher «Dokument-orientiert», also wenig strukturiert, so wird es mit Freemarker und Velocity eher mühsam und XSLT kann seine Vorzüge voll ausspielen.
Techniken zur Code-Generierung
Template Engines wie Velocity und Freemarker sind leistungsfähige Tools zur einfachen Template-basierten Code-Generierung. Sie sind gut dokumentiert und in kürzester Zeit zu erlernen. Es gibt vor allem für Velocity auch Unterstützung in integrierten Entwicklungsumgebungen wie Eclipse und Intellij sowie Integration in verschiedene Web-Frameworks wie das Spring-Framework.
Der Template-basierte Ansatz eignet sich sicherlich nicht für alle Szenarien, sollte jedoch als alternative Display-Logik für Webapplikationen in Betracht gezogen werden. Velocity bietet sich aber (wie die genannten Beispiele zeigen) keinesfalls nur als HTML-Generator an!
Ein kleiner Nachteil kann sich durch die im Vergleich mit XSLT geringere Verbreitung ergeben. Ausserdem ist zu beachten, dass Velocity zwar im Vergleich zu JSP nur wenig Logik erlaubt und diese sauber handhabbar macht, jedoch über den Zugriff auf beliebige Objekte und deren Methoden im Kontext eine erhebliche Komplexität und Abhängigkeit zwischen Programmcode und Template-Code entstehen kann. Wird dann der Programmcode verändert, so kann man die Funktionsweise der Tem-plates zerstören. Eine Fehlersuche ist dann unter Umständen schwierig. Daher sollte das «Navigieren» in komplexen Objektstrukturen aus dem Template heraus nur mit Vorsicht eingesetzt werden.
Alexander Schatten (alexander@schatten.info) ist Assistent am Institut für Softwaretechnik und interaktive Systeme der Technischen Universität Wien.