Intellij Maven: Create jar with all library dependencies - java

Working with IntelliJ Idea, I want to implement the following:
I want to export an application to a jar, which contains all needed libraries. E.g. I need the AWS Java SDK libraries for S3 access, but if I upload the jar to the server and run the jar I get an NoClassDefFoundError, see below:
Exception in thread "main" java.lang.NoClassDefFoundError: com/amazonaws/auth/AWSCredentials
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2625)
at java.lang.Class.getMethod0(Class.java:2866)
at java.lang.Class.getMethod(Class.java:1676)
at sun.launcher.LauncherHelper.getMainMethod(LauncherHelper.java:494)
at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:486)
Caused by: java.lang.ClassNotFoundException: com.amazonaws.auth.AWSCredentials
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
... 6 more
Comparison: I created the same project in Eclipse and get no error! The jar file sizes are very different (Eclipse: ~35 MB vs. IntelliJ Idea: ~5,5 MB)!
I included the libraries via Maven and downloaded them also into the "lib" folder of my project:
As parameter in the run configurations I set "package", see screenshot below:
SOLUTION:
Thanks for all your hints, I got it to work now! The trick was that I did't add the dependencies to the pom.xml file (because I thought that this would be done automatically after setting them in the Project Structure, but it didn't)!!! See also my other question: Intellij IDEA Maven Plugin - Manage Dependencies and https://www.jetbrains.com/idea/help/maven.html! Add the dependencies by
Open pom.xml
Menu "Code" > "Generate" > "Dependency" (or shortcut ALT + INSERT)

You need to use Maven Assembly Plugin to include dependencies:
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>your.package.MainClass</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>
and then: mvn clean compile assembly:single

I recommend that you use the plugin shade. It is better than Assembly because it is possible to relocate classes if a conflict occur.
A typical setup can look like this: (copied from the official documentation)
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.2</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<artifactSet>
<excludes>
<exclude>classworlds:classworlds</exclude>
<exclude>junit:junit</exclude>
<exclude>jmock:*</exclude>
<exclude>*:xml-apis</exclude>
<exclude>org.apache.maven:lib:tests</exclude>
<exclude>log4j:log4j:jar:</exclude>
</excludes>
</artifactSet>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>
If size is important you can try to use the <minimizeJar>true</minimizeJar> to reduce size.

After adding the maven assembly plugin as suggested by Hector, he mentioned to run mvn clean compile assembly:single. I couldn't figure out where to run this command in Intellij.
This is what I ended up doing and successfully creating my xxx-SNAPSHOT-jar-with-dependencies.jar file.
View -> Tool Windows -> Maven -> <Your Package> -> Lifecycle -> right click compile -> Modify Run Configuration -> update Command line with 'compile assembly:single -f pom.xml' -> OK.
This created a new run configuration in the Maven tool window. There, double click on the recently created [compile].
You'll see the jars getting created under the target directory. Attaching a screenshot of my IntelliJ for reference.

For the maven-assembly-plugin compile problem, if you add <executions> block the SNAPSHOT-jar-with-dependencies.jar will be created automatically, so you don't need to modify the maven compile command. see https://medium.com/#randilfernando/when-to-use-maven-jar-maven-assembly-or-maven-shade-ffc3f76ba7a6#:~:text=maven%2Dassembly%2Dplugin%20%3A%20This,Java%20class%20name%20conflict%20issue.
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>com.xxx.Main</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<!-- bind to the packaging phase -->
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>

Related

How to run a jar with lib?

I have this jar file 'domain-directory-configurator-cli-3.1.3000.0.0.0.jar' that doesn't contain dependancies
I have also this file 'domain-directory-configurator-cli-3.1.3000.0.0.0-lib.zip'
I want to run the jar file and my application using the libs.
What is the current syntax?
I tried
java -cp 3.1.3000.0.0.0/domain-directory-configurator-cli-3.1.3000.0.0.0.jar com.imperva.itp.directory.configurator.cli.MainClass
but I keep getting the exception :
Exception in thread "main" java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory
so how do I refer the jar to work with the lib file?
You can do this by using the maven plugin maven-assembly-plugin
https://maven.apache.org/plugins/maven-assembly-plugin/usage.html
http://tutorials.jenkov.com/maven/maven-build-fat-jar.html
Assuming domain-directory-configurator-cli-3.1.3000.0.0.0-lib.zip does contain a number of jar files, you would have to extract themand list them one by one on the classpath.
More user friendly would be to have an executable jar where the main class is already mentioned in the manifest. Note you can configure the classpath in the manifest as well,so for a uer it could be sufficient to run java -jar domain-directory-configurator-cli-3.1.3000.0.0.0.jar and both the main class and the classpath are taken from the manifest.
In case you are using Maven, the below combination can help. It downloads all dependencies into the lib directory and configures both the main class and the classpath into the project's jar file.
<project>
<build>
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<archive>
<manifest>
<addDefaultEntries>true</addDefaultEntries>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>com.imperva.itp.directory.configurator.cli.MainClass</mainClass>
</manifest>
<manifestEntries>
<Build-Date>$(enf.BUILD_DATE)</Build-Date>
<Build-Date-Formatted>$(enf.BUILD_DATE_FORMATTED)</Build-Date-Formatted>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.1.1</version>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Finally, when distributing your application do not forget to deliver not only your jar but also the lib directory with content.

