How to create a jar file with Maven
In this tutorial, we will show you how to use Maven build tool, to create a single executable Jar, and how to deal with the project’s dependencies.
Tools used :
- Maven 3.1.1
- JDK 1.7
- log4j 1.2.17
- Joda-time 2.5
- Eclipse 4.3
1. Create a simple Java project
Create a Java project from the Maven quick start template.
$ mvn archetype:generate -DgroupId=com.mkyong.core.utils -DartifactId=dateUtils
-DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
The following files and folder structure will be created.
.
|____dateUtils
| |____pom.xml
| |____src
| | |____main
| | | |____java
| | | | |____com
| | | | | |____mkyong
| | | | | | |____core
| | | | | | | |____utils
| | | | | | | | |____App.java
| | |____test
| | | |____java
| | | | |____com
| | | | | |____mkyong
| | | | | | |____core
| | | | | | | |____utils
| | | | | | | | |____AppTest.java
Above folder structure is not enough, create a log4j.properties
file and put it in src/main/resources/log4j.properties
, just create the resources folder manually.
# Root logger option
log4j.rootLogger=DEBUG, stdout
# Direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
Make it support Eclipse.
$ mvn eclipse:eclipse
And imports the project into Eclipse IDE, the final project structure should be like this :
2. Update Pom.xml
Update pom.xml
to declare both log4j and the jodatime dependencies, for output to a jar
format, make sure the packaging is set to “jar”. Read below comment for self-explanatory.
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mkyong.core.utils</groupId>
<artifactId>dateUtils</artifactId>
<!-- Output to jar format -->
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>dateUtils</name>
<url>http://maven.apache.org</url>
<properties>
<jdk.version>1.7</jdk.version>
<jodatime.version>2.5</jodatime.version>
<junit.version>4.11</junit.version>
<log4j.version>1.2.17</log4j.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>${jodatime.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
</dependencies>
<build>
<finalName>dateutils</finalName>
<plugins>
<!-- download source code in Eclipse, best practice -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.9</version>
<configuration>
<downloadSources>true</downloadSources>
<downloadJavadocs>false</downloadJavadocs>
</configuration>
</plugin>
<!-- Set a JDK compiler level -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>${jdk.version}</source>
<target>${jdk.version}</target>
</configuration>
</plugin>
<!-- Make this jar executable -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<!-- DO NOT include log4j.properties file in your Jar -->
<excludes>
<exclude>**/log4j.properties</exclude>
</excludes>
<archive>
<manifest>
<!-- Jar file entry point -->
<mainClass>com.mkyong.core.utils.App</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>
3. Update App.java
Update the generated App.java
with the following content :
package com.mkyong.core.utils;
import org.apache.log4j.Logger;
import org.joda.time.LocalDate;
public class App {
private static final Logger logger = Logger.getLogger(App.class);
public static void main(String[] args) {
System.out.println(getLocalCurrentDate());
}
private static String getLocalCurrentDate() {
if (logger.isDebugEnabled()) {
logger.debug("getLocalCurrentDate() is executed!");
}
LocalDate date = new LocalDate();
return date.toString();
}
}
Now, this project has two dependencies : log4j and jodatime.
4. Working with Dependencies
4.1. How can I add dependencies in a jar?
– You can put both log4j.jar and jodatime.jar inside the final.jar, but your classes are unable to call other classes which is inside the unpack log4j.jar, Java jar is designed like this, unless you create a special class loader like one-jar plugin.
– Alternatively, use maven-assembly-plugin
to extract all dependency jars into raw classes, and group it together. Read this StackOverflow thread. This hack is workable in project with less dependencies only, for large project with many dependencies, it will cause Java class name conflict issue.
– Try one-jar plugin, it will create a fat-jar, which includes the entire project’s dependencies into a single jar file, read this article – Create a fat Jar file with Maven
4.2 Solution
The one-jar solution is really good, but I don’t like the custom class loader and fat-jar concept. My simplest and always working solution is copy the entire project’s dependencies to a pre-defined folder, and define the dependency classpath in the jar’s manifest file.
Below is the updated and final pom.xml
, to use maven-dependency-plugin
to copy all dependencies to target/dependency-jars/
folder, and use maven-jar-plugin
to add the dependency classpath.
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mkyong.core.utils</groupId>
<artifactId>dateUtils</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>dateUtils</name>
<url>http://maven.apache.org</url>
<properties>
<jdk.version>1.7</jdk.version>
<jodatime.version>2.5</jodatime.version>
<junit.version>4.11</junit.version>
<log4j.version>1.2.17</log4j.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>${jodatime.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
</dependencies>
<build>
<finalName>dateutils</finalName>
<plugins>
<!-- download source code in Eclipse, best practice -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.9</version>
<configuration>
<downloadSources>true</downloadSources>
<downloadJavadocs>false</downloadJavadocs>
</configuration>
</plugin>
<!-- Set a compiler level -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>${jdk.version}</source>
<target>${jdk.version}</target>
</configuration>
</plugin>
<!-- Make this jar executable -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<excludes>
<exclude>**/log4j.properties</exclude>
</excludes>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>com.mkyong.core.utils.App</mainClass>
<classpathPrefix>dependency-jars/</classpathPrefix>
</manifest>
</archive>
</configuration>
</plugin>
<!-- Copy project dependency -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.5.1</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<!-- exclude junit, we need runtime dependency only -->
<includeScope>runtime</includeScope>
<outputDirectory>${project.build.directory}/dependency-jars/</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
5.The final Jar file
5.1 Package the project.
$ mvn package
Review the folder structure in the target folder
A dateutils.jar
is created, and the entire project runtime dependencies (excluded junit) are copied to target/dependency-jars/
folder.
5.2 List out the dateutils.jar
content :
$ jar tf target/dateutils.jar
META-INF/
META-INF/MANIFEST.MF
com/
com/mkyong/
com/mkyong/core/
com/mkyong/core/utils/
com/mkyong/core/utils/App.class
META-INF/maven/
META-INF/maven/com.mkyong.core.utils/
META-INF/maven/com.mkyong.core.utils/dateUtils/
META-INF/maven/com.mkyong.core.utils/dateUtils/pom.xml
META-INF/maven/com.mkyong.core.utils/dateUtils/pom.properties
5.3 Extracts and review the content of MANIFEST.MF
, the dependencies are added in the Class-Path
.
Manifest-Version: 1.0
Built-By: mkyong
Build-Jdk: 1.7.0_05
Class-Path: dependency-jars/joda-time-2.5.jar dependency-jars/log4j-1.2.17.jar
Created-By: Apache Maven 3.1.1
Main-Class: com.mkyong.core.utils.App
Archiver-Version: Plexus Archiver
5.4 Run it
$ java -jar target/dateutils.jar
log4j:WARN No appenders could be found for logger (com.mkyong.core.utils.App).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
2014-10-19
Oppss…
5.5 Where is log4j.properties
?
This is a GOOD practice to exclude the log4j.properties
in the jar file, to avoid issues like multiple log4j.properties
files in classpath.
You can still pass in the log4j properties via the log4j.configuration
system property like this :
$ java -jar -Dlog4j.configuration=file:/full_path/log4j.properties target/dateutils.jar
17:09:15,385 DEBUG App:18 - getLocalCurrentDate() is executed!
2014-10-19
In future, if you want to move
dateUtils.jar
, make sure to copy its dependency-jars
folder as well. If you have a better idea, do share with me, thanks.
Done.
Hello Sir,
I am trying to create a jar file from the root directory which is your project starting point not for the src/main/java.
Can you share the maven plugin which can handle or to create jart file from the root one as we have testData folder also or we have TestScrenshot folder.
If you need any more details please let me know.
Ankit Sharma
You can provide source directory and test directory in the pom.xml inside build tag. Something like this:
//unable to write angular brackets. So putting slashes instead
//build
//sourceDirectory src/main/java/ //sourceDirectory
//testDirectory src/test/java/ //testDirectory
//build
Thanks, helped me understand things clearly!
Great stuff! Nice tutorial!
Do you think it would be worth mentioning the onejar plugin for executable jars with maven?
Cheers!
I created a fat-jar example with one-jar plugin – https://mkyong.com/maven/maven-create-a-fat-jar-file-one-jar-example/
Article is updated, thanks for introduce one-jar. Did you try it before? will different clssloader bring any problem?
What will be the settings.xml for this example ?
Here is also nice fresh post about maven one jar: http://java.dzone.com/articles/simple-powerful-concept
Thank you for many usefull blog post!
Yes, I have used it for making executable jars. I think its configuration is easier.
However when I create my own jar library (with no main method) which depends on other libraries, I use the Maven Shade Plugin http://maven.apache.org/plugins/maven-shade-plugin/
I needed help building a source code I found
Congratulations. Your explanations are clear and more important, correct!
Hi,
I have two source codes, one is a library jar and the other is an executable jar that depends on that library jar. I want to have both the source files in the same eclipse project. How to do that ? Any ideas will be appreciated.
nice tutorial
MANIFEST.MF needs update with classpath of dependencies.
Added the following line:
org.apache.maven.plugins
maven-jar-plugin
**/log4j.properties
com.oracle.dbtools.App
true <======
dependency-jars/
This needs to be a git repo so it can kept up to date and clone easily!
Thanks!! Best tutorial I’ve found 🙂
What about the case when the app has config file like config.properties? I would not want to put it inside the jar since I may need to change the config for different environments.
I converted an eclipse project https://github.com/RishiGupta12/serial-communication-manager into maven project using eclipse IDE. Now when running mvn clean install, the jar file generated by maven does not contain lib folder. How to tell maven to include lib folder when building jar.
You would need to find a plugin for creating a fat jar.
https://www.mkyong.com/maven/create-a-fat-jar-file-maven-assembly-plugin/
Nice tutorial. Thank you.
I don’t want to use default project structure given by maven.In that case how to create pom..xml
pom is just a xml file, just create it with your favor text editor.
Great!
I just wanted to add that if you want all run-time dependency jars copied into the dependency-jars folder, you should exclude the includeGroupIds tag from the maven-dependency-plugin.
Thanks, there are many parameters to play with – http://maven.apache.org/plugins/maven-dependency-plugin/copy-dependencies-mojo.html
Just add this in pom.xml. It works great
org.apache.maven.plugins
maven-shade-plugin
package
shade
HelloWorld
com.abc.HelloWorld.java
1
Thanks, next time, I should try maven-shade-plugin
please tell the way if we want the libs present in the dependencies folder to be inside the jar file instead of the folder at the same level of the jar.
Try one-jar – https://mkyong.com/maven/maven-create-a-fat-jar-file-one-jar-example/
invaluable, thank you
Very good. Great that you offer more than one solution to the problem at hand. One-Jar looks great too and I had not heard about it before seeing this post. For now, I’m going to use the Maven approach since I’m all set up with Maven and just want a quick single package solution. But, I also look forward to learning about One-Jar now as it seems it has a lot of valid features too. Thanks so much for the excellent tutorial!
Hello.
Nice tutorial!!
I have a question, why do you write in the pom.xml the next property?
1.6
I think this is to indicate that jdk 1.6 is needed, but I don’t sure.
Thank you very much.
Article is updated to use 1.7, yes this is JDK compiler level.
Hello,
I tried using the pom configuration for jar with dependencies, which is creating the jar along with dependencies(manifest) and dependency-jars folder beside the artifact.jar. Is it possible to place all the dependency jars inside the artifact.jar?
Thanks,
Deevan
Try one-jar – https://mkyong.com/maven/maven-create-a-fat-jar-file-one-jar-example/
hello,
i want to use tag file java that is been .jar in maven project…
i do it and from it tag file project make .jar and i put that jar file in pom file project maven dependency …
but i can not see tag file
hello,
i want to use tag file java that is been .jar in maven project…
Thanks for sharing nice tips.
You can also use maven-assembly-plugin or maven-shade-plugin to create a JAR which can be executable and including dependent classes.
http://ducquoc.wordpress.com/2012/03/03/apache-maven-tips/
–Duc
Thanks for your inputs.
this is crazy just to build a JAR file and include dependencies ! crazy… there must be a simple way (I am use to go to eclipse and just export to JAR. Now I have one “maven project ” that I want to include in my “normal” project and I just can’t do it because of dependencies … Just to install one simple plugin -maven-assembly-plugin- in maven is crazy) .. I do not understand yet why maven exist … geeks like to complicate things …
fuck maven, god