Skip to main content

Einsatz von Gradle beim Deployment mit Liferay 7

Gradle Logo

Für viele der Mitarbeiter Portale und Intranets, die wir bei empulse für unsere Kunden entwickeln, wird die Portalsoftware Liferay eingesetzt. Da ab Liferay 7 Gradle als Build-Tool verwendet wird, beschreibe ich in diesem Artikel den Einsatz von Gradle und dessen Stärken und Schwächen.

Das Tool: Gradle

Gradle ist ein Build-Management-Automatisierungstool, das auf Java basiert und mit Ant und Maven vergleichbar ist. Es verbindet die Freiheit von Ant mit der Funktionalität und dem deklarativen Ansatz von Maven.
Es gibt einen integrierten Ant-Builder, sodass einfach Ant-Tasks aufgerufen werden können. Dadurch besteht Zugriff auf eine große Menge stabiler Funktionen. Durch die Integration von Ivy- und Maven-Repositories können Abhängigkeiten einfach verwaltet werden.
Um die zu bauenden Projekte zu beschreiben, nutzt Gradle eine auf Groovy basierende domänenspezifische Sprache (DSL). Werkzeuge können direkt aus dem Build heraus angestoßen werden, sodass die Aufgaben bestmöglich getrennt, aber gleichzeitig alle nötigen Funktionen unterstützt werden.

Eigenschaften von Gradle

Gradle verfolgt die zwei Prinzipien Convention over Configuration und Don’t repeat yourself. Dadurch ist alles einfach, leicht verständlich und es gibt sinnvolle Vorgaben, die leicht angepasst werden können. Wichtig ist aber vor allem, dass Projekte schnell aufgesetzt werden können, da kein großer Konfigurationsaufwand besteht. Es können auch Multi-Projekte gebaut werden, und durch die Vererbungshierarchie der Build-Skripte bleiben diese einfach und verständlich. Für den Fall, dass kein eigenes Build-Skript definiert wird, verwendet Gradle sein Default-Build-Skript, sodass für die ersten Schritte wirklich nicht viel zu beachten ist.

Default Build-Skript: build.gradle

apply plugin: 'groovy'
dependencies {
    compile gradleApi()
    compile localGroovy()
}

Da das Groovy-Plugin auch das Java-Plugin integriert, können mit diesem Build-Skript auch Java-Entwicklungen gebaut werden. Dabei werden die Gradle API Interfaces und Klassen sowie die von Gradle integrierte Groovy Version verwendet.

Es werden hauptsächlich drei Dateien für einen einfachen Build verwendet:

  • build.gradle: Definition der Tasks und Abhängigkeiten eines Projektes
  • settings.gradle (optional): Benennung der Unterprojekte bei einem Multi-Projekt
  • gradle.properties (optional): Angabe von Properties, die für die Initialisierung gültig sind

In Gradle ist es möglich, eigene Tasks zu schreiben und Default-Tasks anzugeben, Standard-Tasks zu überschreiben, Abhängigkeiten einfach zu verwalten, die Abhängigkeiten von Maven- und Ivy-Repositories zu verwenden und Ant-Tasks aufzurufen. Damit hat Gradle einen großen Funktionsumfang. Dieser kann noch durch das Schreiben von Plugins, das auch recht simpel ist, ausgebaut werden. Auch Tests könen von Gradle automatisiert ausgeführt werden und es können verschiedene Plugins zur Überwachung der Codequalität eingebunden werden, sodass dieser Bereich ebenfalls abgedeckt ist.

println "Ausgeführt in der Konfigurationsphase."
 
task myTask
myTask.doLast { print "in der " }
myTask << {
    println "Ausführungsphase."
}
myTask.doFirst { print "Ausgeführt " }

