Standalone Web-Anwendung mit Jetty, GWT & Gradle

Eine moderne Webanwendung die man sowohl auf einem „richtigen“ Java-Webserver betreiben als auch als ausführbare Datei(Standalone Web-Anwendung) Kunden in die Hand drücken kann, so dass diese die Anwendung per Doppelklick bei sich selbst im Netzwerk betreiben können? Zugegeben, eine ganz alltägliche Anforderung ist das sicher nicht, aber es kommt eben doch vor. Genau vor dieser Aufgabe standen wir aber bei der Realisierung des Planspiels „2015 – An den Schalthebeln der Macht„. Die fertige Web-Anwendung soll sowohl klassisch auf einem „richtigen Server“ wie Tomcat oder JBoss gehostet werden können, als auch auf CD an Schulen und andere Institutionen ausgeliefert werden. Lösung des Problems: Eine Standalone Web-Anwendung mit Jetty, GWT & Gradle in Form einer „Runnable, Executable WAR“.

 

Ansatz

Die Grundidee ist relativ simpel. Zunächst ist das Projekt ein ganz normales, gültiges WAR-Archiv. Also eine verpackte Webanwendung mit der von Java-Webservern erwarteten Struktur. Zusätzlich ist das Archiv aber auch noch ein von Java direkt ausführbares JAR, enthält also eine Klasse mit main-Methode.

Wird die Anwendung auf einem Server deployed, wird sie von diesem wie ein einfaches WAR gestartet. Wird hingegen die main-Methode aufgerufen, so startet diese selbst einen Webserver, Jetty, und deployed sich quasi selbst auf diesem. Damit das funktioniert, wird Jetty zusammen mit allen anderen verwendeten Libraries mit in das Archiv gepackt. JBoss oder Tomcat ignorieren sowohl Jetty als auch die main-Methode komplett.

Der Client wird in GWT realisiert, was den Vorteil hat dass als Server ein Servlet-Container völlig ausreicht. Außerdem wird der Server stark entlastet, da der Client einen Großteil der Geschäftslogik übernehmen kann und der Server kaum Zustandsdaten in Sessions vorhalten muss. Damit läuft die Anwendung bei Kunden auch auf schwächerer Hardware noch performant. Natürlich können aber mit entsprechender Konfiguration auch Technologien wie JSF oder Struts verwendet werden.

 

Projekt-Struktur

Grundlage der Projektstruktur ist der normale Aufbau eines Webprojekts. Neben einem Source-Folder für Unit-Tests wird es um einen Ordner in dem die Klasse mit der main-Methode landet erweitert.

 

Main

Auch wenn es vielleicht erst nicht so aussieht, die main-Methode ist relativ einfach. Zunächst wird eine neue Instanz eines Jetty-Servers erzeugt, ein Socket auf Port 80 geöffnet und dem Server zugewiesen. Anschließend wird das WAR „deployed“ in dem ein entsprechender Kontext erzeugt und gesetzt wird. Der letzte Code-Abschnitt sorgt dafür, dass solange gewartet wird bis der Jetty-Server beendet wurde. Viel mehr ist gar nicht notwendig.

 

Build-Skript

Etwas komplizierter wird es, wenn es darum geht das ausführbare WAR-Archiv zu erzeugen. Zum Glück nimmt Gradle einem einen Großteil der Grundlagen ab, so dass wir uns auf die Besonderheiten des Projekts konzentrieren können.

Nachdem Plugins, Defaults und ein paar Variablen gesetzt wurden, wird zunächst eine neue Konfiguration angelegt, die in den späteren Tasks für den Standalone-Betrieb verwendet wird. Die Dependencies können nach Belieben erweitert werden und enthalten hier nur das Minimum. GWT kann natürlich weggelassen werden wenn es nicht verwendet wird, dann sollte allerdings auch der GWT-Compile-Task entfernt werden. In den Source-Sets werden ein paar Pfade geändert, um der verwendete Projektstruktur zu entsprechen.

Der neue Task „extractStandaloneDependencies“ macht genau das was der Name verspricht: Er entpackt die für den Standalone-Betrieb benötigten Jetty-Libraries, so dass der Inhalt später dem WAR hinzugefügt werden können. Zusätzlich werden ein paar nicht benötigte Dateien gelöscht. Diese würden eigentlich nicht stören, aber machen das WAR sonst nur unnötig unübersichtlich.

Anschließend wird der GWT-Compiler per Ant gestartet. Da das Kompilieren immer etwas dauert wird es, um es wenigstens etwas zu beschleunigen, auf alle verfügbaren CPU-Kerne aufgeteilt.

Zum Abschluss wird dann das WAR-Archiv erstellt. Gradle sammelt die meisten benötigten Dateien automatisch ein, die ersten zwei „from“-Befehle sind nur aufgrund der geänderten Projektstruktur notwendig. Im dritten „from“ werden die zuvor entpackten Jetty-Dateien in das WAR aufgenommen. Damit Java das Archiv später wie ein JAR starten kann, wird dann noch die Main-Klasse in der Manifest-Datei festgelegt.

 

Executable

Moment, da fehlt doch noch was. Genau: das WAR ist jetzt zwar „runnable“ aber nicht „executable“. Ein Doppelklick würde Programm momentan nur starten wenn Java in der richtigen Version installiert ist. Was noch fehlt ist das Verpacken des Archivs in einen Wrapper, der daraus eine nativ ausführbare Datei macht und eine JVM mitbringt. Dieser letzte Schritt muss bisher noch von Hand durchgeführt werden. Wir verwenden dazu Launch4j als Wrapper, aber es gibt sicher auch Alternativen. Viel zu erklären gibt es zu diesem letzten Punkt nicht, wichtig ist nur in Launch4j die Option „Don’t wrap“ auszuwählen. Damit wird nicht wirklich ein Wrapper um die WAR gelegt, sondern WAR und Executable sind zwei einzelne Dateien die zusammen ausgeliefert werden. Diese Einschränkung ist leider notwendig, da sonst das Webprojekt nicht auf Jetty deployed werden kann.

Getagged mit: , , , ,

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

*

* Copy This Password *

* Type Or Paste Password Here *