0

I have a Maven generated jar which contains all the dependency jars within it in a directory called lib.

app-0.0.1-SNAPSHOT.jar
|_ lib/
|_ app.properties
|_ app/
|_ spring/

However, when I run it from the command line using:

java -jar app-0.0.1-SNAPSHOT.jar

it complains that it can't find slf4j (which in fact is included in the directory lib). Basically, it is not able to recognize the lib directory.

This jar has been created using the maven-dependency-plugin and the maven-jar-plugin as per this solution. As the solution suggests, a custom classloading code to load the jars is necessary. I'm wondering what this class loading code should look like.

Manifest.mf

Manifest-Version: 1.0
Built-By: me
Build-Jdk: 1.6.0_26
Class-Path: lib/camel-core-2.6.0.jar lib/commons-logging-api-1.1.jar ...
Created-By: Apache Maven 3.1.1
Main-Class: app.Main
Archiver-Version: Plexus Archiver
Community
  • 1
  • 1
mauryat
  • 1,610
  • 5
  • 29
  • 53
  • Can you post the MANIFEST file of your jar? – aviad Mar 11 '15 at 19:50
  • See this question (possible duplicate): [How can I create an executable jar with dependencies with Maven?](http://stackoverflow.com/questions/574594/how-can-i-create-an-executable-jar-with-dependencies-using-maven) – Ryan J Mar 11 '15 at 19:51
  • @RyanJ that is the very solution I've linked in my question :) Yes, it works in that I can create the jar I want. However, I'm not sure how I can run such a jar. – mauryat Mar 11 '15 at 19:52
  • You either need to set the Class-Path field in your main jar's manifest, or on the command-line use the `-cp` option to list out all the dependent jars needed by your application. – ewh Mar 11 '15 at 19:52
  • @user640378 yes I just noticed that, you linked to a specific solution. There's two ways you can accomplish this: create a monster jar with all dependencies packaged (which should also be on that question), or you need to add a classpath prefix to your manifest that will prepend the directory of your dependent jars to it. Add `lib/` to the `` tag in your `maven-jar-plugin` configuration. – Ryan J Mar 11 '15 at 19:54
  • 1
    Sometimes I feel that I'm talking to compiler - maybe that's why my comments are ignored ... – aviad Mar 11 '15 at 19:54
  • The lib directory should not be packaged _in_ the jar, in this manner, it's distributed _alongside_ it. – Ryan J Mar 11 '15 at 19:56
  • I guess that the "..." part includes the correct path to the slf4j, right? – aviad Mar 11 '15 at 19:56
  • @ewh Maven created the `Class-Path` in my main jar. So, would I really need to use `-cp` on the command-line? Also, if I do have to use it, how would I refer to dependency jars contained with my main jar?\ – mauryat Mar 11 '15 at 20:00
  • @aviad yes, the "..." part includes the correct path to slf4j and many other dependency jars. – mauryat Mar 11 '15 at 20:01
  • i think you must add slf4j jar file in to this directory : JAVA-HOME\jre\lib\ext – aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Mar 11 '15 at 20:04
  • @Mahdad I don't think that solves the problem. I have 20 other dependency jars. Java is going to complain about all of them. – mauryat Mar 11 '15 at 20:12
  • @user640378 You will need to show us the contents of MANIFEST.MF to see if the Class-Path entry was created correctly. Based on the file structure you provided, you should have something like, `Class-Path: lib/file1.jar lib/file2.jar ...` – ewh Mar 11 '15 at 20:16
  • @ewh the manifest has been added to the question. – mauryat Mar 11 '15 at 20:18
  • @user640378 Is slf4j listed in the `Class-Path`? You provided an abreviated list of the jars in your edited post, so we cannot confirm that that list contains all the jars your application needs. It may also help to post the exception you get when trying to run your app. – ewh Mar 11 '15 at 20:21
  • @ewh Yes slf4j is contained in the class-path. I think I agree with RyanJ's solution. So, I'll stick to zipping the lib and the main jar into an archive to distribute them. I'll post further updates if that doesn't work out. thank you :) – mauryat Mar 11 '15 at 20:26

1 Answers1

1

It might be easier to show this in an answer, rather than extensive commenting...

You have two options for accomplishing what you want:

  1. Create a single jar file that packages all dependencies within
  2. Create a standalone jar that only contains the resources and classes of the artifact, but no dependencies. The dependent jars are placed in some directory, alongside the jar.

To do #1, you can use the maven-assembly-plugin to build a jar file, with all dependencies, using the following in your POM:

<plugin>
     <artifactId>maven-assembly-plugin</artifactId>
     <configuration>
          <descriptorRefs>
               <descriptorRef>jar-with-dependencies</descriptorRef>
          </descriptorRefs>
          <archive>
               <manifest>
                    <mainClass>${main.class}</mainClass>
               </manifest>
          </archive>
     </configuration>
     <executions>
          <execution>
               <phase>package</phase>
               <goals>
                    <goal>single</goal>
               </goals>
          </execution>
     </executions>
</plugin>

The manifest packed in this jar looks like this:

Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven
Built-By: <username>
Build-Jdk: 1.7.0_60
Main-Class: your.main.Class

With this solution, there's no need to have a bunch of jars on your classpath because they are already in the jar, in their proper package location (in .class format)

If you go with #2, the solution should provide you something like this (assuming you have all dependencies in a directory called lib in your target directory

your.artifact.jar
lib
  |-- dependent jars...

And your manifest for your jar would have:

Manifest-Version: 1.0
Built-By: me
Build-Jdk: 1.6.0_26
Class-Path: lib/camel-core-2.6.0.jar lib/commons-logging-api-1.1.jar ...
Created-By: Apache Maven 3.1.1
Main-Class: app.Main
Archiver-Version: Plexus Archiver

Note that the lib directory is not packed in your jar, but alongside it (ie, you distribute them together, yourself)

Ryan J
  • 8,275
  • 3
  • 25
  • 28
  • I used [this solution](http://stackoverflow.com/a/4323501/640378). It does have the `classpathPrefix` in the `maven-jar-plugin` config as you mentioned. I would like to use this solution as the `maven-assembly-plugin` solution is creating other problems in my case. Also, I opt for the `lib` directory to be within my main jar so that it can be distributed easily. Is there a cleaner way of distributing a `lib` directory that's outside my main jar? If there is, then I'd like to know. – mauryat Mar 11 '15 at 20:09
  • 1
    @user640378 Use a zip archive, is the simplest way. If you package the directory _in_ the jar like you've shown, you need to load the classes as resources using a custom class loader at runtime. You're asking for more trouble if you try to do that, so I would just distribute them like they are meant to be used, as individual files on the disk pulled together using your main jar's manifest entries. – Ryan J Mar 11 '15 at 20:11
  • yes that's what I was gonna end up doing if I didn't find another solution. Good to know that the class loader bit is not straightforward. – mauryat Mar 11 '15 at 20:16