Hier habe ich meinen eigenen Task myTask definiert. Befehle außerhalb von Tasks werden in der Konfigurationsphase ausgeführt, alle anderen erst in der nachfolgenden Ausführungsphase bei Aufruf des Tasks. Um einen Task zu definieren, kann entweder ein Initialisierungs-Closure (optional) oder direkt Argumente angegeben werden. Mit << können Aktionen hinten angehängt werden, ebenso mit doLast(). Mit doFirst() können am Anfang der Aktionsliste Aktionen eingefügt werden. Wichtig ist es, dass der Initialisierungs-Closure nicht mit dem normalen Closure für eigentliche Build-Aktionen verwechselt wird. Daher sollte er durch einen Kommentare gekennzeichnet werden.

Wenn ich den Task nun durch gradle myTask aufrufe, erscheint folgende Ausgabe:

$ gradle myTask
Ausgeführt in der Konfigurationsphase.
:myTask
Ausgeführt in der Ausführungsphase.
 
BUILD SUCCESSFUL

Ein oder mehrere Default-Tasks können definiert werden, indem die Zeile defaultTasks ‚task1′[, ‚task2‘] eingefügt wird. Diese werden ausgeführt, wenn beim Aufruf kein Task angegeben wird. Des Weiteren ist es möglicherweise noch wichtig, die Abhängigkeiten der Tasks untereinander anzugeben, also welcher Task vor der Ausführung eines anderen erfolgreich ausgeführt werden muss. Dies geschieht über den Methodenaufruf dependsOn(), wobei mehrere Aufrufe additiv wirken. Neben dem Methodenaufruf beim Task kann die Abhängigkeit auch bei der Task-Definition oder im Initialisierungs-Closure angegeben werden. Möchte ich mehrere Abhängigkeiten bei der Task-Definition definieren, müssen sie in eckigen Klammern angegeben werden.

task compile
compile.doLast { println "Aufruf des Compile-Tasks" }
 
task compileTest(dependsOn: compile)
 
task test
test.dependsOn compileTest
 
task assemble {// Initialisierungs-Closure
    dependsOn compile
}
 
task build(dependsOn: [ test, assemble ] ) << {
    println "Aufruf des Tasks build"
}

In diesem Beispiel habe ich fünf Tasks definiert, um zu zeigen, auf welche verschiedenen Weisen die Abhängigkeiten zwischen den Tasks angegeben werden können. Dabei liest sich der Abhängigkeitsgraph (DAG) so, dass um den Task rechts auszuführen die Tasks auf der linken Seite vorher erfolgreich ausgeführt werden müssen. Um test auszuführen, müssen vorher compileTest und dafür compile erfolgreich ausgeführt werden.

So kann also schon mit einfachen Konfigurationen ein Build-Skript geschrieben werden.

Gradle bietet aber noch einen anderen bedeutenden Vorteil: Beim Build wird Zeit gespart, indem vorher erzeugte Zwischenergebnisse in einem lokalen Cache abgelegt werden. So müssen Tasks, die bereits ausgeführt wurden und bei denen sich nichts geändert hat, nicht erneut ausgeführt werden. Änderungen an der Ausgangsdatei werden erkannt, sodass dann entsprechende Tasks erneut ausgeführt werden. Außerdem laufen bestimmte Tasks (z.B. Tests) parallel auf mehreren CPUs, wenn der Gradle Dämon verwendet wird. Eine Neugenerierung kann ich mit gradle clean erzwingen, da dann der Inhalt im Ausgabeverzeichnis build gelöscht wird.

Ein Build bei Maven kann gut und gerne schon mal seine ein, zwei oder mehrere Minuten dauern. Gerade bei Web-Lösungen deploye ich häufig, um die Änderungen in HTML, Javascript, CSS oder in einer JSP zu testen; da kann ich mit Gradle viel Zeit sparen. Des Weiteren gibt es für die gängigen IDEs Gradle-Plugins, sodass die Verwendung erleichtert wird. Ich verwende IntelliJ IDEA, die eine sehr gute Gradle-Integration bietet.

Gradle im Einsatz mit Liferay 7

