46

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?

Gili
  • 86,244
  • 97
  • 390
  • 689

3 Answers3

64

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>
Pascal Thivent
  • 562,542
  • 136
  • 1,062
  • 1,124
  • @Gilli Well, the `dependency:copy-dependencies` has a [`classifier`](http://maven.apache.org/plugins/maven-dependency-plugin/copy-dependencies-mojo.html#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. – Pascal Thivent Aug 26 '10 at 17:08
  • @Pascal, is there a way to tell "copy-dependencies" to pretend classifier is not set even if it's set elsewhere in the POM? – Gili Aug 26 '10 at 17:22
  • 1
    @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). – Pascal Thivent Aug 26 '10 at 17:32
  • I ended up removing the use of a classifier in the POM. That fixed it. Thanks! – Gili Aug 26 '10 at 17:41
  • 1
    I had to use a different output directory to make it work: “${project.build.directory}/classes/lib” Thanks for the answer (and the question)! – Martin Aug 27 '10 at 11:05
  • @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. – Pascal Thivent Aug 27 '10 at 19:55
  • 3
    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. – Sahil Dave Oct 23 '13 at 16:03
  • @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. – Gili Dec 17 '14 at 07:05
  • I have tried this ([see this question](http://stackoverflow.com/questions/30631267/specify-library-target-of-maven)) 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? :/ – Stefan Falk Jun 03 '15 at 22:27
4

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.

PShetty
  • 545
  • 6
  • 14
0

You can use the maven jar plugin, take a look on this page: http://maven.apache.org/plugins/maven-jar-plugin/examples/manifest-customization.html

Skarab
  • 6,981
  • 13
  • 48
  • 86
  • You can also maven assembly plugin: http://maven.apache.org/plugins/maven-assembly-plugin/ – Skarab Aug 24 '10 at 16:14
  • How do I get the maven-assembly-plugin to store the dependency JAR files alongside (as opposed to inside) my-project.jar? – Gili Aug 24 '10 at 23:39
  • See this page - http://maven.apache.org/plugins/maven-assembly-plugin/examples/single/filtering-some-distribution-files.html. Personally, I use the maven-ant-run plugin (http://wbarczynski.org/wp/wp-content/pom.xml_2.txt) to copy jars around but it can be done also using "pure" maven. – Skarab Aug 25 '10 at 04:44