ClassNotFoundException when running a jar but not when running the app directly

My app is facing a NoClassDefFoundError when trying to access external dependencies, but only when run as a jar.
Using Intellij, I have a simple app with main class with some calls to external dependencies such as slf4j.
public class Main {
private static final Logger logger = LoggerFactory.getLogger(Main.class);
///
public static void main(String[] args) {
logger.debug("start");
}
}
The pom.xml includes the relevant dependencies and the app is compiled successfully.
When running the app from the intellij as regular application it is running with no problem.
But when creating an executable jar out of it and trying to run it, it crashes and can't find external dependencies:
java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory
at com.example.Main.<clinit>(Main.java:18)
Caused by: java.lang.ClassNotFoundException: org.slf4j.LoggerFactory
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 1 more
Exception in thread "main"
Process finished with exit code 1
I added this to the pom to try to solve the problem, but it didn't help:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<!-- Build an executable JAR -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>com.example.Main</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
The manifest file is located in src/main/resources/META-INF/MANIFEST.MF:
Manifest-Version: 1.0
Main-Class: com.example.Main
It's simple to understand. The jar that you are creating contains only the .class files of your code, not of the other libraries that you're importing. So when you run your code, it is unable to find the external library (org.slf4j in this case) that you have referenced in your code.
I don't know about the maven jar plugin that you're using, but try the maven assembly plugin. This plugin is used to bundle up all the other libraries that you use along with your code and create a "bundled" jar. Add the following to your pom.xml. You can add this portion between <plugins> and </plugins>.
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>example</id>
<configuration>
<archive>
<manifest>
<mainClass>com.example.Main</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
This additionally binds this "assembly" goal to the package phase of maven. So mvn package should create your jar.
Your meta-inf should have following attributes
Manifest-Version: 1.0
Created-By: xxxx
Main-Class: com.sample.Main
ClassPath: path/to/jars/your.jar
java -cp yourpath/example.jar com.sample.Main
Or
java -jar example.jar

ClassNotFoundException despite the manifest classpath including a .jar which contains the class

I have a maven java project that I am trying to run from the command line. The project is built using Netbeans 8.1. It is built to java-snap-2.0.jar with the maven-dependency-plugin and maven-jar-plugin.
In my root/target directory, I find a lib/ directory containing all the .jar needed for my project's dependencies. I have, for instance, a snap-core-6.0.0-SNAPSHOT.jar which contains, among others, org/esa/snap/core/datamodel/Product.class . In my executable jar, I have a META-INF/MANIFEST.MF file which contains a list of white-space-seperated paths to the jar files in the lib/ directory, including lib/snap-core-6.0.0-20170810.175327-200.jar.
Despite this, when I run the jar file from the command line like so : java -jar java-snap-2.0.jar argument1, argument2 ... argumentN, I get the following error :
Exception in thread "main" java.lang.NoClassDefFoundError: org/esa/snap/core/datamodel/Product
at com.batchprocessing.java.snap.ProcessMultiTemporal.main(ProcessMultiTemporal.java:56)
at com.batchprocessing.java.snap.Main.ProcessMultiTemporalHPC(Main.java:178)
at com.batchprocessing.java.snap.Main.main(Main.java:189)
Caused by: java.lang.ClassNotFoundException: org.esa.snap.core.datamodel.Product
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 3 more
Here is an excerpt from the pom.xml file :
(...)
<build>
<plugins>
<!-- Copy dependencies during package phase to root/lib directory -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<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>
<!-- Build an executable JAR and add classpaths (in lib/) to manifest -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>com.batchprocessing.java.snap.Main</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
(... other plugins ...)
</build>
(...)
I would greatly appreciate help figuring this out. I've been using this application for a year by running it from the IDE, but I'd like to be able to run it from the command line and move it to other machines (by moving the executable jar and the lib/ directory). I run into other issues if I try using a jar-with-dependencies or shade approach, so I'd like to get this setup (maven-jar-plugin + maven-dependency-plugin) working.
Alright, the issue (as rightly identified by Roman Pushkovskiy) was that the names of the jar files in the manifest file were different from their names in the lib directory. These dependencies are mostly snapshots, and so the name of the jar is something like dependency-1.0.0-SNAPSHOT.jar. In the manifest, they would be attributed a unique name based on the date of the snapshot : lib/dependency-1.0.0-20170810.175327-200.jar. The solution was to add this line to the maven-jar-plugin : <useUniqueVersions>false</useUniqueVersions>.
Updated pom.xml excerpt :
(...)
<build>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>com.batchprocessing.java.snap.Main</mainClass>
<useUniqueVersions>false</useUniqueVersions>
</manifest>
</archive>
</configuration>
</plugin>
(... other plugins ...)
</build>
(...)
Hope this can help others !

