Maven: Packaging dependencies alongside project JAR?

50,758

Solution 1

I've like Maven to package a project with run-time dependencies.

This part is unclear (it's not exactly what you describe just after). My answer covers what you described.

I expect it to create a JAR file with the following manifest (...)

Configure the Maven Jar Plugin to do so (or more precisely, the Maven Archiver):

<project>
  ...
  <build>
    <plugins>
      <plugin>
         <artifactId>maven-jar-plugin</artifactId>
         <configuration>
           <archive>
             <manifest>
               <addClasspath>true</addClasspath>
               <classpathPrefix>lib/</classpathPrefix>
               <mainClass>com.acme.MainClass</mainClass>
             </manifest>
           </archive>
         </configuration>
      </plugin>
    </plugins>
  </build>
  ...
  <dependencies>
    <dependency>
      <groupId>dependency1</groupId>
      <artifactId>dependency1</artifactId>
      <version>X.Y</version>
    </dependency>
    <dependency>
      <groupId>dependency2</groupId>
      <artifactId>dependency2</artifactId>
      <version>W.Z</version>
    </dependency>
  </dependencies>
  ...
</project>

And this will produce a MANIFEST.MF with the following entries:

...
Main-Class: fully.qualified.MainClass
Class-Path: lib/dependency1-X.Y.jar lib/dependency2-W.Z.jar
...

and create the following directory structure (...)

This is doable using the Maven Dependency Plugin and the dependency:copy-dependencies goal. From the documentation:

  • dependency:copy-dependencies takes the list of project direct dependencies and optionally transitive dependencies and copies them to a specified location, stripping the version if desired. This goal can also be run from the command line.

You could bind it on the package phase:

<project>
  [...]
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <version>2.1</version>
        <executions>
          <execution>
            <id>copy-dependencies</id>
            <phase>package</phase>
            <goals>
              <goal>copy-dependencies</goal>
            </goals>
            <configuration>
              <outputDirectory>${project.build.directory}/lib</outputDirectory>
              <overWriteReleases>false</overWriteReleases>
              <overWriteSnapshots>false</overWriteSnapshots>
              <overWriteIfNewer>true</overWriteIfNewer>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  [...]
</project>

Solution 2

Add the following plugins in pom.xml. Check the value at mainClass,classpathPrefix,addClasspath tags.

<plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <version>2.4</version>
        <configuration>
            <archive>
                <manifest>
                    <mainClass>org.apache.camel.spring.Main</mainClass>
                    <classpathPrefix>lib/</classpathPrefix>
                    <addClasspath>true</addClasspath>
                </manifest>
            </archive>
        </configuration>
    </plugin>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-assembly-plugin</artifactId>
        <version>2.4</version>
        <configuration>
            <descriptors>
                <descriptor>src/assembly/some-assembly.xml</descriptor>
            </descriptors>
        </configuration>
        <executions>
            <execution>
                <id>make-assembly</id>
                <phase>package</phase>
                <goals>
                    <goal>single</goal>
                </goals>
            </execution>
        </executions>
    </plugin>

Create some-assembly.xml under src/assembly as below.

<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
<id>distribution</id>
<formats>
    <format>zip</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<fileSets>
    <fileSet>
        <directory>${project.build.directory}</directory>
        <outputDirectory>/</outputDirectory>
        <includes>
            <include>*.jar</include>
        </includes>
    </fileSet>
</fileSets>
<dependencySets>
    <dependencySet>
        <scope>runtime</scope>
        <outputDirectory>/lib</outputDirectory>
        <useProjectArtifact>false</useProjectArtifact>
        <unpack>false</unpack>
    </dependencySet>
</dependencySets>

Note that useProjectArtifact flag to false, unpack flag to false. If root folder inside zip file is not required,then one can make includeBaseDirectory to false.

This will create name-version-distribution.zip file. Inside zip file, there will be folder name-version. Inside this folder, your executable jar and lib folder containing all dependency jars will be present. Check manifest.MF file of executable jar. It contains both main class and classpath information.

Share:
50,758

Related videos on Youtube

Gili
Author by

Gili

Email: cowwoc2020 at gmail dot com.

Updated on December 17, 2020

Comments

  • Gili
    Gili over 3 years

    I'd like Maven to package a project alongside its run-time dependencies. I expect it to create a JAR file with the following manifest:

    .....
    Main-Class : com.acme.MainClass
    Class-Path : lib/dependency1.jar lib/dependency2.jar
    .....
    

    and create the following directory structure:

    target
    |-- ....
    |-- my-project.jar
    |-- lib
        |-- dependency1.jar
        |-- dependency2.jar
    

    Meaning, I want the main JAR to exclude any dependencies and I want all transitive dependencies to get copied into a "lib" sub-directory. Any ideas?

  • Skarab
    Skarab over 13 years
    You can also maven assembly plugin: maven.apache.org/plugins/maven-assembly-plugin
  • Gili
    Gili over 13 years
    How do I get the maven-assembly-plugin to store the dependency JAR files alongside (as opposed to inside) my-project.jar?
  • Skarab
    Skarab over 13 years
    See this page - maven.apache.org/plugins/maven-assembly-plugin/examples/sing‌​le/…. Personally, I use the maven-ant-run plugin (wbarczynski.org/wp/wp-content/pom.xml_2.txt) to copy jars around but it can be done also using "pure" maven.
  • Devanshu Mevada
    Devanshu Mevada over 13 years
    @Gilli Well, the dependency:copy-dependencies has a classifier optional parameter and my guess is that you have a ${classifier} property defined somewhere in your POM. In any case, that's specific to your project.
  • Gili
    Gili over 13 years
    @Pascal, is there a way to tell "copy-dependencies" to pretend classifier is not set even if it's set elsewhere in the POM?
  • Devanshu Mevada
    Devanshu Mevada over 13 years
    @Gilli I don't think so. The obvious suggestion would be to change the name of the property to avoid this collision. Or, use dependency:copy and list explicitly the dependencies that you want (of course, this might induce lots of duplication).
  • Gili
    Gili over 13 years
    I ended up removing the use of a classifier in the POM. That fixed it. Thanks!
  • Martin
    Martin over 13 years
    I had to use a different output directory to make it work: “<outputDirectory>${project.build.directory}/classes/lib</ou‌​tputDirectory>” Thanks for the answer (and the question)!
  • Devanshu Mevada
    Devanshu Mevada over 13 years
    @Martin Weird, the above just works (tested) if you want to get the jars in target/lib. Of course, if you want another location, you'll have to change the outputDirectory. But that's another story.
  • Sahil Dave
    Sahil Dave over 10 years
    Even after specifying the above config for dependency plugin I get all the jars in the default 'dependency' folder. And on packaging 'MANIFEST.MF' has the main class and classpath, but no dependency jars are packaged in the final jar.
  • Gili
    Gili over 9 years
    @SahilDave Maybe it wasn't clear, but no dependency jars are packaged in the final jar is by design. When I posted this question, I wanted the dependencies stored outside the main JAR.
  • Stefan Falk
    Stefan Falk almost 9 years
    I have tried this (see this question) but I am getting the error "Artifact has not been packaged yet. When used on reactor artifact, copy should be executed after packaging: see MDEP-187.". What could be the problem? :/
  • asgs
    asgs about 7 years
    excellent! does this only copy the dependencies marked with scope runtime?