Das Gradle-Plug-in application
erzeugt eine praktikable Distribution für eine Java-Applikation, was aber wenn mehr Flexibilität gebraucht wird? Zum Beispiel ein über-JAR oder ein JAR das sich durch Doppelklick startet lässt.
Das Plug-in application
basiert auf dem Plug-in distribution
. Es definiert dass alle JARs ins Unterverzeichnis lib
kopiert werden und das Startskripte für Windows und Linux/Unix im Unterverzeichnis bin
angelegt werden. Wenn man möchte kann man noch weitere Dateien ergänzen:
plugins { id 'java' id 'application' } application.mainClassName = 'de.muspellheim.example.App' distributions { main { contents { from "doc" } } }
Hier werden alle Dateien im Projektverzeichnis doc
mit in die Distribution aufgenommen.
Ein uber-JAR erzeugen
Ein uber-JAR ist ein einzelnes JAR, welches die Applikation einschließlich aller ihrer Abhängigkeiten zusammenfasst. Es wird der Inhalt aller JARs schlicht zu einem JAR zusammenkopiert.
Ein uber-JAR erleichtert die Verteilung einer Java-Applikation, da nur noch ein JAR weitergegeben werden muss. Um die Abhängigkeiten muss man sich dabei nicht mehr kümmern, da sie ja enthalten sind.
Mit Gradle kann man das wie folgt machen:
jar { manifest { attributes 'Main-Class': mainClassName } from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } }
Hinweis: Ein uber-JAR darf nicht verwendet werden, wenn Dateien in den JARs mehrfach vorkommen. Zum Beispiel wenn man Service Provider Interfaces (SPI) verwendet, liegen im Verzeichnis META-INF/services
der JARs gleichnamige Dateien, wenn mehrere JARs eine Implementierung für das gleiche Interface bereitstellen. Diese Dateien würden beim Erstellen des uber-JARs überschrieben, so das zur Laufzeit Implementierungen fehlen.
Wenn man jetzt die Distribution mit dem application
-Plug-in baut, hat man ein großes uber-JAR, aber immer noch alle Abhängigkeiten zusätzlichen im lib
-Verzeichnis. Um das zu vermeiden, konfigurieren wir das distribution
-Plug-in selber.
Als erstes wird das application
-Plugin ausgetauscht:
plugins { id 'java' id 'distribution' }
Dann die main
-Distribution mit dem erwarteten Inhalt konfigurieren:
distributions { main { contents { from jar from "$projectDir/src/dist" } } }
An dieser Stelle können wie mit dem application
-Plug-in gewohnt, weiter Dateien zum Inhalt der Distribution hinzugefügt werden.
Da wir das application
-Plug-in nicht mehr verwenden, gibt es auch keine Definition für die Main-Klasse mehr. Die kann aber einfach als lokale Variable angelegt werden. Dazu muss
application.mainClassName = 'de.muspellheim.example.App'
durch
def mainClassName = 'de.muspellheim.example.App'
ersetzt werden.
Zum Schluss fehlt noch der run
-Task zum Starten der Applikation:
task run(type: JavaExec) { group 'application' classpath = sourceSets.test.runtimeClasspath main = mainClassName }
Nun kann ein Distributionspaket mit dem Gradle-Task distZip
erstellt werden.
Ein komplettes Beispiel
Eine vollständiges Beispiel der build.gradle
kann so aussehen:
import org.apache.tools.ant.filters.FixCrLfFilter plugins { id 'java' id 'distribution' } version = '1.0.0' def mainClassName = 'de.muspellheim.example.App' jar { manifest { attributes 'Main-Class': mainClassName attributes 'Implementation-Version': project.version } from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } } distributions { main { contents { from jar from(projectDir) { include 'README.md' include 'CHANGELOG.md' rename 'md', 'txt' filter(FixCrLfFilter, eol: FixCrLfFilter.CrLf.newInstance('crlf')) } from "$projectDir/src/dist" } } } distZip { version = '' doLast { archiveFile.get().asFile.renameTo(destinationDirectory.file("${project.name}-v${project.version}.zip").get().asFile) } } assemble.dependsOn = [distZip] task run(type: JavaExec) { group 'application' classpath = sourceSets.test.runtimeClasspath main = mainClassName }
Bei dieser Variante gibt es noch ein paar Erweiterungen:
- Die Version der Applikation wird in das Manifest des JARs geschrieben, so dass diese später zu Beispiel in einem About-Dialog ausgelesen werden kann.
- Die README und CHANGELOG werden mit eingepackt und dabei die Dateiendung zu
txt
geändert und die Zeilenenden für Windowsnutzer angepasst. - Für die ZIP-Datei der Distribution wird die Version entfernt, damit sie im Verzeichnisname in der ZIP-Datei nicht mehr enthalten ist. Nach dem Erzeugen der ZIP-Datei wird diese umbenannt, damit die Version im Namen der ZIP-Datei dagegen noch enthalten ist.
- Der Task
distZip
wird als einzige Abhängigkeit vom Taskassemble
gesetzt, so dass nur noch ein ZIP und kein TAR mehr erzeugt wird.