Intellij Java 2016 & Maven : how to embed dependencies in JAR?

I'm using Intellij Java 2016.2.2 and Maven to create a very simple Java console application.
I want to add an external library, so I add my dependency in Maven like this:
<dependency>
<groupId>jline</groupId>
<artifactId>jline</artifactId>
<version>2.12</version>
</dependency>
It works fine when I run it in the IDE, but not in an external console (I have the following error: java.lang.NoClassDefFoundError).
I checked and for some reason, the external JAR is not added in the JAR I just generated. I also tried many things in "File -> Project Structure", but still not working...
I just want to build my JAR with my dependencies in it, so I can simply run my application in a console using:
java -jar myproject.jar
How can I do that? Thanks for your help!
I finally managed to generate this JAR with Intellij Java, here is how I do:
add the dependencies in the pom.xml file
go to File -> Project Structure -> Artifacts -> New -> JAR -> From module with dependencies
choose the Main class and click OK
in your project, in src/main, create the "resources" folder
move the "META-INF" (with MANIFEST.MF in it) folder in this "resources" folder
go to Build -> build artifacts to build the JAR
EDIT
A better (and easier way) to do it is adding the following lines in the pom.xml file :
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>your.MainClass</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
then use the "clean" and "package" maven commands.
The last 3 steps above (about MANIFEST.MF) still seem to be mandatory.
Okay, so you basically want to create a "fat jar" (sometimes called assembly), that contains all its own dependencies (usually, the dependencies are external).
You need to use a Maven plugin for that. Below is a sample assembly plugin configuration jar-with-dependencies:
<project>
...
<build>
...
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.6</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
...
</project>
then, simply run
mvn package

Maven native dependences in Eclipse

I'm trying to get some native dependencies (LWJGL) working smoothly in Eclipse (Juno) with Maven.
Current situation:
I'm using the maven-nativedependencies-plugin version 0.0.6. This seems to successfully download and unpack the native libraries to the target/natives subdirectory of my project. Fine so far.
I can make the dependencies work by manually adding the target/natives directory in Properties / Java Build Path / Maven Dependencies / Native Library Location / Edit...
However this only works temporarily as it doesn't seem that the native library location can be specified anywhere in the pom. In particular, it breaks whenever I do a Maven / Update Project... because the Native Library Location is cleared (presumably by m2e re-configuring the project according to the pom)
Whenever the native library location is cleared, I just get the error "java.lang.UnsatisfiedLinkError: no lwjgl in java.library.path"
What is the best way to get this working reliably?
Try this steps;
Go to the run configuration of your java project.
Open the Arguments tab and enter "-Djava.library.path=target/natives"
into the VM Arguments.
For maven project;
Make sure your pom.xml like this;
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.5</source>
<target>1.5</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>com.googlecode.mavennatives</groupId>
<artifactId>maven-nativedependencies-plugin</artifactId>
<version>0.0.6</version>
<executions>
<execution>
<id>unpacknatives</id>
<phase>generate-resources</phase>
<goals>
<goal>copy</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<archive>
<manifest>
<mainClass>${main.class}</mainClass>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2.1</version>
<executions>
<execution>
<id>bundle-project-sources</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>src/META-INF/assembly.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
And then left click on the eclipse project, go to the maven menu and click on "update project configuration". If you are still having problems, in the eclipse console tab, open the maven console and do the "update project configuration" option again
In my application, I need sqljdbc4.jar from MS. My project is a Maven project, so I had the same problem that I could not edit Native Library Location under sqljdbc4.jar in Eclipse.
My error is this:
Failed to load the sqljdbc_auth.dll cause : no sqljdbc_auth in java.library.path
So I added this:
-Djava.library.path="C:\Documents and Settings\ccrhlj01\My Documents\Microsoft JDBC Driver 4.0 for SQL Server\sqljdbc_4.0\enu\auth\x86"
in my VM arguments.
Go to the run configuration of your java project.
Open the Arguments tab and enter -Djava.library.path="C:\Documents and Settings\ccrhlj01\My Documents\Microsoft JDBC Driver 4.0 for SQL Server\sqljdbc_4.0\enu\auth\x86" into the VM Arguments.
Remember the quotation marks around the path. In my case, that is my location, you need to find your dll location.
Actually, you can copy your dll to any directory on the PATH variable, ex: C:\Windows\System32. This way, you don't have to add the argument in the VM arguments tab in Eclipse.

Categories