DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • How To Use AzureSignTool to Sign Executables With Azure DevOps
  • Mastering Git
  • Easy Oracle Database Migration with SQLcl
  • SonarQube Analysis With Ginkgo on Mac

Trending

  • Orchestrating Microservices with Dapr: A Unified Approach
  • How To Introduce a New API Quickly Using Quarkus and ChatGPT
  • Creating a Web Project: Caching for Performance Optimization
  • Detection and Mitigation of Lateral Movement in Cloud Networks

Create an Executable Fat JAR With Your Command Line

Want to get an executable fat JAR file up and running with just your command line? See the groundwork you need to lay and how to get it done.

By 
Miro Wengner user avatar
Miro Wengner
·
Oct. 28, 16 · Tutorial
Likes (13)
Comment
Save
Tweet
Share
131.3K Views

Join the DZone community and get the full member experience.

Join For Free

This article is a unification of my blog posts reviewing the possibility of creating fat JARs (Java Archive file) in Java without using any additional plugin, IDE, or any other tool — just pure command line and Java.

In the world of build tools (Ant, Maven, or Gradle) it might not even seem useful to think about the command line. The most famous IDEs (IntelliJ, Eclipse, or NetBeans) offer building tools and implementation immediately. But let's say you only have the command line and no Internet access.

What will you do then ?

Part 1: Compile ExecutableOne.jar (GitHub)

The goal of this first part is to create an executable JAR file. Let's call it ExecutableOne.jar. Opening up your comand line, let's start with creation of a simple project folder: executable-one. The example project structure is following the Maven Standard Directory Layout structure.

./libs
./out
./README.md
./src
./src/main
./src/main/java
./src/main/java/com
./src/main/java/com/exec
./src/main/java/com/exec/one
./src/main/resources

As our intent is to create an executable JAR file, we have to create a main class. Let's do it in the package: com.exec.one. The package can be found in the folder SRC/MAIN/JAVA of our sample project structure.

package com.exec.one;

public class Main {
    public static void main(String[] args){                                                                                                                                            
        System.out.println("Main Class Start");                                                                                                                                        
    }                                                                                                                                                                                  
}

Inside the folder SRC/MAIN/RESOURCES, we create the META-INF folder, and then inside, we place MANIFEST.FM file there. Let's open the newly created MANIFEST.FM file and put in a basic description.

Manifest-Version: 1.0   
Class-Path: .                                                                                                                                                                          
Main-Class: com.exec.one.Main

Note: There will be the only one MANIFEST.FM file per JAR file.

The MANIFEST.FM file contains details on how the JAR file will be used. We're not going into deep details — let's stay focused on the options we have defined.

  1. Manifest-Version : manifest file version.

  2. Class-Path: The application or extension class loader uses the value of this attribute to construct its internal search path. Originally, the class loader downloads and opens each element in its search path. For these purposes, a simple linear search algorithm has been used.

  3. Main-Class: There is the name of the class that the launcher will load at the startup time.

Now, we create the JAR file without any *.jar library. The LIBS folder in our project structure is still empty. To do so, we need to first compile the project by using javac. Meanwhile, we'll store the output in the OUT folder. Let's go back to our command line and, inside the root of the project, type:

$javac -cp ./src/main/java ./src/main/java/com/exec/one/*.java -d ./out/

The project has been compiled into the OUT directory. You can check it by using the ls command.
The second step is to create an executable JAR file from the resources located inside the OUT directory. We go back to the command line and execute the following command:

$jar cvfm ExecutableOne.jar ./src/main/resources/META-INF/MANIFEST.MF -C ./out/ .

Let's briefly review/explain the JAR tool options we have used:

  • c: indicates we want to create a new JAR file.

  • v: generates verbose output to standard output.

  • f: specifies the jarfile to be created.

  • m: indicates the manifest file we use. The manifest file includes name-value pairs.

  • -C: indicates temporary changes to the directory. Classes are added from this directory to the JAR file. The dot indicates all classes (files).

For the final output, open the command line and type:

$java -jar ./ExecutableOne.jar

standard output: 
Main Class Start

Great job! Let's move to Part 2.

Part 2.: Compile ExecutableOne.jar With Additional Packages

The main goal of this section is to show how you how to compile an executable JAR file with additional packages included. For such purposes, we've created MagicService. This service provides us with the getMessage() method and prints the message to the standard output.

Let's open the command line and create a new folder, SERVICE, with file MagicService.java:

$mkdir src/main/java/com/exec/one/service
$vi src/main/java/com/exec/one/service/MagicService.java

The newly created MagicService can be used in the following example:

package com.exec.one.service;                                                                                                                                                          

public class MagicService {                                                                                                                                                            

  private final String message;                                                                                                                                                      
    public MagicService(){                                                                                                                                                             
        this.message = "Magic Message";                                                                                                                                                
    }                                                                                                                                                                                  

    public String getMessage(){                                                                                                                                                        
         return message;                                                                                                                                                                
    }                                                                                                                                                                                  

}

The MagicService is located in a different place in the package structure than the Main class. Now we go back to the Main class and import the newly created MagicService. After the import and service instantiation, the Main class will receive the access to the getMessage() method. The Main class will change in following manner.

package com.exec.one;                                                                                                                                                                  

import com.exec.one.service.MagicService;                                                                                                                                              

public class Main {                                                                                                                                                                    
    public static void main(String[] args){                                                                                                                                            
        System.out.println("Main Class Start");                                                                                                                                        
        MagicService service = new MagicService();                                                                                                                                     
        System.out.println("MESSAGE : " + service.getMessage());                                                                                                                       
     }                                                                                                                                                                                  
} 

Now we have reached the point where the code is ready to compile. Let's go back to the command line and go into the root folder of the Executable-One project. The first step will be to compile/recompile the Executable-One project into the OUT folder. For these purposes, we need to add the location of the newly created class MagicService.java.

javac -cp ./src/main/java ./src/main/java/com/exec/one/*.java ./src/main/java/com/exec/one/**/*.java -d ./out/

