Wenn man mit Maven eine JAR-Datei baut, werden die abhängigen Bibliotheken, im Gegensatz zu WAR-Archiven, normalerweise nicht mit in das erstellte Artefakt inkludiert. Dies ist meistens auch sinnvoll, da die notwendigen Resourcen nicht mehrfach abgelegt werden sollen und bei Bedarf leicher austauschbar sind. Möchte man jedoch eine lauffähige Anwendung verbreiten, ist es zwingend notwendig, dass alle Abhängigkeiten vorhanden sind.
Als Lösung dafür sieht Maven das maven-assembly-plugin vor, welches alle Dateien (wie z.B. die Klassen-Dateien, Skripte, Property-Dateien, Bibliotheken, etc), die für einen Release notwendig sind, in einem Archiv (z.B. jar, zip, tar, gzip tar, als Verzeichnis, etc.) zusammenstellt.
Das Assembly-Plugin kann auch verwendet werden, um eine ausführbare JAR-Datei zu erstellen.
So wäre es schön, eine ausführbare JAR-Datei zu erstellen, die den eigenen Programmcode und alle Abhängigkeiten enthält.
Leider kann Java die benötigten Bibliotheken nicht aus der selben JAR-Datei laden, die auch die Main-Klasse enthält, sodass man entweder
- einen eigenen (angepassten) Class-Loader verwenden muss,
- oder alle Klassen auspacken und anschließend in eine einizige JAR-Datei verpackt (Scheidet aus, wenn man vorhandene signierte JAR-Dateien verwenden muss.)
- oder die JAR-Dateien in eine weitere JAR-Datei verpacken muss.
Um die Lösung 2 umzusetzten, muss man die Konfiguration des maven-assembly-plugin in der pom.xml wie folgt anpassen:
<project>
...
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>org.sample.App</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
...
</project>
Durch den Aufruf des folgenden Kommandos, kann man das packen des Assembly's auslösen:
$ mvn clean package assembly:assembly
Soll das Assembly automatisch erstellt werden, kann man das maven-assembly-plugin an Maven Lebenszyklus binden:
<project>
...
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
...
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>attached</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>
Nun wird das Assembly schon durch einen Aufruf des folgenden Kommandos zusammengestellt:
$ mvn clean package
Möchte man sich an Lösung 3 versuchen, muss man eine eigene Assembly-Konfiguration hinterlegen:
<project>
...
<build>
<plugins>
...
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>
<descriptor>src/assembly/dist.xml</descriptor>
</descriptors>
</configuration>
</plugin>
</plugins>
</build>
...
</project>
Die Datei dist.xml schaut z.B. wie folgt aus:
<assembly>
<id>dist</id>
<formats>
<format>jar</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>target</directory>
<outputDirectory></outputDirectory>
<includes>
<include>*.jar</include>
</includes>
</fileSet>
</fileSets>
<dependencySets>
<dependencySet>
<outputDirectory>/lib</outputDirectory>
<unpack>false</unpack>
<scope>runtime</scope>
</dependencySet>
</dependencySets>
</assembly>
Da die Bibliotheken nun auf der gleichen Ebene wie unsere JAR-Datei mit der Main-Klasse auftauchen, muss diese nun eine Definition des Classpath im MANIFEST.MF erhalten:
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>com.example.App</mainClass>
<addClasspath>true</addClasspath>
<classpathPrefix>lib</classpathPrefix>
</manifest>
</archive>
</configuration>
</plugin>
...
</project>
http://maven.apache.org/plugins/maven-assembly-plugin/usage.html#Resources