3
votes
<plugin>
    <artifactId>maven-assembly-plugin</artifactId>
    <executions>
      <execution>
        <phase>package</phase>
        <goals>
          <goal>single</goal>
        </goals>
      </execution>
    </executions>
    <configuration>
         <archive>
            <manifest>           
                <mainClass>com.XXXX.XXXXOfflineApp</mainClass>
            </manifest>
        </archive>
      <descriptorRefs>
        <descriptorRef>jar-with-dependencies</descriptorRef>
      </descriptorRefs>
    </configuration>
  </plugin>
</plugins>

I have the above code piece to create an jar with dependencies, but in my pom i also have some dependencies with scope provided but these are not included in the uber jar, Well I cannot change the scope of those dependencies because the regular jars build should not include them.Since those are provided by the container.

5

5 Answers

2
votes

The predefined jar-with-dependencies descriptor unpacks all dependencies needed at runtime into the root of the produced JAR. If you want to add provided dependencies, it is possible to build on it and add a specific <dependencySet> with <scope>provided</scope>.

<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-dependencies</id>
  <formats>
    <format>jar</format>
  </formats>
  <includeBaseDirectory>false</includeBaseDirectory>
  <dependencySets>
    <dependencySet>
      <outputDirectory>/</outputDirectory>
      <useProjectArtifact>true</useProjectArtifact>
      <unpack>true</unpack>
      <scope>provided</scope>
    </dependencySet>
    <dependencySet>
      <outputDirectory>/</outputDirectory>
      <useProjectArtifact>true</useProjectArtifact>
      <unpack>true</unpack>
      <scope>runtime</scope>
    </dependencySet>
  </dependencySets>
</assembly>

This will include all dependencies normally needed at runtime (so having a scope of compile and runtime) and all dependencies having the provided scope.

You would configure the use of this descriptor format with:

<plugin>
  <artifactId>maven-assembly-plugin</artifactId>
  <version>2.6</version>
  <executions>
    <execution>
      <id>id</id>
      <phase>package</phase>
      <goals>
        <goal>single</goal>
      </goals>
      <configuration>
        <archive>
          <manifest>
            <mainClass>com.XXXX.XXXXOfflineApp</mainClass>
          </manifest>
        </archive>
        <descriptors>
          <descriptor>path/to/assembly.xml</descriptor>
        </descriptors>
      </configuration>
    </execution>
  </executions>
</plugin>

where path/to/assembly.xml corresponds to the path to the above descriptor format, relative to the location of the POM.

2
votes

Try Maven Shade plugin:

You can control which project dependencies should be included/excluded in the uber JAR. Lots of options.

https://maven.apache.org/plugins/maven-shade-plugin/examples/includes-excludes.html

1
votes

This is an old post but I think both answers are not accurate and not 100% correct.

The fact is that uber JAR must contain all runtime and provided dependencies. But the first issue starts from here.

Usually, dependencies contain their own MANIFEST.MF files and if you assembly them into a JAR with the examples above then you will get a WARNING during the Maven build about the fact that you overwrite them because JAR can only have one MANIFEST file. This is not good. So please do not put dependencies under the same directory.

The second issue is related to the following two warnings:

[WARNING] Configuration options: 'appendAssemblyId' is set to false, and 'classifier' is missing. Instead of attaching the assembly file: [...]/target/....jar, it will become the file for main project artifact.

NOTE: If multiple descriptors or descriptor-formats are provided for this project, the value of this file will be non-deterministic!

[WARNING] Replacing pre-existing project main-artifact file: [...]/target/....jar with assembly file: [...]/target/....jar

The reason why you get this because you build a new JAR with the assembly-plugin but the classifier of your assembled JAR is not specified.

A Maven project that is not of type pom produces by default as the main artifact. This artifact is the result of maven packaging (JAR, WAR, or EAR). This artifact is attached to your project and used by Maven while installing, deploying, or releasing your project with mvn install, mvn deploy. Of course, you can generate different files under the target folder, but your main artifact is still the same.

And the JAR that you are building with the assembly plugin is in conflict with your default artifact. So I highly recommend you to NOT use this:

<appendAssemblyId>false</appendAssemblyId>

The assembly ID is the classifies of your uber JAR so, please set the property above to TRUE or ignore it completely because its default value is TRUE.

The default naming convention for artifacts is this: artifactId-version(-classifier).type

You can find a complete example below.

pom.xml:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>${maven.assembly.plugin.version}</version>
            <configuration>
                <appendAssemblyId>false</appendAssemblyId>
                <finalName>${project.artifactId}-aa-${project.version}</finalName>
                <descriptors>
                    <descriptor>${basedir}/src/main/resources/assembly.xml</descriptor>
                </descriptors>
                <archive>
                    <manifest>
                        <mainClass>x.y.z.Main</mainClass>
                    </manifest>
                </archive>
            </configuration>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Assembly plugin configuration:

<assembly>
    <id>with-dependencies</id>
    <formats>
        <format>jar</format>
    </formats>
    <includeBaseDirectory>false</includeBaseDirectory>
    <dependencySets>
        <dependencySet>
            <outputDirectory>/</outputDirectory>
            <useProjectArtifact>true</useProjectArtifact>
            <unpack>true</unpack>
            <scope>provided</scope>
        </dependencySet>
        <dependencySet>
            <outputDirectory>/</outputDirectory>
            <useProjectArtifact>true</useProjectArtifact>
            <unpack>true</unpack>
            <scope>runtime</scope>
        </dependencySet>
    </dependencySets>
</assembly>

This is how the assembled JAR is named: <project-name>-1.0-with-dependencies.jar

Hope that it helps you to build your perfect uber JAR.

0
votes

Try adding an assembly.xml file like this:

<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>fat-tests</id>
    <formats>
        <format>jar</format>
    </formats>
    <includeBaseDirectory>false</includeBaseDirectory>
    <dependencySets>
        <dependencySet>
            <outputDirectory>/</outputDirectory>
            <useProjectArtifact>true</useProjectArtifact>
            <unpack>true</unpack>
            <scope>test</scope>
        </dependencySet>
    </dependencySets>
    <fileSets>
        <fileSet>
            <directory>${project.build.directory}/test-classes</directory>
            <outputDirectory>/</outputDirectory>
            <includes>
                <include>**/*.class</include>
            </includes>
            <useDefaultExcludes>true</useDefaultExcludes>
        </fileSet>
    </fileSets>
</assembly>

Adding any dependencySet you need for each scope.

You'll also need this in your pom.xml

    <plugin>
        <artifactId>maven-assembly-plugin</artifactId>
        <configuration>
            <descriptor>src/main/assembly/assembly.xml</descriptor>
        </configuration>
0
votes

I was also working on creating a distribution file, I did some thing like below assembly.xml

<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-dependencies</id>
    <formats>
        <format>jar</format>
    </formats>
    <includeBaseDirectory>false</includeBaseDirectory>
    <dependencySets>
        <dependencySet>
            <outputDirectory>/</outputDirectory>
            <useProjectArtifact>true</useProjectArtifact>
            <unpack>true</unpack>
            <scope>provided</scope>
        </dependencySet>
        <dependencySet>
            <outputDirectory>/</outputDirectory>
            <useProjectArtifact>true</useProjectArtifact>
            <unpack>true</unpack>
            <scope>runtime</scope>
        </dependencySet>
    </dependencySets>
</assembly>

distribution.xml

   <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">
    </assembly>
    <id>offline-distribution</id>
    <!-- Specifies that our binary distribution is a zip package -->
    <formats>
        <format>zip</format>
    </formats>
    <!-- Adds the dependencies of our application to the lib directory -->
    <dependencySets>

        <dependencySet>
            <useProjectArtifact>true</useProjectArtifact>
            <outputDirectory>/lib</outputDirectory>
            <unpack>false</unpack>
        </dependencySet>
        <dependencySet>
            <outputDirectory>/lib</outputDirectory>
            <useProjectArtifact>true</useProjectArtifact>
            <unpack>false</unpack>
            <scope>provided</scope>
        </dependencySet>
        <dependencySet>
            <outputDirectory>/lib</outputDirectory>
            <useProjectArtifact>true</useProjectArtifact>
            <unpack>false</unpack>
            <scope>runtime</scope>
        </dependencySet>
    </dependencySets>

    <fileSets>

        <fileSet>
            <directory>${project.basedir}</directory>
            <outputDirectory>/bin</outputDirectory>
            <includes>
                <include>startup*</include>
            </includes>
            <useDefaultExcludes>true</useDefaultExcludes>
        </fileSet>
        <fileSet>
            <directory>${project.basedir}</directory>
            <outputDirectory>/</outputDirectory>
            <includes>
                <include>README*</include>
            </includes>
            <useDefaultExcludes>true</useDefaultExcludes>
        </fileSet>
        <fileSet>
            <directory>${project.basedir}/src/main/resources</directory>
            <outputDirectory>/config</outputDirectory>
            <includes>
                <include>log4j2.xml</include>
            </includes>
            <useDefaultExcludes>true</useDefaultExcludes>
        </fileSet>
    </fileSets>
</assembly>

In parent pom.xml

<plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>2.6</version>
            <executions>
                <execution>
                    <id>id</id>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                    <configuration>
                        <!-- <archive>
                            <manifest>
                                <mainClass>com.XXXX.XXXXOfflineApp</mainClass>
                            </manifest>
                        </archive> -->
                        <descriptors>
                            <descriptor>distribution.xml</descriptor>
                             <descriptor>assembly.xml</descriptor>
                        </descriptors>
                    </configuration>
                </execution>
            </executions>
        </plugin>