The second step is to create an executable JAR file from the compiled classes. We don't need to make any changes to the command because we have not changed the JAR file logic. It means that the MANIFEST.FM file stays as it is, without any changes:

1 Manifest-Version: 1.0                                                                                                                                                                  
2 Class-Path: .                                                                                                                                                                          
3 Main-Class: com.exec.one.Main


Now we can again execute command similar to that of Part 1 in the root directory of the sample project:

jar cvfm ExecutableOne.jar ./src/main/resources/META-INF/MANIFEST.MF -C ./out/ .

By executing the created JAR file, we obtain the message printed into the standard output.

$java -jar ExecutableOne.jar 
output: 
Main Class Start
MESSAGE : Magic Message

Congratulations! Great job, again !

Part 3: Create an Executable Fat JAR (GitHub)

The goal of this part is to create a fat JAR (Java Archive) file that contains all the necessary dependencies of the developed program. As the external library, we need to use the JAR file we created in Part 2. In Part 3, we create a new sample project call it "Executable-Two" (which you can download from the link above).

The executable-two project has following folder structure:

./libs
./libs/ExecutableOne.jar
./out
./README.md
./src
./src/main
./src/main/java
./src/main/java/com
./src/main/java/com/exec
./src/main/java/com/exec/two
./src/main/java/com/exec/two/Main.java
./src/main/resources
./src/main/resources/META-INF
./src/main/resources/META-INF/MANIFEST.MF

The LIBS folder contains the previously created JAR file "ExecutableOne.jar." The "ExecutableOne.jar" contains the MagicService class, which we will use inside "ExecutableTwo." We will instantiate the class MagicService and execute the public method getMessage(). All this will happen inside the Main class of the project "ExecutableTwo".

Let's create following Main class in the project package com.exec.two:

package com.exec.two;                                                                                                                                                                  

import com.exec.one.service.MagicService;                                                                                                                                              

public class Main {                                                                                                                                                                    

    public static void main(String[] args){                                                                                                                                            

        System.out.println("Executable-Two Main");                                                                                                                                     
        MagicService service = new MagicService();                                                                                                                                     
        System.out.println("MagicService from Executable-ONE");                                                                                                                        
        System.out.println("MESSAGE: " + service.getMessage());                                                                                                                        

     }                                                                                                                                                                                  

}

Now we have everything prepared for our fat JAR file creation. We imported MagicService from the JAR library we have previously created and executed its getMessage() method. For the next few steps, we will use javac and the JAR tools provided by the Java JDK. Let's go back the command line and compile the project. In the command, we need to inform the compiler that we should extend its classpath to the used library.

$javac -cp ./src/main/java 
./src/main/java/com/exec/two/*.java -d ./out/ 
-classpath ./libs/ExecutableOne.jar

The "Executable-Two" project has been successfully compiled into the OUT directory.
Now it's time to properly prepare the OUT directory for fat JAR creation. Inside the OUT directory, we have the compiled classes that we have created for "Executable-Two." Meanwhile, the JAR tool only reads the file physically located on the filesystem — it won't read the compressed JAR file. Of course, that means that the JAR tool will not decompress and read any *.jar file located inside the OUT directory.

The result is that even when we copy the ExecutableOne.jar into the OUT directory, the JAR tool will not unpack the ExecutableOne.jar file, and the library will be added (in its compressed form) to the result. Of course, because it's compressed, it will be ignored.

The problem is that the $java -jar tool does not read inner packaged *.jar archive files!

It implies that we need to unpack the previously created Java Archive (JAR) "Executable-One.jar" into the "Executable-Two" project 's OUT directory. Open the command line and type:

$cp libs/ExecutableOne.jar ./out/
$cd ./out
$tar xf ExecutableOne.jar
$rm ExecutableOne.jar


Now, the "Executable-Two" project output directory is ready to be used as the source folder for the new JAR file.

Note: Inside every executable JAR file, there is only one MANIFEST.FM file available.

For packing our "Executable-Two" project into the JAR archive file, we use the newly created manifest file located in the folder ./src/main/resources/META-INF/ :

Manifest-Version: 1.0                                                                                                                                                                  
Class-Path: .                                                                                                                                                                          
Main-Class: com.exec.two.Main

And now we can pack all that together by typing:

$jar cvfm ExecutableTwo.jar ./src/main/resources/META-INF/MANIFEST.FM -C./out/ .

When we do execute the newly created fat JAR file, "ExecutableTwo.jar", we receive the following output.

$java -jar ./ExecutableTwo.jar
output:
Executable-Two Main
MagicService from Executable-ONE
MESSAGE: Magic Message

Congratulations! You've executed your fat JAR file!

JAR (file format) Command (computing) Manifest file Executable Directory

Opinions expressed by DZone contributors are their own.

Related

  • How To Use AzureSignTool to Sign Executables With Azure DevOps
  • Mastering Git
  • Easy Oracle Database Migration with SQLcl
  • SonarQube Analysis With Ginkgo on Mac

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!