mpopp_0

Java-Anwendungen mit Maven als zip-Archiv paketieren

Maven bietet einfache Möglichkeiten, um ein Projekt als jar- oder war-Datei auszuliefern. Für eigenständige Anwendungen reicht es aber oft nicht aus, eine jar-Datei zu generieren. Das gewünschte Ergebnis ist vielmehr ein zip-Archiv, welches die Anwendung inklusive der benötigten Bibliotheken, einem Skript zum Starten der Anwendung, und einer Dokumentation z.B. in Form einer Readme-Datei enthält.

In diesem Artikel werde ich vorstellen, wie man mit dem Maven Assembly Plugin und dem Application Assembler Maven Plugin solch ein auslieferbares Archiv erzeugt.

Um in der package-Phase von Maven ein zip-Archiv zu erzeugen muss das Maven Assembly Plugin unter build/plugins in pom.xml konfiguriert werden:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>2.2.1</version>
    <configuration>
        <descriptors>
            <descriptor>src/main/assembly/dist.xml</descriptor>
        </descriptors>
    </configuration>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>single</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Diese Konfiguration verweist nur auf die Datei dist.xml, in welcher die eigentliche Konfiguration vorgenommen wird. In dieser Descriptor-Datei wird angegeben, in welchem Dateiformat Archive erzeugt werden sollen (in diesem Beispiel zip und tar.gz), und welche Dateien im Archiv enthalten sein sollen:

<assembly>
    <id>dist</id>
    <formats>
        <format>tar.gz</format>
        <format>zip</format>
    </formats>
    <fileSets>
        <fileSet>
            <directory>${project.basedir}</directory>
            <outputDirectory>/</outputDirectory>
            <includes>
                <include>README*</include>
            </includes>
        </fileSet>
        <fileSet>
            <directory>${project.build.directory}/appassembler</directory>
            <outputDirectory>/</outputDirectory>
        </fileSet>
    </fileSets>
</assembly>

Wenn wir mit der aktuellen Konfiguration “mvn package” ausführen, bekommen wir erst mal ein zip-Archiv mit der README-Datei, aber noch keine ausführbare Anwendung. Man könnte zwar schon in der Descriptor-Datei vom Assembly Plugin die Einbindung der benötigten jar-Dateien konfigurieren, aber wir verwenden hierfür das Application Assembler Maven Plugin, mit welchem dann auch gleich ein Shell-Skript bzw. eine Bat-Datei für Windows generiert werden.

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>appassembler-maven-plugin</artifactId>
    <version>1.6</version>
    <configuration>
        <programs>
            <program>
                <mainClass>com.cenarion.example.mvndistsample.HelloWorld</mainClass>
                <id>helloworld</id> <!-- Name des generierten Scripts -->
            </program>
        </programs>
        <!-- Konfiguration von /src/main/config kopieren -->
        <copyConfigurationDirectory>true</copyConfigurationDirectory>
        <repositoryLayout>flat</repositoryLayout>
        <repositoryName>lib</repositoryName>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>assemble</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Wenn wir mit der aktuellen Konfiguration “mvn package” ausführen, bekommen wir erst mal ein zip-Archiv mit der README-Datei, aber noch keine ausführbare Anwendung. Man könnte zwar schon in der Descriptor-Datei vom Assembly Plugin die Einbindung der benötigten jar-Dateien konfigurieren, aber wir verwenden hierfür das Application Assembler Maven Plugin, mit welchem dann auch gleich ein Shell-Skript bzw. eine Bat-Datei für Windows generiert werden.

Das Ausgabe-Verzeichnis vom Application Assembler hatten wir bereits in obiger Konfiguration in die erzeugte zip-Datei inkludiert. Da beide Plugins in der package-Phase vom Maven-Build ausgeführt werden, ist also wichtig, dass die folgende Konfiguration des Application Assemblers auch vor der Konfiguration des Assembly Plugins in pom.xml eingefügt wird, um die Dateien zu erzeugen, bevor das Archiv erstellt wird.

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>appassembler-maven-plugin</artifactId>
    <version>1.6</version>
    <configuration>
        <programs>
            <program>
                <mainClass>com.cenarion.example.mvndistsample.HelloWorld</mainClass>
                <id>helloworld</id> <!-- Name des generierten Scripts -->
            </program>
        </programs>
        <!-- Konfiguration von /src/main/config kopieren -->
        <copyConfigurationDirectory>true</copyConfigurationDirectory>
        <repositoryLayout>flat</repositoryLayout>
        <repositoryName>lib</repositoryName>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>assemble</goal>
            </goals>
        </execution>
    </executions>
</plugin>

In der Konfiguration des Application Assemblers werden die für das erzeugte Anwendungs-Skript benötigten Informationen, wie zum Beispiel die Klasse mit der main-Methode, angegeben. Weiters werden hier auch Informationen zum Kopieren der jar-Dateien aus den Projekt-Abhängigkeiten angegeben (repositoryLayout und repositoryName).

Interessant ist hier auch noch das Konfigurationselement copyConfigurationDirectory. Dieses bewirkt, dass alle Dateien von “/src/main/config” in das Verzeichnis “/etc” kopiert werden. Hier können dann Konfigurationsdateien eingefügt werden, welche vom Endanwender angepasst werden dürfen. In den generierten Anwendungs-Skripten wird dieses Verzeichnis dann in den Classpath hinzugefügt, sodass darin enthaltene Konfigurationen (z.B. properties-Dateien wie in etwa eine log4j-Konfiguration) einfach im Java-Code aus dem Classpath geladen werden können.

Wenn wir nun mit dieser Konfiguration “mvn package” ausführen, wird die Anwendung inklusive den generierten Skripten zum Starten der Anwendung und allen benötigten Abhängigkeiten fertig paketiert und im target-Verzeichnis des Projektes abgelegt. Weiters ist durch die automatische Generierung der Skripte auch Plattformunabhängigkeit gegeben, welche bei manuellem Schreiben nur mit erheblichem Aufwand möglich wäre.

Das Beispielprojekt mit oben vorgestellter Konfiguration kannst du hier downloaden. Auch diese zip-Datei wurde mit dem Assembly Plugin erstellt. Dafür gibt es den vordefinierten Descriptor “project” um das Projektverzeichnis, also den Quelltext ohne target-Verzeichnis oder etwaige svn-Verzeichnisse, zu paketieren. Man ist also mit dem Assembly Plugin nicht auf die Paketierung von fertigen java-Anwendungen beschrängt, sondern es können damit beliebige Dateien in ein zip-Archiv gepackt werden. So wäre ein weiterer Anwendungsfall zum Beispiel die Paketierung von Konfigurationsverzeichnissen, welche dann einfach am Server deployed werden können, indem das Archiv extrahiert wird.

Hast du auch schon mal mit Maven andere Artefakte, als nur jar-, war-Dateien oder ähnliches erzeugt? Wenn ja, wofür verwendest du das Assembly Plugin?

Markus Popp (Software Developer)