Whats the best way to bundle the whole project in Maven? - java

I'd like to create a fat-jar which is autoexecutable (Shade Plugin), but also includes sources, resources, tests, and everything in a Eclipse-importable way.
The jar would be single-file executable app, which also contains the whole project in an importable fashion as a maven project someway (after unzipping the jar, I assume).
Another option would be a resulting project zip that includes the binary distribution at base level.
Is there such a thing?

A possible solution requires 3 steps:
Create an uber-jar of the project with the maven-shade-plugin. This JAR will contain all the dependencies and will be executable.
Create a side artifact consisting of the sources of the project with the maven-assembly-plugin. This ZIP will contain the pom.xml and all of the sources under src.
Create another side artitact that will effectively be our final artifact, that will contain both the unpacked shaded JAR (to make it effectively executable) and the sources ZIP.
This would be a possible configuration:
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<id>make-uberjar</id>
<goals>
<goal>shade</goal>
</goals>
<phase>package</phase>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>fully.classified.name.to.main.Class</mainClass>
</transformer>
</transformers>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<id>sources</id>
<goals>
<goal>single</goal>
</goals>
<phase>package</phase>
<configuration>
<descriptors>
<descriptor>src/main/assembly/assembly-sources.xml</descriptor>
</descriptors>
</configuration>
</execution>
<execution>
<id>final</id>
<goals>
<goal>single</goal>
</goals>
<phase>package</phase>
<configuration>
<descriptors>
<descriptor>src/main/assembly/assembly.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
This configures the execution of the maven-shade-plugin and 2 execution of the maven-assembly-plugin. The first execution will creates the sources ZIP. This would be the assembly-sources.xml assembly descriptor file:
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
<id>sources</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>${project.basedir}</directory>
<includes>
<include>pom.xml</include>
<include>src/**</include>
</includes>
</fileSet>
</fileSets>
</assembly>
It includes pom.xml and everything under src into a ZIP having the sources classifier.
The second assembly.xml assembly descriptor file would be:
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
<id>jar-with-sources</id>
<formats>
<format>jar</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<dependencySets>
<dependencySet>
<includes>
<include>${project.groupId}:${project.artifactId}:jar:${project.version}</include>
</includes>
<unpack>true</unpack>
</dependencySet>
<dependencySet>
<useProjectAttachments>true</useProjectAttachments>
<includes>
<include>${project.groupId}:${project.artifactId}:zip:sources:${project.version}</include>
</includes>
</dependencySet>
</dependencySets>
</assembly>
This creates 2 <dependencySet>. The first one unpacks the shaded JAR (which replaced the main artifact at that point in the build). The second one simply includes the sources ZIP attached artifact; note that we need to use <useProjectAttachments> to include the attached sources ZIP.
When you run mvn clean install, you will have as result a file {finalName}-jar-with-sources.jar that will be your wanted executable JAR. It will also contain a ZIP file of all the sources in the root folder.

I found that using the maven assembly-plugin gives you the best control over these kind of things, for both an uber jar or an installable zip. The shade plugin works well for uber jars, but not so well for installer zips or installers.
In the past i also augmented that functionality by adding in the mix the izpack plugin, which creates a real installer, off of the distributable zip that you create with the assembly plugin.
For example, with the assembly-plugin you can choose to include or exclude certain file resources. This happens for example when you want to use a configuration file while working with the IDE, but you really want to exclude it in the final distribution zip, or you want to include a slightly different one.
Same happens when you want to generate an rpm: the assembly plugin can help quite a bit in first preparing what's needed for the rpm plugin to operate.
The assembly plugin comes with all sort of settings, which you declare in its xml configuration file and it is well documented.

In most cases, trying to pack all the application dependencies in a single .jar is a bad idea, mainly because .jars are not designed to be used this way. In fact, they might contain META-INF/ and other jar-specific files that might clash each other.
When I have to build binary distributions that can be run from the command line, I use the Assembly plug-in, as I've described in this post. If you need to distribute a GUI application or a one-click, self-contained web server, the approach would be very similar.

Related

maven-assembly-plugin - What does "useProjectArtifact" actually do?

I am trying to bring over an old project to maven for simplification of the build and deployment process.
In the project there is one set of java classes that need to be bundled with three different sets of resources into three different .jars.
Trying to follow this: How to maven release multiple profiles project when redeploys are disabled I am trying to use the maven-assembly-plugin using three executions with in the assembly.xml for the resources.
I would like to not have any dependencies in the resulting jar, only my classes and my resources.
I added
<dependencySets>
<dependencySet>
<unpack>true</unpack>
<useProjectArtifact>true</useProjectArtifact>
</dependencySet>
</dependencySets>
to my assembly.xml thinking that not using any <includes> and <excludes> this would only add my own classes to the .jar, instead now I have all dependencies in the .jar as well. If I remove this, I don't get any classes at all - not even my own. I would like to have a .jar that only has my own classes but not the dependencies that are in the pom.
I think that I am missunderstanding the purpose of useProjectArtifact?
Can I achieve what I want without using for all the dependencies? Or is the maven-assembly-plugin not the way to go?
Extract of pom.xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<id>bundle-1</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>${project.basedir}/path/to/assembly.xml</descriptor>
</descriptors>
</configuration>
</execution>
<execution>

How to create a jar with Maven that includes dependencies in a separate folder for each dependency

I want build a Maven project with dependencies outside the .jar, but every dependency must be in a separate directory as:
libs/${dependency.groupId}/${dependency.artifactId}/${dependency.version}
Just like this:
/build
my.jar
/libs
/org.yaml
/snakeyaml
/1.17
snakeyaml-1.17.jar
/etc...
I know that there is a maven-dependency-plugin, but how to configure it like that? Or there is any other plugin that can do this?
What you really want to do here is to create a custom archive for your project. For that, you want to use the maven-assembly-plugin.
There are multiple aspects to consider:
You want a base directory of build. This is set by the <baseDirectory> parameter.
You want to have all of the project's dependencies in a repo-like directory layour. For that, you can use a custom outputFileNameMapping for the dependencySet. This enables to use a set of properties; in this case we're interested in the group id, artifact id and version. We need to disable <useProjectArtifact>, otherwise it would also include the project main artifact.
For the main artifact, we simply use a <file> element, pointing to the main artifact of the project.
The assembly descriptor would look like:
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>bin</id>
<formats>
<format>dir</format> <!-- to turn into final archive type -->
</formats>
<baseDirectory>build</baseDirectory>
<files>
<file>
<source>${project.build.directory}/${project.build.finalName}.${project.packaging}</source>
</file>
</files>
<dependencySets>
<dependencySet>
<outputDirectory>libs</outputDirectory>
<outputFileNameMapping>${artifact.groupId}/${artifact.artifactId}/${artifact.version}/${artifact.artifactId}-${artifact.version}${dashClassifier?}.${artifact.extension}</outputFileNameMapping>
<useProjectArtifact>false</useProjectArtifact>
</dependencySet>
</dependencySets>
</assembly>
Note that it uses a <format> of dir, meaning that it does not "package" the output structure inside an archive but leaves it as a directory structure. In the future, you'll be able to customize this assembly descriptor with your launcher scripts and make a final archive of it by changing this format to the one you'll want.
Finally, the configuration of the plugin itself will be
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<descriptors>
<descriptor>src/main/assembly/assembly.xml</descriptor> <!-- path to the above assembly descriptor -->
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
With such a configuration, launching mvn clean install will output the wanted directory structure inside target/${finalName}.

maven assembly plugin including jars dependencies and external files

I am trying to use the Assembly plugin to include the dependecies as well as
Here is my assembly plugin
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4.1</version>
<executions>
<execution>
<id>create-executable-jar</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
<configuration>
<descriptorRefs>
<descriptorRef>
jar-with-dependencies
</descriptorRef>
</descriptorRefs>
<descriptors>
<descriptor>src/assembly/assembly.xml</descriptor>
</descriptors>
<archive>
<manifest>
<mainClass>com.sarm.myproject.XMLParser.LPUnMarshaller</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
here is my assembly.xml descriptor, i made the id same as the descritpor ref as it was creating a different jar for this descriptor. So i have two descriptors one is a jar-with-dependencies descriptor ref and one is the below descriptor, in this case what is happening is that it creating two jars and the second jar overwrites the previous jar. How is it possible to include these two in the same jar.
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>jar-with-dependencies</id>
<formats>
<format>jar</format>
</formats>
<fileSets>
<fileSet>
<directory>${basedir}</directory>
<includes>
<include>*.xml</include>
</includes>
<excludes>
<exclude>30000dests.xml</exclude>
</excludes>
</fileSet>
<fileSet>
<directory>${basedir}/test</directory>
<includes>
<include>*.xml</include>
</includes>
</fileSet>
</fileSets>
</assembly>
Edit: I have tried some other approaches and i have seen that if i use the assembly.xml as a descriptor, a new jar is created along side any other default jar that is created as well. So i end up with two jars. The default jar is an executable like the jar plugin is configured. even though the assembly plugin is configured to have a manifest.mf with my main class name the jar created thru the assmbly.xml is not executable and has a folder of my projects name with the executable jar inside it.
I use mvn clean install to build my project and the jar file.
You should create two modules that produce two jars. Typically maven is going to produce one jar file per module with the module name as the jar name.
Maven recommends that each module have a single output artifact.
If you have concerns about duplicating code. You can inherit relevant stuff by have a parent pom.

Generate distribution from Maven project

I want to build an application distribution with Maven. I have a module in my project which holds the main source code. I decided to build the application distribution from this module. I'm telling maven in the module's POM to copy all the configuration files and dependant libs to the target/ directory. My problem is that Maven keeps all the build related temporary dirs (like. classes, generated-sources, maven-archiver) in the target directory. I wan't to auto delete these at least during the install phase. How can i achive this? If i put the maven-clean-plugin to the end of the build it looks like Maven always deletes the whole target directory, no matter who i'm trying to exclude files what i'm trying to keep.
try this in your pom
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-4</version>
<executions>
<execution>
<id>user_distribution</id>
<phase>package</phase>
<goals>
<goal>attached</goal>
</goals>
<configuration>
<descriptors>
<descriptor>src/main/assembly/dist.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
and here is the xml
<?xml version="1.0" encoding="UTF-8"?>
<assembly xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/xsd/assembly-1.1.1.xsd">
<id>dist</id>
<formats>
<format>zip</format>
</formats>
<files>
<file>
<source>target/${pom.artifactId}-${pom.version}.jar</source>
<outputDirectory>lib/</outputDirectory>
</file>
</files>
<fileSets>
<fileSet>
<directory>directory to be included</directory>
<outputDirectory>/</outputDirectory>
<includes>
<include>file name to be included</include>
</includes>
</fileSet>
<fileSet>
<directory>another directory to be included</directory>
<outputDirectory>/</outputDirectory>
</fileSet>
</fileSets>
</assembly>
I'm telling maven in the module's POM to copy all the configuration
files and dependant libs to the target/ directory
How are you doing this?
Based on the question, it looks like you need to worry less about selectively deleting contents of target folder (which can be done by customizing maven clean plugin), but creating a distribution using maven assembly plugin (as pointed out by #Scorpion).
The latter allows you to bundle together all your project artifacts including the dependencies into zip or other formats, which can then be easily used by developers. You may want to decouple this from regular build by using a separate profile, so that the distribution is built only on need basis.

Building non-webapp maven2 based project with extra non-essential files and batch files

I'm just beginning to grasp the setup of maven2 while porting over a simple project of mine. I've ran through the package command line example on the Sonatype web site but I'm a bit confused of how I could expand and change the packaging of this project. I've tried to find more information on this subject but I think I'm either thinking about the problem wrong or phrasing the searches wrong.
Essentially I want to build a project that would be a zip of all the dependency jars, the main jar itself, batch scripts to start it for convenience and maybe other various files for the application (like properties files or something). However, I don't want these all bundled into the jar. I'd like to just have a compressed archive build of the project with a lib directory of all the jars, the root containing properties files and batch scripts and maybe sub folders to hold extra non-essential files. Kind of like:
sample-proj.zip:
easy.bat
props.ini
lib
dependent1.jar
dependent2.jar
main.jar
sub_dir
someextrafile.txt
What's the correct way of building a project like this using maven2? Do I need to make a parent project that builds the zip and includes the sub project as a module? This would be just a simple project that wouldn't need to be multi-module...
Building on matt b's answer. Here's some examples of how to use the referenced plugins. Note the executions are bound to the integration-test phase to ensure the jar has been created before attempting to package it.
The appassembler-maven-plugin will generate batch/shell files, download the dependencies (in this example to the lib directory), and place all the contents in target/appassembler
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>appassembler-maven-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<id>assemble-standalone</id>
<phase>integration-test</phase>
<goals>
<goal>assemble</goal>
<!--if you only want to create the repo and skip script generation,
use this goal instead-->
<!--goal>create-repository</goal-->
</goals>
<configuration>
<programs>
<program>
<mainClass>name.seller.rich.Foo</mainClass>
<name>foo</name>
</program>
</programs>
<platforms>
<platform>windows</platform>
<platform>unix</platform>
</platforms>
<repositoryLayout>flat</repositoryLayout>
<repositoryName>lib</repositoryName>
</configuration>
</execution>
</executions>
</plugin>
The assembly plugin can be used to package the appassembler output into a zip:
<profiles>
<profile>
<id>archive</id>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-2</version>
<executions>
<execution>
<phase>integration-test</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>src/main/assembly/archive.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
And the assembly descriptor looks something like this:
<assembly>
<id>archive</id>
<formats>
<format>zip</format>
</formats>
<fileSets>
<fileSet>
<directory>${project.build.directory}/appassembler</directory>
<outputDirectory>/</outputDirectory>
</fileSet>
</fileSets>
<!-- add any other filesets to include docs, readmes, licences etc here -->
</assembly>
The appassembler plugin does exactly what you describe: create shell scripts, and package up the dependencies.
However I don't believe that it packages them up into a zip/tar.gz/etc (appassembler just puts them into a folder structure under target/appassembler. But, you can use the maven-assembly plugin in concert with appassembler to just package up that output - maven-assembly does an excellent job of producing a zip/jar/tar.gz/bzip2/whatever, and you can control what goes into the file completely with an XML descriptor.

Categories

Resources