Bisher haben wir häufig Maven genutzt. Da Liferay ab der Version 7 Gradle für seinen Build verwendet und auch bei Tutorials, etc. einsetzt, haben wir bei unseren Liferay 7 Projekten ebenfalls auf Gradle gesetzt. Dabei konnte ich einige Stärken und Schwächen feststellen.

Stärken und Schwächen

Gradle einzusetzen ist sinnvoll, …

  • wenn von der Standard-Maven-Struktur abgewichen werden möchte, vor allem bei Integrationstests. Dann muss bei Maven extra ein Profil angelegt werden, bei dem die Ordnerstruktur benannt wird oder diverse Ziele und Abhängigkeiten. In Gradle wird dies einfach durch die Abhängigkeiten der Tasks untereinander gelöst.
  • wenn Tests ausgeschlossen werden sollen. Das funktioniert in Gradle einfach mit den Methoden include und exclude.
  • wenn man schneller starten und Flexibilität und mehr Komfort möchte. Gradle ist Maven etwas überlegen, weil Skripte geschrieben werden können.
  • wenn beim Build-Prozess Zeit gespart werden soll.
  • wenn man Abhängigkeiten einfach verwalten möchte (kann Ant nur über Ivy) und eigene Tasks schreiben möchte (kann Maven nur über Plugins, die aber aufwendiger zu schreiben sind als Gradle Tasks).

Gradle hat aber auch einige Schwächen:

  • Bei Gradle darf man es nicht mit der Konfiguration und dem Umfang von Skripten übertreiben, da diese dann nicht mehr übersichtlich sind, unverständlich werden und schwierig zu warten sind.
  • Aus eigener Erfahrung kann ich sagen, dass Gradle manchmal sehr kleine Änderungen an Dateien nicht erkennt, sodass ein Modul beim Ausführen des deploy Tasks nicht neu gebaut wird. Dann muss man erst ein clean ausführen, damit erfolgreich deployt wird.
    Andererseits habe ich auch bei einem Kundenprojekt mit Liferay erlebt, dass man mit Maven nach dem Deploy noch einmal deployen muss, weil die Änderungen noch nicht verfügbar waren.
  • Wenn es Fehler bei der Verarbeitung gibt, sind diese nicht immer eindeutig zu erkennen. Vor allem bei der Verwendung einer IDE (hier IntelliJ IDEA), die eine graphische Aufbereitung der Task-Abarbeitung liefert, werden keine Fehlermeldungen ausgegeben. Hier kann bei IntelliJ aber in den Text-Mode gewechselt werden, wo ggf. die Fehlermeldungen abgedruckt werden.
  • Fehler in .scss-Dateien werden vom Task buildCSS nicht erkannt.

Gradle Dokumentation

Auf der Webseite von Gradle ist ein Handbuch, Tutorials und die API-Dokumentation verfügbar, um einen schnellen Einstieg zu ermöglichen. Die Dokumentation ist sehr umfangreich.

Außerdem gibt es von Liferay eine Dokumentation zu Liferay’s Gradle Plugins und wie sie verwendet werden.

Fazit

Für mich bedeutet der Einsatz von Gradle deutlich mehr Komfort als der von Maven. Die Build-Skripte sind einfacher zu schreiben, ich spare bei meinen häufigen Builds Zeit und bislang kann ich die erwähnten Schwächen als gering einstufen. Wenn man sie erkannt hat, kann man sie gut ausgleichen bzw. beachten. Durch die Verwendung einer Skriptsprache halte ich Gradle für mächtiger.

Weiterführende Infos:

Wünschen Sie weitere Details oder haben Sie konkrete Fragen?

Vereinbaren Sie einfach einen Beratungstermin mit uns oder schreiben Sie uns.

Über den Autor

empulse Team

Unser kompetentes und erfahrenes Team von (Java-)Entwickler ist stark in Beratung, technischer Konzeption und zuverlässiger Umsetzung komplexer Projekte. In unseren Reihen haben wir Spezialisten für unterschiedliche Themengebiete, die hier ihr Fachwissen zum Besten geben.