Migration zu Apache Wicket 6: Was ist zu tun?

Print Friendly, PDF & Email

Wicket 6 Migration
Apache Wicket hat viele Stärken. API-Kompatibilität zwischen den großen Versionsnummern gehört leider nicht dazu! Was muss man wissen, wenn man ein bestehendes Projekt zu Wicket 6 migrieren möchte?

Der Versionssprung von Wicket 1.4 zu Wicket 1.5 hatte schon für viel Arbeit gesorgt. Die Entwickler hatten nicht nur neue Funktionen hinzugefügt, sondern die API teilweise erheblich verändert haben. Der Wechsel von Wicket 1.5 zu Wicket 6 ist etwas leichter, doch es gibt erneut viel zu tun.

Die neue Versionsstrategie des Entwickler-Teams von Apache Wicket sieht vor, dass „kleine“ Versionssprünge (d.h. in der zweiten Stelle) kompatibel bleiben sollen. Große Versionssprünge (d.h. in der ersten Stelle) sind hingegen explizit keine „Drop-in Replacements“. Hier behalten sich die Entwickler vor, umfangreiche, nicht-kompatible Änderungen vorzunehmen. Dementsprechend liefert ein Update der Wicket-Bibliotheken von 1.5 auf 6.0 viele Kompilierfehler. Was dann zu tun ist, beschreibt eine Migrationsanleitung. Leider ist diese Anleitung nicht vollständig und gibt kein klares Bild, wieviel Arbeit die Migration bedeutet.

Unsere Migration

Mein aktuelles Projekt hat bei Wicket 1.4 gestartet und ist früh auf 1.5 gewechselt. Nachdem Wicket 6 inzwischen bei Version 6.8 angekommen ist, kam es uns stabil genug vor, die Migration anzugehen. Der Zeitpunkt war letztlich eine Mischung aus einer Bauchentscheidung und etwas Freiraum in der Projektplanung. Unsere Hauptmotivation war, von den aktuellen Weiterentwicklungen profitieren zu können (z. B. der Integration von WebSockets).

Der zu migrierende Java-Code im Frontend umfasste rund 100.000 Zeilen. Nach dem Wechsel der Versionsnummern in den POM-Dateien meldete der Compiler rund 850 Fehler. Nachdem alle Import-Anweisungen angepasst waren, kamen noch einmal rund 100 Fehler dazu. Rund 90% dieser Fehler gingen auf das Konto von drei großen API-Änderungen. Nach vier Stunden stupider Anpassungen mit viel Copy&Paste blieben nur noch rund 100 Fehler übrig. Diese Fehler zu beseitigen, benötigte einen weiteren Tag Arbeit.

Erfreulicherweise war die Arbeit mit dem letzten Kompilierfehler schon fast erledigt. Von unseren vielen Hundert WicketTester-Unit-Tests schlug nur ein einziger fehl. Und auch nach dem testweisen Start unserer beiden Web-Anwendungen gab es nur vereinzelte Probleme zu beheben.

Nach eineinhalb (Mann-)Tagen Arbeit war die Migration schließlich vollständig abgeschlossen. Spätere Überraschungen im Produktivbetrieb gab es keine.

Die großen API-Änderungen

Die Schnittstelle IHeaderResponse bot in Wicket 5 viele Methoden, CSS- oder Javascript-Ressourcen zu registrieren. Eine typische Implementierung der renderHead-Methode schaute so aus:

In Wicket 6 wurden die vielen zuvor existierenden renderXY-Methoden durch eine einzige neue Methode render(HeaderItem) ersetzt. Der analoge Code schaut in Wicket 6 somit so aus:

Die Schnittstelle IColumn hat in Wicket 6 einen zweiten Typ-Parameter erhalten: IColumn<T, S>. Der neu hinzugekommene Parameter dient dazu, auch andere Klassen zur Sortierung verwenden zu können als String. Die Auswirkungen davon sind in der gesamten API für Tabellen zu spüren. Jede Verwendung der Klassen IColumn, ISortableDataProvider, DataTable usw. muss nun für den neuen Typ-Parameter angepasst werden.

In der Schnittstelle IDataProvider wurde der Rückgabewert der Methode size() von int auf long geändert. Als Folge davon müssen alle DataProvider-Implementierungen angepasst werden. Falls die jeweils aufgerufenen Service-Methoden auf Integer basieren, sollte man sich überlegen, ob man diese auch entsprechend anpasst.

Weitere kleinere Änderungen, die uns betrafen

Wicket 5 hat im HTML-Code für Ajax-Aufrufe an den betroffenen Elementen Javascript-Eventhandler generiert. Wenn ein Anchor-Tag bespielsweise nach einem Klick einen Ajax-Aufruf auslösen soll, wurde dem <a>-Tag bisher ein onclick-Attribut hinzugefügt. Wicket 6 hat diesen Mechanismus grundlegend geändert. Neuerdings generiert Wicket zu Beginn der HTML-Seite einen Javascript-Codeblock, in dem alle Eventhandler der Seite eingerichtet werden. Pro Event wird eine Zeile mit einem Aufruf von Wicket.Ajax.ajax erzeugt. Eine direkte Folge davon ist, dass es die Schnittstelle IAjaxCallDecorator nicht mehr gibt, über die man zuvor die Möglichkeit hatte, manuell Javascript-Code vor oder nach dem eigentlichen Ajax-Aufruf einzufügen. Als Alternative muss man nun die Methode updateAjaxAttributes aus der Klasse AbstractDefaultAjaxBehavior überschreiben und dort einen AjaxCallListener registrieren.

Wicket 5 bot noch die Möglichkeit, im Browser eine globale Javascript-Funktion mit dem Namen wicketGlobalPostCallHandler als Ajax-Callback einzurichten. Wenn diese Funktion existierte, wurde sie automatisch am Ende jeder Ajax-Operation aufgerufen. Als Alternative zu wicketGlobalPostCallHandler gibt es in Wicket 6 nun einen komplexeren Event-Mechanismus, der über das Objekt Wicket.Event ermöglicht, beliebige Funktionen zu registrieren:

Bis in Wicket 5 gab es nur ein FeedbackMessages-Objekt pro Session, in dem alle Nachrichten an den Nutzer gesammelt wurden. In Wicket 6 speichert nun jede Komponente selbst ihre Nachrichten. Dies erfordert Anpassungen, wenn man bisher Nachrichten manuell aus dem FeedbackMessages-Objekt verarbeitet und entfernt hat.

Fazit

Obwohl wir manche Bereiche von Wicket sehr intensiv nutzen (z.B. Modelle über komplexe Objektgraphen und viele Ajax-Funktionen), verlief die Migration von Wicket 1.5 auf Wicket 6 erstaunlich reibungslos! Die Anzahl der Kompilierfehler nach dem Versionswechsel der Bibliotheken war jedoch erschreckend. Es wäre verständlich, wenn sich andere Projektverantwortliche entscheiden, den Aufwand der Migration zu meiden und bei Wicket 1.5 zu bleiben. Daher würde es mich sehr freuen, wenn das Entwicklerteam für Wicket 7 wieder mehr Wert auf API-Kompatibilität setzen würde!