Einführung in das Framework Apache Wicket: Java mit Leichtigkeit
Der Kern des schlanken Frameworks verschreibt sich der Manipulation von Markup (XML/XHTML) durch Java. Ein wohldurchdachtes Komponentenmodell ermöglicht die Programmierung von Webanwendungen analog zu GUIs mit der vollen Kontrolle über den erzeugten Code. Viele mitgelieferte Komponenten ermöglichen dabei den schnellen Aufbau einer funktionalen Oberfläche. Dieser Artikel gibt eine kurze Einführung in Apache Wicket anhand einiger Beispiele. Der komplette, lauffähige Code steht über den Softlink am Ende des Artikels zum Download bereit.
Java + HTML = Wicket
Ein einfaches Beispiel soll den Aufbau einer Webanwendung mit Wicket verdeutlichen. Eine Anwendung in Wicket besteht immer aus einem Application-Objekt, das die Konfiguration festlegt, und mehreren Seiten für verschiedene Ansichten. Für eine Seite sind zwei Dateien notwendig: eine Java-Klasse und der HTML-Code. Dem „Convention over Configuration“-Ansatz [1] folgend, ist der Name der Klasse die Basis für den Namen des Markup-Dokuments.
JAVA ExampleApplication.java
public class ExampleApplication extends WebApplication { public Class getHomePage() { return HelloWorld.class; } }
Listing 1
JAVA HelloWorld.java
public class HelloWorld extends WebPage { public HelloWorld() { add(new Label("message", "Hello, World!")); } }
Listing 2
HTML HelloWorld.html
<html> <body> <h1 wicket:id="message">[My message]</h1> </body> </html>
Listing 3
Für die Applikationsklasse „ExampleApplication“ reicht es aus, eine Einstiegsseite festzulegen, die als Startseite dargestellt wird. Basis für alle Seiten ist die Klasse „WebPage“, die Komponenten werden im Konstruktor mittels „add(…)“ zur Seite hinzugefügt. Die Komponente „Label“ wird zur Darstellung von Text verwendet – in diesem Fall „Hello, World!“. Die ID „message“ ordnet das Label einem Tag mit dem „wicket:id“-Attribut gleichen Werts zu. Der Inhalt des Tags wird bei der Ausgabe durch die gerenderte Komponente ersetzt. Die zugehörige Markup-Datei „HelloWorld.html“ ist gültiges HTML und kann direkt im Browser betrachtet werden. Dies ist in Wicket generell der Fall und vereinfacht die Entwicklung und Zusammenarbeit beim Design der Oberfläche.
Webapplikationen in Java |
Eine Webapplikation wird in Java durch einen so genannten Servlet-Container (z. B. Tomcat oder Jetty) ausgeführt und in ein spezielles Paket geschnürt (WAR).Eine Datei „web.xml“ definiert die vorhandenen Ressourcen einer Webapplikation, wie z. B. Servlets, Filter und Weiteres. Durch den Einsatz eines speziellen Build-Tools wie Maven oder Ant passiert der Aufbau der benötigten Dateistruktur automatisch. |
Die meisten Web-Frameworks sind an das bekannte MVC-Framework Struts [2] angelehnt, das nur eine Separation zwischen Applikationsfluss und Darstellung erreicht, sich jedoch bei der Programmierung sehr an den Request-/Response-Zyklus von HTTP anlehnt. Die Entwicklung einer nur ansatzweise GUI-ähnlichen Webapplikation mit einem solchen Framework ist nur mit großem Aufwand möglich.
Komponenten als Bausteine der Oberfläche
Komponenten sind wiederverwendbare Objekte, die zu einer komplexen Web-Oberfläche zusammengesetzt werden können. Die Programmierung von Webapplikationen wird so deutlich vereinfacht, da das Verhalten in den Komponenten gekapselt wird und die Logik nicht an den Request-/Response-Zyklus gebunden ist. Eigene Komponenten können in Wicket im Gegensatz zu anderen Frameworks wie JavaServer Faces mit wenig Aufwand selbst erstellt werden. Das objektorientierte Design, das auch Vererbung von Markup vorsieht, ermöglicht zudem die Erweiterung bestehender Komponenten.
Ein Beispiel mit bleibendem Zustand und mehr Action
Komponenten können neben ihrem Verhalten auch einen Zustand in Form von Instanzvariablen besitzen. Der Zustand wird über verschiedene Aufrufe hinweg serverseitig gespeichert – um die Details kümmert sich Wicket. Als Beispiel soll eine einfache Komponente für ein Rating dienen, die später um Ajax-Fähigkeiten (mit Fallback) erweitert wird, ohne eine Zeile JavaScript zu schreiben. Die Rating-Komponente speichert die Anzahl der Abstimmungen mit den vergebenen Punkten und hat jeweils einen Link für „mögen“ („like“) und „nicht mögen“ („dislike“).
JAVA Rating.java
public class Rating extends Panel { int votes = 0; int score = 0; public Rating(String id) { super(id); add(new Label("rating", new PropertyModel(this, "rating"))); add(new Label("votes", new PropertyModel(this, "votes"))); add(new Link("like") { public void onClick() { votes++; score++; } }); add(new Link("dislike") { public void onClick() { votes++; } }); } public double getRating() { return votes > 0 ? score / (double)votes * 10 : 0; } public int getVotes() { return votes; } }
Listing 4
HTML Rating.html
<html> <style> div.rating { font-size: 3em } div.votes { font-size: 0.8em } </style> <body> <wicket:panel> <div class="rating" wicket:id="rating">7.1</div> <div class="votes"><span wicket:id="votes">67</span> ratings</div> <div class="links"> <a href="#" wicket:id="like">Like</a> | <a href="#" wicket:id="dislike">Dislike</a> </div> </wicket:panel> </body> </html>
Listing 5
Grundlage für die Rating-Komponente ist in diesem Fall die Klasse „Panel“, die andere Komponenten aufnehmen kann und eigenes Markup besitzt. Dieses wird in der HTML-Datei zwischen den „<wicket:panel>“-Tags angegeben, der Rest der Datei dient der Vorschau. Wie jede Komponente benötigt sie selber einen Bezeichner, den sie als „id“ im Konstruktor übergeben bekommt und an den Konstruktor der Superklasse „Panel“ weitergibt. Die beiden Labels „rating“ und „votes“ rendern das Gesamt-Rating und die Anzahl der Stimmen. In diesem Fall ist der Wert der Labels kein String, sondern ein so genanntes Model.
Für das Verhalten des Ratings sorgen die anonym implementierten „onClick()“-Methoden der Link-Komponenten. Die entsprechende Methode wird bei Klick eines Links im Browser aufgerufen und verändert in diesem Beispiel den Zustand und die Darstellung der Rating-Komponente. Wie das Beispiel zeigt, werden im Java-Code keinerlei Parameter des Requests abgefragt, denn die für einen Link generierte URL teilt Wicket mit, welcher Link auf der Seite geklickt wurde. Da sie für jede Instanz der Komponente auf einer Seite eindeutig ist, kann eine Komponente ohne Probleme auch mehrmals auf einer Seite genutzt werden.
Models binden Komponenten an Daten
Die vorab erwähnten Models sind ein wichtiges Konzept von Wicket, um Komponenten an Daten zu binden. Der Wert eines Models wird erst bei der Darstellung der Seite bestimmt. Im Falle des im Beispiel verwendeten „PropertyModel“, wird als Wert die Eigenschaft eines Objekts benutzt (Eigenschaften entsprechen Getter- und Setter-Methoden in JavaBeans-Namenskonventionen: „rating“ bezeichnet also „getRating()“). Durch die Kapselung des Datenzugriffs ermöglichen Models die Integration mit anderen Ebenen einer Anwendung (z. B. Geschäfts- und Persistenz-Schicht).
Abwärtskompatibles Ajax ohne „J“ mit Wicket
Um der Rating-Komponente eine Abstimmung mittels Ajax zu ermöglichen, bei der die Seite nicht neu geladen werden muss, reicht es vollkommen aus, den Java-Code etwas abzuändern:
JAVA Rating.java
public Rating() { final Label ratingLabel; add(ratingLabel = new Label("rating", new PropertyModel(this, "rating"))); ratingLabel.setOutputMarkupId(true); final Label votesLabel; add(votesLabel = new Label("votes", new PropertyModel(this, "votes"))); votesLabel.setOutputMarkupId(true); add(new AjaxFallbackLink("like") { public void onClick(AjaxRequestTarget target) { votes++; score++; if(target != null) { target.addComponent(ratingLabel); target.addComponent(votesLabel); } } }); add(new AjaxFallbackLink("dislike") { public void onClick(AjaxRequestTarget target) { votes++; if(target != null) { target.addComponent(ratingLabel); target.addComponent(votesLabel); } } }); }
Listing 6
Die beiden Labels müssen für die Verwendung mit Ajax eine HTML-ID zugewiesen bekommen. Dies übernimmt die Eigenschaft „setOutputMarkupId“. Die Komponente „AjaxFallbackLink“ ist ein erweiterter Link, der den Klick via Ajax übermittelt, falls JavaScript zur Verfügung steht. Ansonsten verhält er sich als Fallback wie die normale Link-Komponente. Mit dem „AjaxRequestTarget“-Objekt, das bei der Behandlung des Klicks in der „onClick(…)“-Methode zur Verfügung steht, können Komponenten registriert werden, die per Ajax neu gerendert werden sollen. Der benötigte JavaScript-Code wird von Wicket automatisch erzeugt. Über das „AjaxRequestTarget“-Objekt kann aber auch eigenes JavaScript ausgeführt werden.
Testgetriebene Webentwicklung mit Wicket
Wer seine Webanwendung testgetrieben entwickeln möchte, kann dies mit Wicket auch für die Benutzeroberfläche ohne viel Aufwand tun. Für Unit-Tests stellt Wicket die Klasse „WicketTester“ bereit, mit der Seiten und Komponenten getestet werden können. Hier kommt Wicket der unmanaged-Ansatz zugute, bei der eine Instanziierung von Komponenten und Seiten jederzeit möglich ist. Ein einfacher Test für die Rating-Komponente könnte wie folgt aussehen:
JAVA RatingTests.java
public class RatingTests extends TestCase { public void testRender() { WicketTester tester = new WicketTester(); Rating rating = (Rating) tester.startPanel(Rating.class); tester.clickLink("panel:like"); assertEquals("like increments score", 1, rating.score); tester.assertLabel("panel:votes", "1"); tester.assertLabel("panel:rating", "10"); tester.clickLink("panel:dislike"); assertEquals("score not incremented", 1, rating.score); tester.assertLabel("panel:votes", "2"); tester.assertLabel("panel:rating", "5"); } }
Listing 7
Die „WicketTester“-Klasse simuliert in diesem Unit-Test das Klicken der Links und überprüft die angezeigten Werte auf Richtigkeit. Oftmals kann die Entwicklung einer kompletten Webapplikation zunächst testgetrieben durchgeführt und erst später auf einem Servlet-Container ausgeführt werden. Das im Gegensatz zu PHP oder Ruby langsame Develop/Deploy bei der Entwicklung einer Webanwendung mit Java kann so kompensiert werden. Zudem erreicht die Software erfahrungsgemäß eine höhere Qualität mit weniger Defekten und besserem Design.
Fazit
Wicket ermöglicht die Entwicklung eigener Komponenten bei voller Kontrolle über den erzeugten HTML-Code; Ajax-Features können ohne Programmierung von JavaScript implementiert werden. Zudem ermöglichen Hilfsklassen und das einfache Programmiermodell die testgetriebene Entwicklung.
Zusätzlich löst Wicket noch viele weitere Probleme, etwa durch umfangreiche Möglichkeiten zur Lokalisierung bis auf Markup-Ebene, Integration in Application-Frameworks wie Spring [4], Benutzung von Speaking-URLs, Bereitstellung dynamisch generierter Ressourcen (JavaScript, Grafiken) und Vorbereitung für Clustering. Weitere Informationen zu Wicket liefert die Projekt-Website. Auf WicketStuff.org werden Komponenten und Integrationen außerhalb des offiziellen Projekts veröffentlicht, außerdem findet sich dort eine Live-Demo mit vielen Beispielen.
Wer bislang dachte, dass Java für Webapplikationen unnötig komplex ist und keine Vorteile bietet, sollte sich Apache Wicket auf jeden Fall genauer ansehen. Vielleicht ist Wicket auch ein guter Grund, sich einmal wieder mit Java auseinanderzusetzen und die nächste Webapplikation mit diesem Framework umzusetzen.
Net schlecht, der Artikel, könnte aber ein *bisschen* ausführlicher sein.
Und wie sieht das ganze mir Spring Boot aus ?